From 26e0d382e64fe987d371c1c39d1d6bf7b3f0db6d Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:51:37 -0400 Subject: [PATCH] fix(@angular/build): adjust coverage includes/excludes for unit-test vitest runner The experimental `unit-test` builder with `vitest` support will now explicitly include the tested files instead of using the default of all files within the workspace. This prevents a potentially long wait time to calculate coverage. The excludes have also been adjusted to ensure that application code bundled into the intermediate test output is also counted within the coverage reports. --- .../build/src/builders/unit-test/builder.ts | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/angular/build/src/builders/unit-test/builder.ts b/packages/angular/build/src/builders/unit-test/builder.ts index a4dd349d12dd..7f625e0c31c2 100644 --- a/packages/angular/build/src/builders/unit-test/builder.ts +++ b/packages/angular/build/src/builders/unit-test/builder.ts @@ -24,11 +24,13 @@ import { OutputHashing } from '../application/schema'; import { writeTestFiles } from '../karma/application_builder'; import { findTests, getTestEntrypoints } from '../karma/find-tests'; import { useKarmaBuilder } from './karma-bridge'; -import { normalizeOptions } from './options'; +import { NormalizedUnitTestOptions, normalizeOptions } from './options'; import type { Schema as UnitTestOptions } from './schema'; export type { UnitTestOptions }; +type VitestCoverageOption = Exclude; + /** * @experimental Direct usage of this function is considered experimental. */ @@ -230,15 +232,11 @@ export async function* execute( include: [], reporters: normalizedOptions.reporters ?? ['default'], watch: normalizedOptions.watch, - coverage: { - enabled: !!normalizedOptions.codeCoverage, - excludeAfterRemap: true, - exclude: normalizedOptions.codeCoverage?.exclude ?? [], - // Special handling for `reporter` due to an undefined value causing upstream failures - ...(normalizedOptions.codeCoverage?.reporters - ? { reporter: normalizedOptions.codeCoverage.reporters } - : {}), - }, + coverage: generateCoverageOption( + normalizedOptions.codeCoverage, + workspaceRoot, + outputPath, + ), ...debugOptions, }, { @@ -249,7 +247,7 @@ export async function* execute( // Create a subproject that can be configured with plugins for browser mode. // Plugins defined directly in the vite overrides will not be present in the // browser specific Vite instance. - await context.injectTestProjects({ + const [project] = await context.injectTestProjects({ test: { name: projectName, root: outputPath, @@ -282,6 +280,15 @@ export async function* execute( }, ], }); + + // Adjust coverage excludes to not include the otherwise automatically inserted included unit tests. + // Vite does this as a convenience but is problematic for the bundling strategy employed by the + // builder's test setup. To workaround this, the excludes are adjusted here to only automaticallyAdd commentMore actions + // exclude the TypeScript source test files. + project.config.coverage.exclude = [ + ...(normalizedOptions.codeCoverage?.exclude ?? []), + '**/*.{test,spec}.?(c|m)ts', + ]; }, }, ], @@ -377,3 +384,24 @@ function generateOutputPath(): string { return path.join('dist', 'test-out', `${datePrefix}-${uuidSuffix}`); } +function generateCoverageOption( + codeCoverage: NormalizedUnitTestOptions['codeCoverage'], + workspaceRoot: string, + outputPath: string, +): VitestCoverageOption { + if (!codeCoverage) { + return { + enabled: false, + }; + } + + return { + enabled: true, + excludeAfterRemap: true, + include: [`${path.relative(workspaceRoot, outputPath)}/**`], + // Special handling for `reporter` due to an undefined value causing upstream failures + ...(codeCoverage.reporters + ? ({ reporter: codeCoverage.reporters } satisfies VitestCoverageOption) + : {}), + }; +}