Repository navigation
Closed
Labels
area: compilerIssues related to `ngc`, Angular's template compilerIssues related to `ngc`, Angular's template compilercross-cutting: signals
Milestone
Description
Command
test
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
Running ng test with AoT enabled produced inaccurate code coverage for branches, with signals (always?) marked as a not tested branch.
For example: input.required<UserModel>(); has <UserModel>() marked as untested (but input.required is marked as tested),
computed(...) or signal(...) have their last parenthesis marked as an untested branch as well.
Minimal Reproduction
This can be reproduced in a brand new CLI app.
npx @angular/cli@21.0.0-next.8 new aot-coverage --defaults --no-routing
cd aot-coverage
Running ng test --no-watch --code-coverage gives:
=============================== Coverage summary ===============================
Statements : 100% ( 5/5 )
Branches : 100% ( 0/0 )
Functions : 100% ( 0/0 )
Lines : 100% ( 3/3 )
================================================================================
Running ng test --no-watch --code-coverage --aot gives:
=============================== Coverage summary ===============================
Statements : 100% ( 50/50 )
Branches : 85.71% ( 6/7 )
Functions : 100% ( 7/7 )
Lines : 100% ( 43/43 )
================================================================================
In coverage/aot-coverage/app.ts.html we can see the last parenthesis of the signal is marked as not tested:
Exception or Error
Your Environment
Angular CLI : 21.0.0-next.8
Angular : 21.0.0-next.8
Node.js : 22.18.0
Package Manager : npm 10.9.0
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 21.0.0-next.8 │ ^21.0.0-next.8 │
│ @angular/cli │ 21.0.0-next.8 │ ^21.0.0-next.8 │
│ @angular/common │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/compiler │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/compiler-cli │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/core │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/forms │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/platform-browser │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/router │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.2 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else relevant?
Note that the same happens with the unit-test runner, with Vitest or Karma.
Reacted by Ken, rexsuecia, AndreasGartmyr, Loïc Chau and Jeroen Akkerman
Metadata
Metadata
Assignees
Labels
area: compilerIssues related to `ngc`, Angular's template compilerIssues related to `ngc`, Angular's template compilercross-cutting: signals
Activity
[-]Inaccurate code coverage ith aot testing[/-][+]Inaccurate code coverage with aot testing[/+]clydin commentedon Oct 22, 2025
Most likely cause is the
signalMetadataTransformlocated here in the Angular compiler:angular/packages/compiler-cli/src/ngtsc/transform/src/implicit_signal_debug_name_transform.ts
Line 478 in 9126d0d
Transferring to the framework repo for further triage.
JeanMeche commentedon Oct 23, 2025
I'm seeing that we apply the transform unconditionnaly. This raises 2 issues
dgp1130 commentedon Oct 27, 2025
I'm assuming the issue here is that we're effectively generating a conditional on
ngDevModealong the lines ofngDevMode ? 'someDebugName' : undefined. Since testing only runs withngDevMode === true, thefalsepart of this branch is never executed, triggering coverage with weird line numbers, since this isn't present in source code.We could disable the transform for
ng test, but this is not trivial to do in google3, since we share a single emit format for both testing and non-testing use cases, so we can't easily determine when to enable / disable this transform.I think the better solution is likely to continue to always use this transform but then include some kind of ignore comment telling the coverage tool to ignore the false conditional.
c8seems to support an ignore statement and I vaguely recall some kind ofLCOV_IGNOREdirective? Not sure of the full matrix of support coverage tools and their opt-outs to know how viable this really is.fagia commentedon Dec 22, 2025
Still same issue also with Angular 21.0.1
Ritzlgrmft commentedon Jan 12, 2026
still the same issue with Angular 21.0.5
is there any workaround beside ignoring signal lines like:
Martinspire commentedon Feb 26, 2026
So what can we do about this? Is there some setting that is conflicting or what? With Analog Vitest, I did not have this issue (perhaps because they compiled it themselves). But migration is blocked if the numbers aren't reported properly. I also think that ignoring code might lead to other issues as well.
mpalourdio commentedon Feb 26, 2026
I find this a little irritating too. We force quality gates in our CI (based on code coverage among other metrics), but for now, we had to disable this check for angular 21 projects.
Could this get a bit of love please ? Thanks!
fix(compiler-cli): ignore generated ngDevMode signal branch for code …
fix(compiler-cli): ignore generated ngDevMode signal branch for code …
fix(compiler-cli): ignore generated ngDevMode signal branch for code …
fix(compiler-cli): ignore generated ngDevMode signal branch for code …
angular-automatic-lock-bot commentedon Apr 4, 2026
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.