diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts index 59a1e5136456..877dbdaf1606 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts @@ -232,16 +232,12 @@ export async function createVitestConfigPlugin( delete config.plugins; } - // Add browser source map support if coverage is enabled + // Validate browser coverage support if coverage is enabled if ( (browser || testConfig?.browser?.enabled) && (options.coverage.enabled || testConfig?.coverage?.enabled) ) { - // Validate that enabled browsers support the selected coverage provider validateBrowserCoverage(browser, testConfig?.browser, determinedProvider); - - projectPlugins.unshift(createSourcemapSupportPlugin()); - setupFiles.unshift('virtual:source-map-support'); } const projectResolver = createRequire(projectSourceRoot + '/').resolve; @@ -408,7 +404,7 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins const map = sourceMapText ? JSON.parse(sourceMapText) : undefined; if (map) { - adjustSourcemapSources(map, !vitestConfig?.coverage?.enabled, workspaceRoot, id); + adjustSourcemapSources(map, true, workspaceRoot, id); } return { @@ -475,36 +471,6 @@ function adjustSourcemapSources( } } -function createSourcemapSupportPlugin(): VitestPlugins[0] { - return { - name: 'angular:source-map-support', - enforce: 'pre', - resolveId(source) { - if (source.includes('virtual:source-map-support')) { - return '\0source-map-support'; - } - }, - async load(id) { - if (id !== '\0source-map-support') { - return; - } - - const packageResolve = createRequire(__filename).resolve; - const supportPath = packageResolve('source-map-support/browser-source-map-support.js'); - - const content = await readFile(supportPath, 'utf-8'); - - // The `source-map-support` library currently relies on `this` being defined in the global scope. - // However, when running in an ESM environment, `this` is undefined. - // To workaround this, we patch the library to use `globalThis` instead of `this`. - return ( - content.replaceAll(/this\.(define|sourceMapSupport|base64js)/g, 'globalThis.$1') + - '\n;globalThis.sourceMapSupport.install();' - ); - }, - }; -} - interface CustomBrowserConfigOptions { enabled?: boolean; instances?: { browser: string }[]; diff --git a/tests/e2e/tests/vitest/browser-coverage-sourcemaps.ts b/tests/e2e/tests/vitest/browser-coverage-sourcemaps.ts new file mode 100644 index 000000000000..db8b91a0abbe --- /dev/null +++ b/tests/e2e/tests/vitest/browser-coverage-sourcemaps.ts @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict'; +import { applyVitestBuilder } from '../../utils/vitest'; +import { ng } from '../../utils/process'; +import { installPackage } from '../../utils/packages'; +import { expectFileToExist, readFile } from '../../utils/fs'; + +export default async function (): Promise { + await applyVitestBuilder(); + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + await installPackage('@vitest/coverage-v8@4'); + + // Run tests with coverage in browser mode. + // We use the default passing tests generated for the project. + const { stdout } = await ng( + 'test', + '--no-watch', + '--browsers', + 'chromiumHeadless', + '--coverage', + '--coverage-reporters=json-summary', + ); + + // Verify that tests passed + assert.match(stdout, /pass/, 'Expected tests to run successfully.'); + + // Verify that coverage files are generated + const summaryPath = 'coverage/test-project/coverage-summary.json'; + await expectFileToExist(summaryPath); + + const summary = JSON.parse(await readFile(summaryPath)); + + // Find the key for app.ts (it might be an absolute path) + const appFileKey = Object.keys(summary).find((key) => key.endsWith('app.ts')); + assert.ok(appFileKey, 'Expected coverage summary to contain app.ts.'); + + const appCoverage = summary[appFileKey]; + assert.ok(appCoverage.lines.pct > 0, 'Expected lines percentage to be greater than 0.'); + + // Also verify that spec files are NOT present in the summary + const specFileKey = Object.keys(summary).find((key) => key.endsWith('.spec.ts')); + assert.ok(!specFileKey, 'Expected coverage report to not contain .spec.ts files.'); +}