From d90da47aec6f1ed51e6fc5e78c684f3ce3385cb4 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:04:57 -0400 Subject: [PATCH] fix(@angular/build): preserve custom config options in runnerConfig for vitest Ensure optional Vitest configuration keys (such as `outputFile`, `cache`, and `testNamePattern`) are only defined on the inline options passed to `startVitest` if they are not `undefined`. This prevents Vitest's config merging from overriding user-defined custom configuration values in the runner configuration file with `undefined`. --- .../unit-test/runners/vitest/executor.ts | 6 +- .../behavior/runner-config-vitest_spec.ts | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) 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 7a9b56168d7b..9d3760322ecb 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 @@ -354,10 +354,10 @@ export class VitestExecutor implements TestExecutor { config: externalConfigPath, root: workspaceRoot, project, - outputFile, - cache: cacheOptions.enabled ? undefined : (false as const), - testNamePattern: this.options.filter, watch, + ...(outputFile !== undefined ? { outputFile } : {}), + ...(cacheOptions.enabled ? {} : { cache: false as const }), + ...(this.options.filter !== undefined ? { testNamePattern: this.options.filter } : {}), ...(typeof ui === 'boolean' ? { ui } : {}), ...debugOptions, }; diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts index 609d736e00f7..ccd745ba5def 100644 --- a/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts @@ -42,6 +42,63 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { harness.expectFile('vitest-results.xml').toExist(); }); + it('should use custom reporters and outputFile defined as an object in runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + reporters: ['junit'], + outputFile: { + junit: './vitest-results-outputfile.xml', + }, + }, + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results-outputfile.xml').toExist(); + }); + + it('should preserve custom coverage options (e.g. clean: false) from runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + coverage: true, + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + coverage: { + clean: false, + }, + }, + }); + `, + ); + + // Create a placeholder file in the coverage directory before running the test + harness.writeFile('coverage/test/placeholder-file.txt', 'do-not-delete'); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + // The builder outputs to coverage/test by default. + // If clean: false is preserved, the placeholder-file.txt should still exist. + harness.expectFile('coverage/test/placeholder-file.txt').toExist(); + }); + it('should override reporters defined in runnerConfig file when CLI option is present', async () => { harness.useTarget('test', { ...BASE_OPTIONS,