Skip to content

Commit b17a510

Browse files
committed
fix(@angular/build): deduplicate and merge coverage excludes with vitest
Previously, providing a --coverage-exclude CLI option to the builder would completely clobber any custom coverage.exclude items defined natively within vitest.config.ts. This correctly merges both sources using an internal Set to prevent duplicate exclusions and preserves configurations so developers can combine global ignores alongside CLI-specific boundaries.
1 parent 9cb03fe commit b17a510

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,12 +437,15 @@ async function generateCoverageOption(
437437
// Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures
438438
...(optionsCoverage.exclude
439439
? {
440-
exclude: [
441-
// Augment the default exclude https://vitest.dev/config/#coverage-exclude
442-
// with the user defined exclusions
443-
...optionsCoverage.exclude,
444-
...defaultExcludes,
445-
],
440+
exclude: Array.from(
441+
new Set([
442+
// Augment the default exclude https://vitest.dev/config/#coverage-exclude
443+
// with the user defined exclusions
444+
...(configCoverage?.exclude || []),
445+
...optionsCoverage.exclude,
446+
...defaultExcludes,
447+
]),
448+
),
446449
}
447450
: {}),
448451
...(optionsCoverage.reporters

packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,59 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
164164
expect(results.numPassedTests).toBe(1);
165165
});
166166

167+
it('should correctly merge coverage.exclude arrays from builder and runner options', async () => {
168+
harness.useTarget('test', {
169+
...BASE_OPTIONS,
170+
coverage: true,
171+
runnerConfig: 'vitest.config.ts',
172+
coverageExclude: ['src/app/cli-excluded.ts'],
173+
});
174+
175+
harness.writeFile(
176+
'vitest.config.ts',
177+
`
178+
import { defineConfig } from 'vitest/config';
179+
export default defineConfig({
180+
test: {
181+
coverage: {
182+
exclude: ['src/app/config-excluded.ts'],
183+
},
184+
},
185+
});
186+
`,
187+
);
188+
189+
// Create two files that would normally be covered
190+
harness.writeFile('src/app/cli-excluded.ts', 'export const cliExcluded = true;');
191+
harness.writeFile('src/app/config-excluded.ts', 'export const configExcluded = true;');
192+
193+
// Update the test file to import them so they're picked up by coverage
194+
harness.writeFile(
195+
'src/app/app.component.spec.ts',
196+
`
197+
import { test, expect } from 'vitest';
198+
import './cli-excluded';
199+
import './config-excluded';
200+
test('should pass', () => {
201+
expect(true).toBe(true);
202+
});
203+
`,
204+
);
205+
206+
const { result } = await harness.executeOnce();
207+
expect(result?.success).toBeTrue();
208+
harness.expectFile('coverage/test/coverage-final.json').toExist();
209+
210+
const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
211+
const coveredFiles = Object.keys(coverageMap);
212+
213+
const hasCliExcluded = coveredFiles.some((f) => f.includes('cli-excluded.ts'));
214+
const hasConfigExcluded = coveredFiles.some((f) => f.includes('config-excluded.ts'));
215+
216+
expect(hasCliExcluded).withContext('CLI target should be excluded').toBeFalse();
217+
expect(hasConfigExcluded).withContext('Config file target should be excluded').toBeFalse();
218+
});
219+
167220
it('should allow overriding globals to false via runnerConfig file', async () => {
168221
harness.useTarget('test', {
169222
...BASE_OPTIONS,

0 commit comments

Comments
 (0)