Skip to content

Commit 02d78f7

Browse files
committed
fix(@angular/build): correct Vitest coverage path resolution for JSDOM on Windows
This commit addresses an issue where Vitest coverage reports were incomplete on Windows when using the JSDOM test environment. The root cause is an improperly formatted absolute path that can result from manually converting a file URL back to a path on Windows, leading to a superfluous leading slash (e.g., '/D:/path' instead of 'D:/path'). The 'angular:test-in-memory-provider' plugin now explicitly detects and removes this leading slash from absolute Windows paths, thereby correcting the path format and enabling proper source file mapping for coverage collection.
1 parent 8bd7f28 commit 02d78f7

File tree

2 files changed

+19
-10
lines changed

2 files changed

+19
-10
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import assert from 'node:assert';
1010
import { readFile } from 'node:fs/promises';
1111
import { createRequire } from 'node:module';
12+
import { platform } from 'node:os';
1213
import path from 'node:path';
1314
import type {
1415
BrowserConfigOptions,
@@ -165,12 +166,24 @@ export async function createVitestConfigPlugin(
165166

166167
export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins {
167168
const { workspaceRoot, buildResultFiles, testFileToEntryPoint } = pluginOptions;
169+
const isWindows = platform() === 'win32';
168170

169171
return [
170172
{
171173
name: 'angular:test-in-memory-provider',
172174
enforce: 'pre',
173175
resolveId: (id, importer) => {
176+
// Workaround for Vitest in Windows when a fully qualified absolute path is provided with
177+
// a superfluous leading slash. This can currently occur with the `@vitest/coverage-v8` provider
178+
// when it uses `removeStartsWith(url, FILE_PROTOCOL)` to convert a file URL resulting in
179+
// `/D:/tmp_dir/...` instead of `D:/tmp_dir/...`.
180+
if (id[0] === '/' && isWindows) {
181+
const slicedId = id.slice(1);
182+
if (path.isAbsolute(slicedId)) {
183+
return slicedId;
184+
}
185+
}
186+
174187
if (importer && (id[0] === '.' || id[0] === '/')) {
175188
let fullPath;
176189
if (testFileToEntryPoint.has(importer)) {

tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,12 @@ export default async function () {
6363
const { stdout: jsdomStdout } = await ng('test', '--no-watch', '--coverage');
6464
assert.match(jsdomStdout, expectedMessage, `Expected ${totalTests} tests to pass in JSDOM mode.`);
6565

66-
// TODO: Investigate why coverage-final.json is empty on Windows in JSDOM mode.
67-
// For now, skip the coverage report check on Windows.
68-
if (process.platform !== 'win32') {
69-
// Assert that every generated file is in the coverage report by reading the JSON output.
70-
const jsdomSummary = JSON.parse(await readFile(coverageJsonPath));
71-
const jsdomSummaryKeys = Object.keys(jsdomSummary);
72-
for (const file of generatedFiles) {
73-
const found = jsdomSummaryKeys.some((key) => key.endsWith(file));
74-
assert.ok(found, `Expected ${file} to be in the JSDOM coverage report.`);
75-
}
66+
// Assert that every generated file is in the coverage report by reading the JSON output.
67+
const jsdomSummary = JSON.parse(await readFile(coverageJsonPath));
68+
const jsdomSummaryKeys = Object.keys(jsdomSummary);
69+
for (const file of generatedFiles) {
70+
const found = jsdomSummaryKeys.some((key) => key.endsWith(file));
71+
assert.ok(found, `Expected ${file} to be in the JSDOM coverage report.`);
7672
}
7773

7874
// Setup for browser mode

0 commit comments

Comments
 (0)