Skip to content

Commit d097df2

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. (cherry picked from commit 3020b50)
1 parent c568c0d commit d097df2

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

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

Lines changed: 27 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,
@@ -173,6 +174,7 @@ async function loadResultFile(file: ResultFile): Promise<string> {
173174

174175
export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins {
175176
const { workspaceRoot, buildResultFiles, testFileToEntryPoint } = pluginOptions;
177+
const isWindows = platform() === 'win32';
176178

177179
return [
178180
{
@@ -184,6 +186,31 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
184186
return id;
185187
}
186188

189+
// Workaround for Vitest in Windows when a fully qualified absolute path is provided with
190+
// a superfluous leading slash. This can currently occur with the `@vitest/coverage-v8` provider
191+
// when it uses `removeStartsWith(url, FILE_PROTOCOL)` to convert a file URL resulting in
192+
// `/D:/tmp_dir/...` instead of `D:/tmp_dir/...`.
193+
if (id[0] === '/' && isWindows) {
194+
const slicedId = id.slice(1);
195+
if (path.isAbsolute(slicedId)) {
196+
return slicedId;
197+
}
198+
}
199+
200+
if (importer && (id[0] === '.' || id[0] === '/')) {
201+
let fullPath;
202+
if (testFileToEntryPoint.has(importer)) {
203+
fullPath = toPosixPath(path.join(workspaceRoot, id));
204+
} else {
205+
fullPath = toPosixPath(path.join(path.dirname(importer), id));
206+
}
207+
208+
const relativePath = path.relative(workspaceRoot, fullPath);
209+
if (buildResultFiles.has(toPosixPath(relativePath))) {
210+
return fullPath;
211+
}
212+
}
213+
187214
// Determine the base directory for resolution.
188215
let baseDir: string;
189216
if (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
@@ -36,16 +36,12 @@ export default async function () {
3636
const { stdout: jsdomStdout } = await ng('test', '--no-watch', '--coverage');
3737
assert.match(jsdomStdout, expectedMessage, `Expected ${totalTests} tests to pass in JSDOM mode.`);
3838

39-
// TODO: Investigate why coverage-final.json is empty on Windows in JSDOM mode.
40-
// For now, skip the coverage report check on Windows.
41-
if (process.platform !== 'win32') {
42-
// Assert that every generated file is in the coverage report by reading the JSON output.
43-
const jsdomSummary = JSON.parse(await readFile(coverageJsonPath));
44-
const jsdomSummaryKeys = Object.keys(jsdomSummary);
45-
for (const file of generatedFiles) {
46-
const found = jsdomSummaryKeys.some((key) => key.endsWith(file));
47-
assert.ok(found, `Expected ${file} to be in the JSDOM coverage report.`);
48-
}
39+
// Assert that every generated file is in the coverage report by reading the JSON output.
40+
const jsdomSummary = JSON.parse(await readFile(coverageJsonPath));
41+
const jsdomSummaryKeys = Object.keys(jsdomSummary);
42+
for (const file of generatedFiles) {
43+
const found = jsdomSummaryKeys.some((key) => key.endsWith(file));
44+
assert.ok(found, `Expected ${file} to be in the JSDOM coverage report.`);
4945
}
5046

5147
// Setup for browser mode

0 commit comments

Comments
 (0)