Skip to content

Commit cf722f5

Browse files
committed
refactor(@angular/build): co-locate vitest browser provider logic
Refactors the Vitest test runner to improve code organization and separation of concerns. The browser-specific helper functions (`findBrowserProvider`, `normalizeBrowserName`, and `setupBrowserConfiguration`) are moved from the main `executor.ts` file into a new, dedicated `browser-provider.ts` file. This cleanup allows the main executor to focus solely on its primary responsibility of managing the test execution lifecycle, while properly encapsulating the browser setup logic in a single, co-located file.
1 parent d1dd3a8 commit cf722f5

File tree

2 files changed

+93
-83
lines changed

2 files changed

+93
-83
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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 { createRequire } from 'node:module';
10+
11+
function findBrowserProvider(
12+
projectResolver: NodeJS.RequireResolve,
13+
): import('vitest/node').BrowserBuiltinProvider | undefined {
14+
// One of these must be installed in the project to use browser testing
15+
const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const;
16+
17+
for (const providerName of vitestBuiltinProviders) {
18+
try {
19+
projectResolver(providerName);
20+
21+
return providerName;
22+
} catch {}
23+
}
24+
25+
return undefined;
26+
}
27+
28+
function normalizeBrowserName(browserName: string): string {
29+
// Normalize browser names to match Vitest's expectations for headless but also supports karma's names
30+
// e.g., 'ChromeHeadless' -> 'chrome', 'FirefoxHeadless' -> 'firefox'
31+
// and 'Chrome' -> 'chrome', 'Firefox' -> 'firefox'.
32+
const normalized = browserName.toLowerCase();
33+
34+
return normalized.replace(/headless$/, '');
35+
}
36+
37+
export function setupBrowserConfiguration(
38+
browsers: string[] | undefined,
39+
debug: boolean,
40+
projectSourceRoot: string,
41+
): { browser?: import('vitest/node').BrowserConfigOptions; errors?: string[] } {
42+
if (browsers === undefined) {
43+
return {};
44+
}
45+
46+
const projectResolver = createRequire(projectSourceRoot + '/').resolve;
47+
let errors: string[] | undefined;
48+
49+
try {
50+
projectResolver('@vitest/browser');
51+
} catch {
52+
errors ??= [];
53+
errors.push(
54+
'The "browsers" option requires the "@vitest/browser" package to be installed within the project.' +
55+
' Please install this package and rerun the test command.',
56+
);
57+
}
58+
59+
const provider = findBrowserProvider(projectResolver);
60+
if (!provider) {
61+
errors ??= [];
62+
errors.push(
63+
'The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
64+
' Please install one of these packages and rerun the test command.',
65+
);
66+
}
67+
68+
// Vitest current requires the playwright browser provider to use the inspect-brk option used by "debug"
69+
if (debug && provider !== 'playwright') {
70+
errors ??= [];
71+
errors.push(
72+
'Debugging browser mode tests currently requires the use of "playwright".' +
73+
' Please install this package and rerun the test command.',
74+
);
75+
}
76+
77+
if (errors) {
78+
return { errors };
79+
}
80+
81+
const browser = {
82+
enabled: true,
83+
provider,
84+
headless: browsers.some((name) => name.toLowerCase().includes('headless')),
85+
86+
instances: browsers.map((browserName) => ({
87+
browser: normalizeBrowserName(browserName),
88+
})),
89+
};
90+
91+
return { browser };
92+
}

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

Lines changed: 1 addition & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { FullResult, IncrementalResult } from '../../../application/results
1919
import { writeTestFiles } from '../../../karma/application_builder';
2020
import { NormalizedUnitTestBuilderOptions } from '../../options';
2121
import type { TestExecutor } from '../api';
22+
import { setupBrowserConfiguration } from './browser-provider';
2223

2324
type VitestCoverageOption = Exclude<InlineConfig['coverage'], undefined>;
2425

@@ -171,89 +172,6 @@ export class VitestExecutor implements TestExecutor {
171172
}
172173
}
173174

174-
function findBrowserProvider(
175-
projectResolver: NodeJS.RequireResolve,
176-
): import('vitest/node').BrowserBuiltinProvider | undefined {
177-
// One of these must be installed in the project to use browser testing
178-
const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const;
179-
180-
for (const providerName of vitestBuiltinProviders) {
181-
try {
182-
projectResolver(providerName);
183-
184-
return providerName;
185-
} catch {}
186-
}
187-
188-
return undefined;
189-
}
190-
191-
function normalizeBrowserName(browserName: string): string {
192-
// Normalize browser names to match Vitest's expectations for headless but also supports karma's names
193-
// e.g., 'ChromeHeadless' -> 'chrome', 'FirefoxHeadless' -> 'firefox'
194-
// and 'Chrome' -> 'chrome', 'Firefox' -> 'firefox'.
195-
const normalized = browserName.toLowerCase();
196-
197-
return normalized.replace(/headless$/, '');
198-
}
199-
200-
function setupBrowserConfiguration(
201-
browsers: string[] | undefined,
202-
debug: boolean,
203-
projectSourceRoot: string,
204-
): { browser?: import('vitest/node').BrowserConfigOptions; errors?: string[] } {
205-
if (browsers === undefined) {
206-
return {};
207-
}
208-
209-
const projectResolver = createRequire(projectSourceRoot + '/').resolve;
210-
let errors: string[] | undefined;
211-
212-
try {
213-
projectResolver('@vitest/browser');
214-
} catch {
215-
errors ??= [];
216-
errors.push(
217-
'The "browsers" option requires the "@vitest/browser" package to be installed within the project.' +
218-
' Please install this package and rerun the test command.',
219-
);
220-
}
221-
222-
const provider = findBrowserProvider(projectResolver);
223-
if (!provider) {
224-
errors ??= [];
225-
errors.push(
226-
'The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
227-
' Please install one of these packages and rerun the test command.',
228-
);
229-
}
230-
231-
// Vitest current requires the playwright browser provider to use the inspect-brk option used by "debug"
232-
if (debug && provider !== 'playwright') {
233-
errors ??= [];
234-
errors.push(
235-
'Debugging browser mode tests currently requires the use of "playwright".' +
236-
' Please install this package and rerun the test command.',
237-
);
238-
}
239-
240-
if (errors) {
241-
return { errors };
242-
}
243-
244-
const browser = {
245-
enabled: true,
246-
provider,
247-
headless: browsers.some((name) => name.toLowerCase().includes('headless')),
248-
249-
instances: browsers.map((browserName) => ({
250-
browser: normalizeBrowserName(browserName),
251-
})),
252-
};
253-
254-
return { browser };
255-
}
256-
257175
function generateOutputPath(): string {
258176
const datePrefix = new Date().toISOString().replaceAll(/[-:.]/g, '');
259177
const uuidSuffix = randomUUID().slice(0, 8);

0 commit comments

Comments
 (0)