Skip to content

Commit b47fea6

Browse files
committed
fix(@angular/build): resolve test files correctly on Windows when using non-C drives
Ensures that test files requested via root-relative paths (common on Windows with non-C drives or specific Vitest configurations) are correctly resolved to their absolute path entry points. This fix prevents 'Cannot find module' errors by explicitly checking the test entry point map after normalizing the path to an absolute POSIX path.
1 parent 59e3ccc commit b47fea6

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
229229
// Construct the full, absolute path and normalize it to POSIX format.
230230
const fullPath = toPosixPath(path.join(baseDir, id));
231231

232+
if (testFileToEntryPoint.has(fullPath)) {
233+
return fullPath;
234+
}
235+
232236
// Check if the resolved path corresponds to a known build artifact.
233237
const relativePath = path.relative(workspaceRoot, fullPath);
234238
if (buildResultFiles.has(toPosixPath(relativePath))) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import assert from 'node:assert/strict';
2+
import * as fs from 'node:fs';
3+
import * as path from 'node:path';
4+
import { execAndCaptureError, silentExec } from '../../utils/process';
5+
import { applyVitestBuilder } from '../../utils/vitest';
6+
import { stripVTControlCharacters } from 'node:util';
7+
8+
export default async function (): Promise<void> {
9+
// This test uses `subst` to map the project directory to a virtual drive letter
10+
// to simulate running tests from a non-C drive on Windows.
11+
if (process.platform !== 'win32') {
12+
return;
13+
}
14+
15+
await applyVitestBuilder();
16+
17+
const originalCwd = process.cwd();
18+
const driveLetter = 'X:'; // Pick a drive letter that is unlikely to be in use.
19+
20+
try {
21+
// 1. Map the parent directory of the project to the virtual drive.
22+
// This avoids running the project from the root of the drive (X:\), which can cause
23+
// issues with workspace detection.
24+
const projectParentDir = path.dirname(originalCwd);
25+
const projectName = path.basename(originalCwd);
26+
27+
await silentExec('subst', driveLetter, projectParentDir);
28+
29+
// 2. Change the current process's working directory to the project folder on the virtual drive.
30+
const newCwd = path.join(driveLetter + '\\', projectName);
31+
process.chdir(newCwd);
32+
33+
// Verify that the file system mapping is working as expected.
34+
assert(fs.existsSync('angular.json'), 'angular.json should exist on the subst drive');
35+
36+
// 3. Run `ng test`.
37+
// We expect this to fail with NG0203 in the subst environment due to dual-package hazards
38+
// (Angular loading from both X: and D:) within bazel. However, the failure proves that the
39+
// test file was discovered and loaded.
40+
const error = await execAndCaptureError('ng', ['test', '--watch=false']);
41+
const output = stripVTControlCharacters(error.message);
42+
43+
console.log('OUTPUT:', output);
44+
45+
// 4. Verify that Vitest found the test file and identified the tests within it.
46+
assert.match(
47+
output,
48+
/src\/app\/app\.spec\.ts \(2 tests/,
49+
'Expected tests to be discovered and loaded, even if execution fails due to subst aliasing.',
50+
);
51+
} finally {
52+
// 5. Teardown: Restore CWD and remove the virtual drive mapping.
53+
try {
54+
process.chdir(originalCwd);
55+
} catch (e) {
56+
console.error('Failed to restore CWD:', e);
57+
}
58+
59+
try {
60+
await silentExec('subst', driveLetter, '/d');
61+
} catch (e) {
62+
// Ignore errors if the drive wasn't mounted or if unmount fails (best effort)
63+
console.error(`Failed to unmount ${driveLetter}:`, e);
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)