From 3f61248853490232fc12e79bd03e0d3b67675a25 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 12 May 2025 14:06:20 -0400 Subject: [PATCH] fix(@angular/build): show unit-test error for missing vitest browser package When using the experimental `unit-test` builder with the `vitest` runner and the `browsers` option, an error will now be reported if the `@vitest/browser` package is not found. Previously, the tests would attempt to execute and produce a runtime error and stack trace. Now an explicit error with instructions to install the package will be printed to the console prior to building and executing the tests. --- .../build/src/builders/unit-test/builder.ts | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/angular/build/src/builders/unit-test/builder.ts b/packages/angular/build/src/builders/unit-test/builder.ts index e86895fdf579..c4564468f9c6 100644 --- a/packages/angular/build/src/builders/unit-test/builder.ts +++ b/packages/angular/build/src/builders/unit-test/builder.ts @@ -181,27 +181,17 @@ export async function* execute( let instance: import('vitest/node').Vitest | undefined; // Setup vitest browser options if configured - let browser: import('vitest/node').BrowserConfigOptions | undefined; - if (normalizedOptions.browsers) { - const provider = findBrowserProvider(projectSourceRoot); - if (!provider) { - context.logger.error( - 'The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' + - ' Please install one of these packages and rerun the test command.', - ); - - return { success: false }; - } + const { browser, errors } = setupBrowserConfiguration( + normalizedOptions.browsers, + projectSourceRoot, + ); + if (errors?.length) { + errors.forEach((error) => context.logger.error(error)); - browser = { - enabled: true, - provider, - instances: normalizedOptions.browsers.map((browserName) => ({ - browser: browserName, - })), - }; + return { success: false }; } + // Add setup file entries for TestBed initialization and project polyfills const setupFiles = ['init-testbed.js']; if (buildTargetOptions?.polyfills?.length) { setupFiles.push('polyfills.js'); @@ -252,11 +242,9 @@ export async function* execute( } function findBrowserProvider( - projectSourceRoot: string, + projectResolver: NodeJS.RequireResolve, ): import('vitest/node').BrowserBuiltinProvider | undefined { - const projectResolver = createRequire(projectSourceRoot + '/').resolve; - - // These must be installed in the project to be used + // One of these must be installed in the project to use browser testing const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const; for (const providerName of vitestBuiltinProviders) { @@ -267,3 +255,48 @@ function findBrowserProvider( } catch {} } } + +function setupBrowserConfiguration( + browsers: string[] | undefined, + projectSourceRoot: string, +): { browser?: import('vitest/node').BrowserConfigOptions; errors?: string[] } { + if (browsers === undefined) { + return {}; + } + + const projectResolver = createRequire(projectSourceRoot + '/').resolve; + let errors: string[] | undefined; + + try { + projectResolver('@vitest/browser'); + } catch { + errors ??= []; + errors.push( + 'The "browsers" option requires the "@vitest/browser" package to be installed within the project.' + + ' Please install this package and rerun the test command.', + ); + } + + const provider = findBrowserProvider(projectResolver); + if (!provider) { + errors ??= []; + errors.push( + 'The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' + + ' Please install one of these packages and rerun the test command.', + ); + } + + if (errors) { + return { errors }; + } + + const browser = { + enabled: true, + provider, + instances: browsers.map((browserName) => ({ + browser: browserName, + })), + }; + + return { browser }; +}