Skip to content

Commit 39aa7c9

Browse files
committed
feat(@angular/build): add isolate option to unit-test builder
This commit adds an `isolate` option to the `@angular/build:unit-test` builder for the Vitest runner. By default, tests run in a non-isolated environment to match the behavior of Karma/Jasmine. This new option allows developers to easily opt-in to Vitest native isolation (running tests in separate threads or processes) without requiring a custom Vitest configuration file. The option is not supported by the Karma runner and will result in an error if used there.
1 parent 73233dc commit 39aa7c9

6 files changed

Lines changed: 72 additions & 3 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
@@ -233,6 +233,7 @@ export type UnitTestBuilderOptions = {
233233
filter?: string;
234234
headless?: boolean;
235235
include?: string[];
236+
isolate?: boolean;
236237
listTests?: boolean;
237238
outputFile?: string;
238239
progress?: boolean;

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,17 @@ export async function normalizeOptions(
5454
const buildTargetSpecifier = options.buildTarget ?? `::development`;
5555
const buildTarget = targetFromTargetString(buildTargetSpecifier, projectName, 'build');
5656

57-
const { runner, browsers, progress, filter, browserViewport, ui, runnerConfig } = options;
57+
const { runner, browsers, progress, filter, browserViewport, ui, runnerConfig, isolate } =
58+
options;
5859

5960
if (ui && runner !== Runner.Vitest) {
6061
throw new Error('The "ui" option is only available for the "vitest" runner.');
6162
}
6263

64+
if (isolate && runner !== Runner.Vitest) {
65+
throw new Error('The "isolate" option is only available for the "vitest" runner.');
66+
}
67+
6368
const [width, height] = browserViewport?.split('x').map(Number) ?? [];
6469

6570
let tsConfig = options.tsConfig;
@@ -121,6 +126,7 @@ export async function normalizeOptions(
121126
watch,
122127
debug: options.debug ?? false,
123128
ui: process.env['CI'] ? false : ui,
129+
isolate: isolate ?? false,
124130
quiet: options.quiet ?? (process.env['CI'] ? false : true),
125131
providersFile: options.providersFile && path.join(workspaceRoot, options.providersFile),
126132
setupFiles: options.setupFiles

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ export class VitestExecutor implements TestExecutor {
378378
projectPlugins,
379379
include,
380380
watch,
381+
isolate: this.options.isolate,
381382
}),
382383
],
383384
};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface VitestConfigPluginOptions {
5454
include: string[];
5555
optimizeDepsInclude: string[];
5656
watch: boolean;
57+
isolate: boolean;
5758
}
5859

5960
async function findTestEnvironment(
@@ -250,8 +251,7 @@ export async function createVitestConfigPlugin(
250251
test: {
251252
setupFiles,
252253
globals: true,
253-
// Default to `false` to align with the Karma/Jasmine experience.
254-
isolate: false,
254+
isolate: options.isolate,
255255
sequence: { setupFiles: 'list' },
256256
},
257257
optimizeDeps: {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
"type": "boolean",
7474
"description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner."
7575
},
76+
"isolate": {
77+
"type": "boolean",
78+
"description": "Enables isolation for test execution. When true, Vitest runs tests in separate threads or processes. This option is only available for the Vitest runner. Defaults to false to align with the Karma/Jasmine experience."
79+
},
7680
"quiet": {
7781
"type": "boolean",
7882
"description": "Suppresses the verbose build summary and stats table on each rebuild. Defaults to `true` locally and `false` in CI environments."
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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: "isolate"', () => {
19+
beforeEach(async () => {
20+
setupApplicationTarget(harness);
21+
});
22+
23+
it('should fail when isolate is true and runner is karma', async () => {
24+
harness.useTarget('test', {
25+
...BASE_OPTIONS,
26+
runner: 'karma' as any,
27+
isolate: true,
28+
});
29+
30+
await expectAsync(harness.executeOnce()).toBeRejectedWithError(
31+
/The "isolate" option is only available for the "vitest" runner/,
32+
);
33+
});
34+
35+
it('should run tests successfully when isolate is true and runner is vitest', async () => {
36+
harness.useTarget('test', {
37+
...BASE_OPTIONS,
38+
runner: 'vitest' as any,
39+
isolate: true,
40+
});
41+
42+
const { result } = await harness.executeOnce();
43+
expect(result?.success).toBeTrue();
44+
});
45+
46+
it('should run tests successfully when isolate is false and runner is vitest', async () => {
47+
harness.useTarget('test', {
48+
...BASE_OPTIONS,
49+
runner: 'vitest' as any,
50+
isolate: false,
51+
});
52+
53+
const { result } = await harness.executeOnce();
54+
expect(result?.success).toBeTrue();
55+
});
56+
});
57+
});

0 commit comments

Comments
 (0)