Skip to content

Commit 299ad3c

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 299ad3c

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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).withContext('There should be no uncovered statements as the uncalled function was ignored').toBeFalse();
79+
});
80+
81+
it('should respect v8 ignore next comments when computing coverage', async () => {
82+
harness.useTarget('test', {
83+
...BASE_OPTIONS,
84+
coverage: true,
85+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
86+
coverageReporters: ['json'] as any,
87+
});
88+
89+
harness.writeFile(
90+
'src/app/app.component.ts',
91+
`
92+
import { Component } from '@angular/core';
93+
94+
@Component({
95+
selector: 'app-root',
96+
template: '<h1>hello</h1>',
97+
standalone: true,
98+
})
99+
export class AppComponent {
100+
title = 'app';
101+
102+
/* v8 ignore next */
103+
untestedFunction() {
104+
return false;
105+
}
106+
}
107+
`,
108+
);
109+
110+
harness.writeFile(
111+
'src/app/app.component.spec.ts',
112+
`
113+
import { AppComponent } from './app.component';
114+
115+
describe('AppComponent', () => {
116+
it('should create', () => {
117+
const comp = new AppComponent();
118+
expect(comp).toBeTruthy();
119+
});
120+
});
121+
`,
122+
);
123+
124+
const { result } = await harness.executeOnce();
125+
expect(result?.success).toBeTrue();
126+
harness.expectFile('coverage/test/coverage-final.json').toExist();
127+
128+
const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
129+
const appComponentPath = Object.keys(coverageMap).find((p) => p.includes('app.component.ts'));
130+
expect(appComponentPath).toBeDefined();
131+
132+
const appComponentCoverage = coverageMap[appComponentPath!];
133+
134+
const statementCounts = Object.values(appComponentCoverage.s) as number[];
135+
const hasUncoveredStatements = statementCounts.some((count) => count === 0);
136+
expect(hasUncoveredStatements).withContext('There should be no uncovered statements as the uncalled function was ignored').toBeFalse();
137+
});
138+
});
139+
});

0 commit comments

Comments
 (0)