Skip to content

Commit 66f0cf7

Browse files
committed
fix(@angular/build): inject source-map-support for Vitest browser tests
This change ensures that `source-map-support` is injected into the setup files when running Vitest tests in a browser environment. This allows stack traces from failing tests to correctly map back to the original source files, significantly improving debugging capabilities. A regression test has been added to `tests/vitest/browser-sourcemaps.ts` to verify that a failing test correctly identifies the source file in its stack trace.
1 parent efd1180 commit 66f0cf7

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

packages/angular/build/src/builders/unit-test/options.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ export async function normalizeOptions(
135135
};
136136
}
137137

138-
export function injectTestingPolyfills(polyfills: string[] = []): string[] {
139-
return polyfills.includes('zone.js') ? [...polyfills, 'zone.js/testing'] : polyfills;
138+
export function injectTestingPolyfills(configuredPolyfills: string[] = []): string[] {
139+
const updatedPolyfills = [...configuredPolyfills];
140+
141+
// Add zone.js testing if zone.js is present
142+
if (configuredPolyfills.includes('zone.js')) {
143+
updatedPolyfills.push('zone.js/testing');
144+
}
145+
146+
return updatedPolyfills;
140147
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import { createRequire } from 'node:module';
910
import path from 'node:path';
1011
import { toPosixPath } from '../../../../utils/path';
1112
import type { ApplicationBuilderInternalOptions } from '../../../application/options';
@@ -18,6 +19,7 @@ function createTestBedInitVirtualFile(
1819
providersFile: string | undefined,
1920
projectSourceRoot: string,
2021
polyfills: string[] = [],
22+
sourcemapSupport: boolean = false,
2123
): string {
2224
const usesZoneJS = polyfills.includes('zone.js');
2325
let providersImport = 'const providers = [];';
@@ -28,13 +30,26 @@ function createTestBedInitVirtualFile(
2830
providersImport = `import providers from './${importPath}';`;
2931
}
3032

33+
// Resolve and add sourcemap support (mainly for browsers)
34+
let sourceMapSetup;
35+
if (sourcemapSupport) {
36+
const packageResolve = createRequire(__filename).resolve;
37+
const sourceMapPath = packageResolve('source-map-support');
38+
39+
sourceMapSetup = `
40+
import sourceMapSupport from '${sourceMapPath}';
41+
sourceMapSupport.install();
42+
`;
43+
}
44+
3145
return `
3246
// Initialize the Angular testing environment
3347
import { NgModule${usesZoneJS ? ', provideZoneChangeDetection' : ''} } from '@angular/core';
3448
import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing';
3549
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
3650
import { afterEach, beforeEach } from 'vitest';
3751
${providersImport}
52+
${sourceMapSetup}
3853
3954
// The beforeEach and afterEach hooks are registered outside the globalThis guard.
4055
// This ensures that the hooks are always applied, even in non-isolated browser environments.
@@ -134,6 +149,7 @@ export async function getVitestBuildOptions(
134149
providersFile,
135150
projectSourceRoot,
136151
buildOptions.polyfills,
152+
!!options.browsers?.length,
137153
);
138154

139155
return {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import assert from 'node:assert/strict';
2+
import { applyVitestBuilder } from '../../utils/vitest';
3+
import { ng } from '../../utils/process';
4+
import { installPackage } from '../../utils/packages';
5+
import { writeFile } from '../../utils/fs';
6+
7+
export default async function (): Promise<void> {
8+
await applyVitestBuilder();
9+
await installPackage('playwright@1');
10+
await installPackage('@vitest/browser-playwright@4');
11+
await ng('generate', 'component', 'my-comp');
12+
13+
// Add a failing test to verify source map support
14+
await writeFile(
15+
'src/app/failing.spec.ts',
16+
`
17+
describe('Failing Test', () => {
18+
it('should fail', () => {
19+
throw new Error('This is a failing test');
20+
});
21+
});
22+
`,
23+
);
24+
25+
try {
26+
await ng('test', '--no-watch', '--browsers', 'chromiumHeadless');
27+
throw new Error('Expected "ng test" to fail.');
28+
} catch (error: any) {
29+
const stdout = error.stdout || error.message;
30+
// We expect the failure from failing.spec.ts
31+
assert.match(stdout, /1 failed/, 'Expected 1 test to fail.');
32+
// Check that the stack trace points to the correct file
33+
assert.match(
34+
stdout,
35+
/src\/app\/failing\.spec\.ts:\d+:\d+/,
36+
'Expected stack trace to point to the source file.',
37+
);
38+
}
39+
}

0 commit comments

Comments
 (0)