diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts index 510cf70545d3..21cdd05c9359 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts @@ -226,7 +226,7 @@ export class VitestExecutor implements TestExecutor { watch: null, }, plugins: [ - createVitestConfigPlugin({ + await createVitestConfigPlugin({ browser: browserOptions.browser, coverage, projectName, diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts index 93ed6497a946..c375d182750d 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts @@ -58,7 +58,9 @@ async function findTestEnvironment( } } -export function createVitestConfigPlugin(options: VitestConfigPluginOptions): VitestPlugins[0] { +export async function createVitestConfigPlugin( + options: VitestConfigPluginOptions, +): Promise { const { include, browser, @@ -69,6 +71,8 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi projectSourceRoot, } = options; + const { mergeConfig } = await import('vitest/config'); + return { name: 'angular:vitest-configuration', async config(config) { @@ -90,17 +94,6 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi delete testConfig.include; } - // The user's setup files should be appended to the CLI's setup files. - const combinedSetupFiles = [...setupFiles]; - if (testConfig?.setupFiles) { - if (typeof testConfig.setupFiles === 'string') { - combinedSetupFiles.push(testConfig.setupFiles); - } else if (Array.isArray(testConfig.setupFiles)) { - combinedSetupFiles.push(...testConfig.setupFiles); - } - delete testConfig.setupFiles; - } - // Merge user-defined plugins from the Vitest config with the CLI's internal plugins. if (config.plugins) { const userPlugins = config.plugins.filter( @@ -115,38 +108,49 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi if (userPlugins.length > 0) { projectPlugins.push(...userPlugins); } + delete config.plugins; } const projectResolver = createRequire(projectSourceRoot + '/').resolve; - const projectConfig: UserWorkspaceConfig = { + const projectDefaults: UserWorkspaceConfig = { + test: { + setupFiles, + globals: true, + // Default to `false` to align with the Karma/Jasmine experience. + isolate: false, + }, + optimizeDeps: { + noDiscovery: true, + include: options.optimizeDepsInclude, + }, + }; + + const { optimizeDeps, resolve } = config; + const projectOverrides: UserWorkspaceConfig = { test: { - ...testConfig, name: projectName, - setupFiles: combinedSetupFiles, include, - globals: testConfig?.globals ?? true, - // Default to `false` to align with the Karma/Jasmine experience. - isolate: testConfig?.isolate ?? false, + // CLI provider browser options override, if present ...(browser ? { browser } : {}), // If the user has not specified an environment, use a smart default. ...(!testConfig?.environment ? { environment: await findTestEnvironment(projectResolver) } : {}), }, - optimizeDeps: { - noDiscovery: true, - include: options.optimizeDepsInclude, - }, plugins: projectPlugins, + optimizeDeps, + resolve, }; + const projectBase = mergeConfig(projectDefaults, testConfig ? { test: testConfig } : {}); + const projectConfig = mergeConfig(projectBase, projectOverrides); + return { test: { coverage: await generateCoverageOption(options.coverage, projectName), // eslint-disable-next-line @typescript-eslint/no-explicit-any ...(reporters ? ({ reporters } as any) : {}), - ...(browser ? { browser } : {}), projects: [projectConfig], }, }; diff --git a/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts index aa0b3c4368fa..afb7aee20d22 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts @@ -16,53 +16,33 @@ import { } from '../setup'; describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { - xdescribe('Option: "browsers"', () => { + describe('Option: "browsers"', () => { beforeEach(async () => { setupApplicationTarget(harness); }); - it('should use jsdom when browsers is not provided', async () => { + it('should use DOM emulation when browsers is not provided', async () => { harness.useTarget('test', { ...BASE_OPTIONS, browsers: undefined, }); - const { result, logs } = await harness.executeOnce(); + const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); - expectLog(logs, 'Using jsdom in Node.js for test execution.'); }); - it('should fail when browsers is empty', async () => { - harness.useTarget('test', { - ...BASE_OPTIONS, - browsers: [], - }); - - await expectAsync(harness.executeOnce()).toBeRejectedWithError( - /must NOT have fewer than 1 items/, - ); - }); - - it('should launch a browser when provided', async () => { + it('should fail when a browser is requested but no provider is installed', async () => { harness.useTarget('test', { ...BASE_OPTIONS, browsers: ['chrome'], }); const { result, logs } = await harness.executeOnce(); - expect(result?.success).toBeTrue(); - expectLog(logs, /Starting browser "chrome"/); - }); - - it('should launch a browser in headless mode when specified', async () => { - harness.useTarget('test', { - ...BASE_OPTIONS, - browsers: ['chromeheadless'], - }); - - const { result, logs } = await harness.executeOnce(); - expect(result?.success).toBeTrue(); - expectLog(logs, /Starting browser "chrome" in headless mode/); + expect(result?.success).toBeFalse(); + expectLog( + logs, + `The "browsers" option requires either "@vitest/browser-playwright", "@vitest/browser-webdriverio", or "@vitest/browser-preview" to be installed`, + ); }); }); });