Skip to content

Commit b71b59f

Browse files
committed
test(@angular/build): verify coverage ignore comments are preserved during compilation
The underlying Vitest coverage engine depends on specific developer comments like `/* istanbul ignore next */` or `/* v8 ignore next */` being present in the executing code to accurately isolate unmeasured blocks. This commit adds strict behavioral tests to assert that the Angular CLI's in-memory compilation pipeline (via esbuild) properly preserves these structural comments and forwards them reliably to Vitest's coverage processing engine.
1 parent 685ebae commit b71b59f

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { execute } from '../../index';
10+
import {
11+
BASE_OPTIONS,
12+
describeBuilder,
13+
UNIT_TEST_BUILDER_INFO,
14+
setupApplicationTarget,
15+
} from '../setup';
16+
17+
describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
18+
describe('Behavior: "coverage ignore comments"', () => {
19+
beforeEach(async () => {
20+
setupApplicationTarget(harness);
21+
});
22+
23+
it('should respect istanbul ignore next comments when computing coverage', async () => {
24+
harness.useTarget('test', {
25+
...BASE_OPTIONS,
26+
coverage: true,
27+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28+
coverageReporters: ['json'] as any,
29+
});
30+
31+
harness.writeFile(
32+
'src/app/app.component.ts',
33+
`
34+
import { Component } from '@angular/core';
35+
36+
@Component({
37+
selector: 'app-root',
38+
template: '<h1>hello</h1>',
39+
standalone: true,
40+
})
41+
export class AppComponent {
42+
title = 'app';
43+
44+
/* istanbul ignore next */
45+
untestedFunction() {
46+
return false;
47+
}
48+
}
49+
`,
50+
);
51+
52+
harness.writeFile(
53+
'src/app/app.component.spec.ts',
54+
`
55+
import { AppComponent } from './app.component';
56+
57+
describe('AppComponent', () => {
58+
it('should create', () => {
59+
const comp = new AppComponent();
60+
expect(comp).toBeTruthy();
61+
});
62+
});
63+
`,
64+
);
65+
66+
const { result } = await harness.executeOnce();
67+
expect(result?.success).toBeTrue();
68+
harness.expectFile('coverage/test/coverage-final.json').toExist();
69+
70+
const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
71+
const appComponentPath = Object.keys(coverageMap).find((p) => p.includes('app.component.ts'));
72+
expect(appComponentPath).toBeDefined();
73+
74+
const appComponentCoverage = coverageMap[appComponentPath!];
75+
76+
const statementCounts = Object.values(appComponentCoverage.s) as number[];
77+
const hasUncoveredStatements = statementCounts.some((count) => count === 0);
78+
expect(hasUncoveredStatements)
79+
.withContext('There should be no uncovered statements as the uncalled function was ignored')
80+
.toBeFalse();
81+
});
82+
83+
it('should respect v8 ignore next comments when computing coverage', async () => {
84+
harness.useTarget('test', {
85+
...BASE_OPTIONS,
86+
coverage: true,
87+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
88+
coverageReporters: ['json'] as any,
89+
});
90+
91+
harness.writeFile(
92+
'src/app/app.component.ts',
93+
`
94+
import { Component } from '@angular/core';
95+
96+
@Component({
97+
selector: 'app-root',
98+
template: '<h1>hello</h1>',
99+
standalone: true,
100+
})
101+
export class AppComponent {
102+
title = 'app';
103+
104+
/* v8 ignore next */
105+
untestedFunction() {
106+
return false;
107+
}
108+
}
109+
`,
110+
);
111+
112+
harness.writeFile(
113+
'src/app/app.component.spec.ts',
114+
`
115+
import { AppComponent } from './app.component';
116+
117+
describe('AppComponent', () => {
118+
it('should create', () => {
119+
const comp = new AppComponent();
120+
expect(comp).toBeTruthy();
121+
});
122+
});
123+
`,
124+
);
125+
126+
const { result } = await harness.executeOnce();
127+
expect(result?.success).toBeTrue();
128+
harness.expectFile('coverage/test/coverage-final.json').toExist();
129+
130+
const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
131+
const appComponentPath = Object.keys(coverageMap).find((p) => p.includes('app.component.ts'));
132+
expect(appComponentPath).toBeDefined();
133+
134+
const appComponentCoverage = coverageMap[appComponentPath!];
135+
136+
const statementCounts = Object.values(appComponentCoverage.s) as number[];
137+
const hasUncoveredStatements = statementCounts.some((count) => count === 0);
138+
expect(hasUncoveredStatements)
139+
.withContext('There should be no uncovered statements as the uncalled function was ignored')
140+
.toBeFalse();
141+
});
142+
});
143+
});

0 commit comments

Comments
 (0)