Skip to content

Commit 4c53c8f

Browse files
clydinangular-robot[bot]
authored andcommitted
test(@angular-devkit/build-angular): additional unit tests for esbuild builder
The following unit tests have been ported over to test the experimental esbuild-based browser application builder: * `baseHref` option * `crossOrigin` option * TypeScript path mapping behavior
1 parent 54cc8d4 commit 4c53c8f

File tree

3 files changed

+325
-0
lines changed

3 files changed

+325
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { buildEsbuildBrowser } from '../../index';
10+
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
11+
12+
describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
13+
describe('Behavior: "TypeScript Path Mapping"', () => {
14+
it('should resolve TS files when imported with a path mapping', async () => {
15+
// Change main module import to use path mapping
16+
await harness.modifyFile('src/main.ts', (content) =>
17+
content.replace(`'./app/app.module'`, `'@root/app.module'`),
18+
);
19+
20+
// Add a path mapping for `@root`
21+
await harness.modifyFile('tsconfig.json', (content) => {
22+
const tsconfig = JSON.parse(content);
23+
tsconfig.compilerOptions.paths = {
24+
'@root/*': ['./src/app/*'],
25+
};
26+
27+
return JSON.stringify(tsconfig);
28+
});
29+
30+
harness.useTarget('build', {
31+
...BASE_OPTIONS,
32+
});
33+
34+
const { result } = await harness.executeOnce();
35+
36+
expect(result?.success).toBe(true);
37+
});
38+
39+
it('should fail to resolve if no path mapping for an import is present', async () => {
40+
// Change main module import to use path mapping
41+
await harness.modifyFile('src/main.ts', (content) =>
42+
content.replace(`'./app/app.module'`, `'@root/app.module'`),
43+
);
44+
45+
// Add a path mapping for `@not-root`
46+
await harness.modifyFile('tsconfig.json', (content) => {
47+
const tsconfig = JSON.parse(content);
48+
tsconfig.compilerOptions.paths = {
49+
'@not-root/*': ['./src/app/*'],
50+
};
51+
52+
return JSON.stringify(tsconfig);
53+
});
54+
55+
harness.useTarget('build', {
56+
...BASE_OPTIONS,
57+
});
58+
59+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
60+
61+
expect(result?.success).toBe(false);
62+
expect(logs).toContain(
63+
jasmine.objectContaining({
64+
message: jasmine.stringMatching('Could not resolve "@root/app.module"'),
65+
}),
66+
);
67+
});
68+
69+
it('should resolve JS files when imported with a path mapping', async () => {
70+
// Change main module import to use path mapping
71+
await harness.modifyFile('src/main.ts', (content) =>
72+
content.replace(`'./app/app.module'`, `'app-module'`),
73+
);
74+
75+
await harness.writeFiles({
76+
'a.js': `export * from './src/app/app.module';\n\nconsole.log('A');`,
77+
'a.d.ts': `export * from './src/app/app.module';`,
78+
});
79+
80+
// Add a path mapping for `@root`
81+
await harness.modifyFile('tsconfig.json', (content) => {
82+
const tsconfig = JSON.parse(content);
83+
tsconfig.compilerOptions.paths = {
84+
'app-module': ['a.js'],
85+
};
86+
87+
return JSON.stringify(tsconfig);
88+
});
89+
90+
// app.module needs to be manually included since it is not referenced via a TS file
91+
// with the test path mapping in place.
92+
await harness.modifyFile('src/tsconfig.app.json', (content) => {
93+
const tsconfig = JSON.parse(content);
94+
tsconfig.files.push('app/app.module.ts');
95+
96+
return JSON.stringify(tsconfig);
97+
});
98+
99+
harness.useTarget('build', {
100+
...BASE_OPTIONS,
101+
});
102+
103+
const { result } = await harness.executeOnce();
104+
105+
expect(result?.success).toBe(true);
106+
harness.expectFile('dist/main.js').content.toContain(`console.log("A")`);
107+
});
108+
});
109+
});
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { buildEsbuildBrowser } from '../../index';
10+
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
11+
12+
describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
13+
describe('Option: "baseHref"', () => {
14+
beforeEach(async () => {
15+
// Application code is not needed for asset tests
16+
await harness.writeFile('src/main.ts', 'console.log("TEST");');
17+
});
18+
19+
it('should update the base element href attribute when option is set', async () => {
20+
harness.useTarget('build', {
21+
...BASE_OPTIONS,
22+
baseHref: '/abc',
23+
});
24+
25+
const { result } = await harness.executeOnce();
26+
expect(result?.success).toBe(true);
27+
harness.expectFile('dist/index.html').content.toContain('<base href="/abc">');
28+
});
29+
30+
it('should update the base element with no href attribute when option is set', async () => {
31+
await harness.writeFile(
32+
'src/index.html',
33+
`
34+
<html>
35+
<head><base></head>
36+
<body></body>
37+
</html>
38+
`,
39+
);
40+
41+
harness.useTarget('build', {
42+
...BASE_OPTIONS,
43+
baseHref: '/abc',
44+
});
45+
46+
const { result } = await harness.executeOnce();
47+
expect(result?.success).toBe(true);
48+
harness.expectFile('dist/index.html').content.toContain('<base href="/abc">');
49+
});
50+
51+
it('should add the base element href attribute when option is set', async () => {
52+
await harness.writeFile(
53+
'src/index.html',
54+
`
55+
<html>
56+
<head></head>
57+
<body></body>
58+
</html>
59+
`,
60+
);
61+
62+
harness.useTarget('build', {
63+
...BASE_OPTIONS,
64+
baseHref: '/abc',
65+
});
66+
67+
const { result } = await harness.executeOnce();
68+
expect(result?.success).toBe(true);
69+
harness.expectFile('dist/index.html').content.toContain('<base href="/abc">');
70+
});
71+
72+
it('should update the base element href attribute when option is set to an empty string', async () => {
73+
harness.useTarget('build', {
74+
...BASE_OPTIONS,
75+
baseHref: '',
76+
});
77+
78+
const { result } = await harness.executeOnce();
79+
expect(result?.success).toBe(true);
80+
harness.expectFile('dist/index.html').content.toContain('<base href="">');
81+
});
82+
83+
it('should not update the base element href attribute when option is not present', async () => {
84+
harness.useTarget('build', {
85+
...BASE_OPTIONS,
86+
});
87+
88+
const { result } = await harness.executeOnce();
89+
expect(result?.success).toBe(true);
90+
harness.expectFile('dist/index.html').content.toContain('<base href="/">');
91+
});
92+
93+
it('should not change the base element href attribute when option is not present', async () => {
94+
await harness.writeFile(
95+
'src/index.html',
96+
`
97+
<html>
98+
<head><base href="."></head>
99+
<body></body>
100+
</html>
101+
`,
102+
);
103+
104+
harness.useTarget('build', {
105+
...BASE_OPTIONS,
106+
});
107+
108+
const { result } = await harness.executeOnce();
109+
expect(result?.success).toBe(true);
110+
harness.expectFile('dist/index.html').content.toContain('<base href=".">');
111+
});
112+
});
113+
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { buildEsbuildBrowser } from '../../index';
10+
import { CrossOrigin } from '../../schema';
11+
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
12+
13+
describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
14+
describe('Option: "crossOrigin"', () => {
15+
beforeEach(async () => {
16+
// Application code is not needed for asset tests
17+
await harness.writeFile('src/main.ts', 'console.log("TEST");');
18+
19+
// Add a global stylesheet to test link elements
20+
await harness.writeFile('src/styles.css', '// Global styles');
21+
22+
// Reduce the input index HTML to a single line to simplify comparing
23+
await harness.writeFile(
24+
'src/index.html',
25+
'<html><head><base href="/"></head><body><app-root></app-root></body></html>',
26+
);
27+
});
28+
29+
it('should add the use-credentials crossorigin attribute when option is set to use-credentials', async () => {
30+
harness.useTarget('build', {
31+
...BASE_OPTIONS,
32+
styles: ['src/styles.css'],
33+
crossOrigin: CrossOrigin.UseCredentials,
34+
});
35+
36+
const { result } = await harness.executeOnce();
37+
expect(result?.success).toBe(true);
38+
harness
39+
.expectFile('dist/index.html')
40+
.content.toEqual(
41+
`<html><head><base href="/"><link rel="stylesheet" href="styles.css" crossorigin="use-credentials"></head>` +
42+
`<body><app-root></app-root>` +
43+
`<script src="main.js" type="module" crossorigin="use-credentials"></script></body></html>`,
44+
);
45+
});
46+
47+
it('should add the anonymous crossorigin attribute when option is set to anonymous', async () => {
48+
harness.useTarget('build', {
49+
...BASE_OPTIONS,
50+
styles: ['src/styles.css'],
51+
crossOrigin: CrossOrigin.Anonymous,
52+
});
53+
54+
const { result } = await harness.executeOnce();
55+
expect(result?.success).toBe(true);
56+
harness
57+
.expectFile('dist/index.html')
58+
.content.toEqual(
59+
`<html><head><base href="/">` +
60+
`<link rel="stylesheet" href="styles.css" crossorigin="anonymous"></head>` +
61+
`<body><app-root></app-root>` +
62+
`<script src="main.js" type="module" crossorigin="anonymous"></script></body></html>`,
63+
);
64+
});
65+
66+
it('should not add a crossorigin attribute when option is set to none', async () => {
67+
harness.useTarget('build', {
68+
...BASE_OPTIONS,
69+
styles: ['src/styles.css'],
70+
crossOrigin: CrossOrigin.None,
71+
});
72+
73+
const { result } = await harness.executeOnce();
74+
expect(result?.success).toBe(true);
75+
harness
76+
.expectFile('dist/index.html')
77+
.content.toEqual(
78+
`<html><head><base href="/">` +
79+
`<link rel="stylesheet" href="styles.css"></head>` +
80+
`<body><app-root></app-root>` +
81+
`<script src="main.js" type="module"></script></body></html>`,
82+
);
83+
});
84+
85+
it('should not add a crossorigin attribute when option is not present', async () => {
86+
harness.useTarget('build', {
87+
...BASE_OPTIONS,
88+
styles: ['src/styles.css'],
89+
});
90+
91+
const { result } = await harness.executeOnce();
92+
expect(result?.success).toBe(true);
93+
harness
94+
.expectFile('dist/index.html')
95+
.content.toEqual(
96+
`<html><head><base href="/">` +
97+
`<link rel="stylesheet" href="styles.css"></head>` +
98+
`<body><app-root></app-root>` +
99+
`<script src="main.js" type="module"></script></body></html>`,
100+
);
101+
});
102+
});
103+
});

0 commit comments

Comments
 (0)