Skip to content

Commit feb41cc

Browse files
clydinalan-agius4
authored andcommitted
feat(@angular/build): add polyfills option to unit-test builder
Adds an optional 'polyfills' array option to the 'unit-test' builder. When omitted, polyfills continue to be inherited from the specified 'buildTarget'. When defined, the test target's 'polyfills' option takes precedence, allowing projects to configure test-specific polyfills or provide polyfills for library test targets that build with '@angular/build:ng-packagr'.
1 parent 4a080b6 commit feb41cc

7 files changed

Lines changed: 113 additions & 2 deletions

File tree

‎goldens/public-api/angular/build/index.api.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ export type UnitTestBuilderOptions = {
236236
isolate?: boolean;
237237
listTests?: boolean;
238238
outputFile?: string;
239+
polyfills?: string[];
239240
progress?: boolean;
240241
providersFile?: string;
241242
quiet?: boolean;

‎packages/angular/build/src/builders/unit-test/builder.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ export async function* execute(
323323
const applicationBuildOptions = {
324324
...buildTargetOptions,
325325
...runnerBuildOptions,
326+
...(normalizedOptions.polyfills !== undefined
327+
? { polyfills: normalizedOptions.polyfills }
328+
: {}),
326329
watch: normalizedOptions.watch,
327330
progress: normalizedOptions.buildProgress ?? buildTargetOptions.progress,
328331
quiet: normalizedOptions.quiet,

‎packages/angular/build/src/builders/unit-test/options.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export async function normalizeOptions(
7777
runnerConfig,
7878
isolate,
7979
splitting = true,
80+
polyfills,
8081
} = options;
8182

8283
if (ui && runner !== Runner.Vitest) {
@@ -158,6 +159,7 @@ export async function normalizeOptions(
158159
dumpVirtualFiles: options.dumpVirtualFiles,
159160
listTests: options.listTests,
160161
preserveSymlinks,
162+
polyfills,
161163
runnerConfig:
162164
typeof runnerConfig === 'string'
163165
? runnerConfig.length === 0

‎packages/angular/build/src/builders/unit-test/runners/karma/executor.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class KarmaExecutor implements TestExecutor {
7676
const karmaOptions: KarmaBuilderOptions = {
7777
karmaConfig,
7878
tsConfig: unitTestOptions.tsConfig ?? buildTargetOptions.tsConfig,
79-
polyfills: injectTestingPolyfills(buildTargetOptions.polyfills),
79+
polyfills: injectTestingPolyfills(unitTestOptions.polyfills ?? buildTargetOptions.polyfills),
8080
assets: buildTargetOptions.assets,
8181
scripts: buildTargetOptions.scripts,
8282
styles: buildTargetOptions.styles,

‎packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function getZoneTestingStrategy(
178178
return 'dynamic-zone';
179179
}
180180

181-
return 'dynamic';
181+
return 'none';
182182
} catch {
183183
return 'none';
184184
}
@@ -256,6 +256,7 @@ export async function getVitestBuildOptions(
256256

257257
const buildOptions: Partial<ApplicationBuilderInternalOptions> = {
258258
...baseBuildOptions,
259+
...(options.polyfills !== undefined ? { polyfills: options.polyfills } : {}),
259260
watch,
260261
incrementalResults: watch,
261262
index: false,

‎packages/angular/build/src/builders/unit-test/schema.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,14 @@
265265
"description": "Specifies the path to a TypeScript file that provides an array of Angular providers for the test environment. The file must contain a default export of the provider array.",
266266
"minLength": 1
267267
},
268+
"polyfills": {
269+
"description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'. If not specified, polyfills will be inherited from the build target.",
270+
"type": "array",
271+
"items": {
272+
"type": "string",
273+
"uniqueItems": true
274+
}
275+
},
268276
"setupFiles": {
269277
"type": "array",
270278
"items": {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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.dev/license
7+
*/
8+
9+
import { execute } from '../../index';
10+
import {
11+
BASE_OPTIONS,
12+
describeBuilder,
13+
UNIT_TEST_BUILDER_INFO,
14+
setupApplicationTarget,
15+
} from '../setup';
16+
17+
describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
18+
describe('Option: "polyfills"', () => {
19+
beforeEach(async () => {
20+
setupApplicationTarget(harness);
21+
});
22+
23+
it('should include polyfills specified on test target', async () => {
24+
await harness.writeFiles({
25+
'src/custom-polyfill.js': `globalThis['CUSTOM_POLYFILL_RAN'] = true;`,
26+
'src/app/app.component.spec.ts': `
27+
import { describe, expect, test } from 'vitest';
28+
describe('Polyfill Test', () => {
29+
test('should have run test polyfill', () => {
30+
expect((globalThis as any)['CUSTOM_POLYFILL_RAN']).toBe(true);
31+
});
32+
});`,
33+
});
34+
35+
harness.useTarget('test', {
36+
...BASE_OPTIONS,
37+
polyfills: ['src/custom-polyfill.js'],
38+
});
39+
40+
const { result } = await harness.executeOnce();
41+
expect(result?.success).toBeTrue();
42+
});
43+
44+
it('should override buildTarget polyfills when polyfills is specified on test target', async () => {
45+
setupApplicationTarget(harness, {
46+
polyfills: ['src/app-polyfill.js'],
47+
});
48+
49+
await harness.writeFiles({
50+
'src/app-polyfill.js': `globalThis['APP_POLYFILL_RAN'] = true;`,
51+
'src/test-polyfill.js': `globalThis['TEST_POLYFILL_RAN'] = true;`,
52+
'src/app/app.component.spec.ts': `
53+
import { describe, expect, test } from 'vitest';
54+
describe('Polyfill Override Test', () => {
55+
test('should have run test polyfill and not app polyfill', () => {
56+
expect((globalThis as any)['TEST_POLYFILL_RAN']).toBe(true);
57+
expect((globalThis as any)['APP_POLYFILL_RAN']).toBeUndefined();
58+
});
59+
});`,
60+
});
61+
62+
harness.useTarget('test', {
63+
...BASE_OPTIONS,
64+
polyfills: ['src/test-polyfill.js'],
65+
});
66+
67+
const { result } = await harness.executeOnce();
68+
expect(result?.success).toBeTrue();
69+
});
70+
71+
it('should allow overriding buildTarget polyfills with an empty array', async () => {
72+
setupApplicationTarget(harness, {
73+
polyfills: ['src/app-polyfill.js'],
74+
});
75+
76+
await harness.writeFiles({
77+
'src/app-polyfill.js': `globalThis['APP_POLYFILL_RAN'] = true;`,
78+
'src/app/app.component.spec.ts': `
79+
import { describe, expect, test } from 'vitest';
80+
describe('Empty Polyfill Override Test', () => {
81+
test('should not have run app polyfill', () => {
82+
expect((globalThis as any)['APP_POLYFILL_RAN']).toBeUndefined();
83+
});
84+
});`,
85+
});
86+
87+
harness.useTarget('test', {
88+
...BASE_OPTIONS,
89+
polyfills: [],
90+
});
91+
92+
const { result } = await harness.executeOnce();
93+
expect(result?.success).toBeTrue();
94+
});
95+
});
96+
});

0 commit comments

Comments
 (0)