Skip to content

Commit d6ea6b0

Browse files
clydinalan-agius4
authored andcommitted
feat(@angular/build): add experimental vitest browser support to unit-testing
The experimental `unit-test` builder now allows for enabling the experimental vitest browser testing support. A `browsers` option is now available that can list one or more browsers to use for test execution. If the `browsers` option is not specified (the default), then `jsdom` will be used to execute the tests without a browser. To use the browser support, either `playwright` or `webdriverio` must be installed within the project. On startup, the testing process will automatically attempt to discover the browser provider. The browser names present in the `browsers` option must be specified based on the installed provider. Each may have differing names for some browsers.
1 parent 1985ba7 commit d6ea6b0

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

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

+44-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
1010
import assert from 'node:assert';
1111
import { randomUUID } from 'node:crypto';
12+
import { createRequire } from 'node:module';
1213
import path from 'node:path';
1314
import { createVirtualModulePlugin } from '../../tools/esbuild/virtual-module-plugin';
1415
import { loadEsmModule } from '../../utils/load-esm';
@@ -133,6 +134,28 @@ export async function* execute(
133134

134135
let instance: import('vitest/node').Vitest | undefined;
135136

137+
// Setup vitest browser options if configured
138+
let browser: import('vitest/node').BrowserConfigOptions | undefined;
139+
if (normalizedOptions.browsers) {
140+
const provider = findBrowserProvider(projectSourceRoot);
141+
if (!provider) {
142+
context.logger.error(
143+
'The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
144+
' Please install one of these packages and rerun the test command.',
145+
);
146+
147+
return { success: false };
148+
}
149+
150+
browser = {
151+
enabled: true,
152+
provider,
153+
instances: normalizedOptions.browsers.map((browserName) => ({
154+
browser: browserName,
155+
})),
156+
};
157+
}
158+
136159
for await (const result of buildApplicationInternal(buildOptions, context, extensions)) {
137160
if (result.kind === ResultKind.Failure) {
138161
continue;
@@ -153,8 +176,11 @@ export async function* execute(
153176
test: {
154177
root: outputPath,
155178
setupFiles,
156-
environment: 'jsdom',
179+
// Use `jsdom` if no browsers are explicitly configured.
180+
// `node` is effectively no "environment" and the default.
181+
environment: browser ? 'node' : 'jsdom',
157182
watch: normalizedOptions.watch,
183+
browser,
158184
coverage: {
159185
enabled: normalizedOptions.codeCoverage,
160186
exclude: normalizedOptions.codeCoverageExclude,
@@ -169,3 +195,20 @@ export async function* execute(
169195
yield { success: testModules.every((testModule) => testModule.ok()) };
170196
}
171197
}
198+
199+
function findBrowserProvider(
200+
projectSourceRoot: string,
201+
): import('vitest/node').BrowserBuiltinProvider | undefined {
202+
const projectResolver = createRequire(projectSourceRoot + '/').resolve;
203+
204+
// These must be installed in the project to be used
205+
const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const;
206+
207+
for (const providerName of vitestBuiltinProviders) {
208+
try {
209+
projectResolver(providerName);
210+
211+
return providerName;
212+
} catch {}
213+
}
214+
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export async function normalizeOptions(
3636
const buildTargetSpecifier = options.buildTarget ?? `::development`;
3737
const buildTarget = targetFromTargetString(buildTargetSpecifier, projectName, 'build');
3838

39-
const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters } = options;
39+
const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters, browsers } = options;
4040

4141
return {
4242
// Project/workspace information
@@ -53,6 +53,7 @@ export async function normalizeOptions(
5353
codeCoverageExclude,
5454
tsConfig,
5555
reporters,
56+
browsers,
5657
// TODO: Implement watch support
5758
watch: false,
5859
};

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

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
"description": "The name of the test runner to use for test execution.",
1919
"enum": ["vitest"]
2020
},
21+
"browsers": {
22+
"description": "A list of browsers to use for test execution. If undefined, jsdom on Node.js will be used instead of a browser.",
23+
"type": "array",
24+
"items": {
25+
"type": "string"
26+
},
27+
"minItems": 1
28+
},
2129
"include": {
2230
"type": "array",
2331
"items": {

0 commit comments

Comments
 (0)