You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
20.x (Karma + karma-coverage-istanbul-reporter)
Description
@vitest/coverage-v8 produces an lcov report with the correct file/function/line structure for every source file — but with DA:line,0 for every executable line, across every file. Every FNH is 0, every LH is 0. Tests run successfully and the assertions hit the production code (vitest verbose output confirms ✓ for each spec), but no execution data is ever associated with the lines in the lcov.
This reproduces on Linux (GitHub Actions ubuntu-latest, native Node, no Docker), so it is not the same as #31842 / #31895, which were limited to Windows path handling and were addressed by #31862.
Downstream impact: SonarCloud's quality gate computes new-code coverage as 0% on every PR regardless of how thoroughly the changed files are tested, because every line in the lcov has zero hits.
Minimal Reproduction
Create a new Angular 21 workspace with the default test runner (vitest):
ng new repro-app
cd repro-app
Install the V8 coverage provider:
npm install --save-dev @vitest/coverage-v8
Configure zoneless change detection by editing src/app/app.config.ts (or the generated providers file) to use:
provideZonelessChangeDetection()
Generate enough components and specs to exceed roughly 15 spec files (smaller suites may also reproduce intermittently):
for i in $(seq 1 16); do ng generate component "feature-$i"; done
Each generated *.component.spec.ts already contains a should create test that exercises the component class.
Ensure angular.json's test target is the default:
{
"builder": "@angular/build:unit-test",
"options": {
"runner": "vitest",
"coverageReporters": ["lcovonly"]
}
}
Run on Linux (or in a Linux-based CI such as GitHub Actions ubuntu-latest):
ng test --coverage
Inspect coverage//lcov.info.
Expected: lines covered by the auto-generated should create tests have non-zero DA:line,N hit counts.
Actual: every DA: line is DA:line,0, every function shows FNDA:0,..., and LH:0 / FNH:0 for every source file — even though vitest verbose output confirms each should create test passes.
Exception or Error
None — the failure is silent. No error, no warning, no stack trace. Tests run to completion, vitest reports them as passing, the lcov file is produced at the expected path. The only signal of the bug is that every hit count in the lcov is zero.
Not browser-specific (the bug is in the test runner / coverage collection, before any browser is involved). The default jsdom environment is used.
Reproduces on Linux CI (GitHub Actions ubuntu-latest, Node 22.x, native runner — not Docker). The Windows leading-slash workaround introduced in PR #31862 for issue #31842 is present in 21.2.8 but doesn't help here because the underlying problem on Linux is different.
Diagnostic data points (in case useful for triage):
The lcov is populated with ~230 SF entries with proper line/function maps; only the hit counts are missing. So the source-map remap from the per-spec bundles to the original .ts files runs and produces the correct file structure — but no execution data is joined to it.
Switching vitest.config.tscoverage.provider to 'istanbul' has no effect: the builder hardcodes V8 (packages/angular/build/src/builders/unit-test/runners/vitest/index.js:46 — checker.check('@vitest/coverage-v8')).
Setting pool: 'forks' in the runnerConfig has no effect either, which lines up with Vitest configuration partially ignore? #31972 (parts of runnerConfig are silently ignored). This makes it hard to test whether worker_threads coverage aggregation is part of the cause.
The virtual-module fix from cd5c92b is present in 21.2.8 (angular:test-in-memory-provider plugin emits the import "./<bundle>.js"; indirection).
All coverage / coverageReporters: ["lcovonly"] config is in place and lcov.info lands at the expected path.
SonarCloud reads the lcov correctly and even matches paths against indexed sources — what's missing is the actual hit data, not the file mapping.
Hypothesis: V8 records execution against the per-spec bundles emitted by the in-memory build, but the source-map remap from those bundles back to the .ts files emits the file structure into lcov without carrying hit data — likely either because the source maps aren't seen as physical files by the coverage provider's resolver (similar in spirit to the file-existence check that prompted the BaseCoverageProvider patch later removed in vitest 4.0.6 — possibly that resolver still has a path that fails on Linux for in-memory entries), or the bundle script IDs registered with V8 don't match the IDs the resolver looks up.
Downstream impact: this makes SonarCloud's quality gate fail on every PR with 0.0% Coverage on New Code, regardless of how thoroughly the changed files are tested.
Command
test
Is this a regression?
The previous version in which this bug was not present was
20.x (Karma + karma-coverage-istanbul-reporter)
Description
@vitest/coverage-v8 produces an lcov report with the correct file/function/line structure for every source file — but with
DA:line,0for every executable line, across every file. EveryFNHis0, everyLHis0. Tests run successfully and the assertions hit the production code (vitest verbose output confirms✓for each spec), but no execution data is ever associated with the lines in the lcov.This reproduces on Linux (GitHub Actions ubuntu-latest, native Node, no Docker), so it is not the same as #31842 / #31895, which were limited to Windows path handling and were addressed by #31862.
Downstream impact: SonarCloud's quality gate computes new-code coverage as 0% on every PR regardless of how thoroughly the changed files are tested, because every line in the lcov has zero hits.
Minimal Reproduction
Create a new Angular 21 workspace with the default test runner (vitest):
ng new repro-app
cd repro-app
Install the V8 coverage provider:
npm install --save-dev @vitest/coverage-v8
Configure zoneless change detection by editing src/app/app.config.ts (or the generated providers file) to use:
provideZonelessChangeDetection()
Generate enough components and specs to exceed roughly 15 spec files (smaller suites may also reproduce intermittently):
for i in $(seq 1 16); do ng generate component "feature-$i"; done
Each generated
*.component.spec.tsalready contains ashould createtest that exercises the component class.Ensure angular.json's test target is the default:
{
"builder": "@angular/build:unit-test",
"options": {
"runner": "vitest",
"coverageReporters": ["lcovonly"]
}
}
Run on Linux (or in a Linux-based CI such as GitHub Actions ubuntu-latest):
ng test --coverage
Inspect coverage//lcov.info.
Expected: lines covered by the auto-generated
should createtests have non-zeroDA:line,Nhit counts.Actual: every
DA:line isDA:line,0, every function showsFNDA:0,..., andLH:0/FNH:0for every source file — even though vitest verbose output confirms eachshould createtest passes.Exception or Error
Your Environment
Anything else relevant?
Not browser-specific (the bug is in the test runner / coverage collection, before any browser is involved). The default jsdom environment is used.
Reproduces on Linux CI (GitHub Actions ubuntu-latest, Node 22.x, native runner — not Docker). The Windows leading-slash workaround introduced in PR #31862 for issue #31842 is present in 21.2.8 but doesn't help here because the underlying problem on Linux is different.
Diagnostic data points (in case useful for triage):
.tsfiles runs and produces the correct file structure — but no execution data is joined to it.vitest.config.tscoverage.providerto'istanbul'has no effect: the builder hardcodes V8 (packages/angular/build/src/builders/unit-test/runners/vitest/index.js:46—checker.check('@vitest/coverage-v8')).pool: 'forks'in therunnerConfighas no effect either, which lines up with Vitest configuration partially ignore? #31972 (parts ofrunnerConfigare silently ignored). This makes it hard to test whether worker_threads coverage aggregation is part of the cause.angular:test-in-memory-providerplugin emits theimport "./<bundle>.js";indirection).coverage/coverageReporters: ["lcovonly"]config is in place and lcov.info lands at the expected path.Hypothesis: V8 records execution against the per-spec bundles emitted by the in-memory build, but the source-map remap from those bundles back to the
.tsfiles emits the file structure into lcov without carrying hit data — likely either because the source maps aren't seen as physical files by the coverage provider's resolver (similar in spirit to the file-existence check that prompted the BaseCoverageProvider patch later removed in vitest 4.0.6 — possibly that resolver still has a path that fails on Linux for in-memory entries), or the bundle script IDs registered with V8 don't match the IDs the resolver looks up.Related issues:
Downstream impact: this makes SonarCloud's quality gate fail on every PR with
0.0% Coverage on New Code, regardless of how thoroughly the changed files are tested.