Skip to content

Commit e981149

Browse files
committed
test_runner: report coverage thresholds in test:coverage
1 parent b77476d commit e981149

File tree

6 files changed

+74
-17
lines changed

6 files changed

+74
-17
lines changed

doc/api/test.md

+5
Original file line numberDiff line numberDiff line change
@@ -2814,6 +2814,11 @@ are defined, while others are emitted in the order that the tests execute.
28142814
numbers and the number of times they were covered.
28152815
* `line` {number} The line number.
28162816
* `count` {number} The number of times the line was covered.
2817+
* `thresholds` {Object} An object containing whether or not the coverage for
2818+
each coverage type.
2819+
* `function` {boolean} Whether or not the coverage surpassed the `function` threshold.
2820+
* `branch` {boolean} Whether or not the coverage surpassed the `branch` threshold.
2821+
* `line` {boolean} Whether or not the coverage surpassed the `line` threshold.
28172822
* `totals` {Object} An object containing a summary of coverage for all
28182823
files.
28192824
* `totalLineCount` {number} The total number of lines.

lib/internal/main/test_runner.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@ if (isUsingInspector() && options.isolation === 'process') {
3131
options.globPatterns = ArrayPrototypeSlice(process.argv, 1);
3232

3333
debug('test runner configuration:', options);
34-
run(options).on('test:fail', (data) => {
34+
const ran = run(options);
35+
ran.on('test:fail', (data) => {
3536
if (data.todo === undefined || data.todo === false) {
3637
process.exitCode = kGenericUserError;
3738
}
3839
});
40+
41+
ran.on('test:coverage', ({ summary: { thresholds: { line, branch, function: func } } }) => {
42+
if (!line || !branch || !func) process.exitCode = kGenericUserError;
43+
});

lib/internal/test_runner/harness.js

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ function lazyBootstrapRoot() {
250250
process.exitCode = kGenericUserError;
251251
}
252252
});
253+
// globalRoot.reporter.on('test:coverage', ({ summary: { thresholds: { line, branch, function: func }}}) => {
254+
// if (!line || !branch || !func) process.exitCode = kGenericUserError;
255+
// });
253256
globalRoot.harness.bootstrapPromise = globalOptions.setup(globalRoot.reporter);
254257
}
255258
return globalRoot;

lib/internal/test_runner/test.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ const {
2929
SymbolDispose,
3030
} = primordials;
3131
const { getCallerLocation } = internalBinding('util');
32-
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
3332
const { addAbortListener } = require('internal/events/abort_listener');
3433
const { queueMicrotask } = require('internal/process/task_queues');
3534
const { AsyncResource } = require('async_hooks');
@@ -1010,8 +1009,12 @@ class Test extends AsyncResource {
10101009
reporter.diagnostic(nesting, loc, `duration_ms ${this.duration()}`);
10111010

10121011
if (coverage) {
1013-
reporter.coverage(nesting, loc, coverage);
1014-
1012+
coverage.thresholds = {
1013+
__proto__: null,
1014+
line: true,
1015+
branch: true,
1016+
function: true,
1017+
};
10151018
const coverages = [
10161019
{ __proto__: null, actual: coverage.totals.coveredLinePercent,
10171020
threshold: this.config.lineCoverage, name: 'line' },
@@ -1026,10 +1029,12 @@ class Test extends AsyncResource {
10261029
for (let i = 0; i < coverages.length; i++) {
10271030
const { threshold, actual, name } = coverages[i];
10281031
if (actual < threshold) {
1029-
process.exitCode = kGenericUserError;
1032+
coverage.thresholds[name] = false;
10301033
reporter.diagnostic(nesting, loc, `Error: ${NumberPrototypeToFixed(actual, 2)}% ${name} coverage does not meet threshold of ${threshold}%.`);
10311034
}
10321035
}
1036+
1037+
reporter.coverage(nesting, loc, coverage);
10331038
}
10341039

10351040
if (harness.watching) {

test/parallel/test-runner-coverage-thresholds.js

+45-12
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ function getTapCoverageFixtureReport() {
4444
}
4545

4646
const fixture = fixtures.path('test-runner', 'coverage.js');
47-
const neededArguments = [
48-
'--experimental-test-coverage',
49-
'--test-reporter', 'tap',
50-
];
47+
const reporter = fixtures.fileURL('test-runner/custom_reporters/coverage.mjs');
5148

5249
const coverages = [
5350
{ flag: '--test-coverage-lines', name: 'line', actual: 78.35 },
@@ -56,10 +53,12 @@ const coverages = [
5653
];
5754

5855
for (const coverage of coverages) {
59-
test(`test passing ${coverage.flag}`, async (t) => {
56+
test(`test passing ${coverage.flag}`, () => {
6057
const result = spawnSync(process.execPath, [
61-
...neededArguments,
58+
'--test',
59+
'--experimental-test-coverage',
6260
`${coverage.flag}=25`,
61+
'--test-reporter', 'tap',
6362
fixture,
6463
]);
6564

@@ -70,10 +69,27 @@ for (const coverage of coverages) {
7069
assert(!findCoverageFileForPid(result.pid));
7170
});
7271

73-
test(`test failing ${coverage.flag}`, async (t) => {
72+
test(`test passing ${coverage.flag} with custom reporter`, () => {
73+
const result = spawnSync(process.execPath, [
74+
'--test',
75+
'--experimental-test-coverage',
76+
`${coverage.flag}=25`,
77+
'--test-reporter', reporter,
78+
fixture,
79+
]);
80+
81+
const stdout = JSON.parse(result.stdout.toString());
82+
assert(stdout.summary.thresholds[coverage.name]);
83+
assert.strictEqual(result.status, 0);
84+
assert(!findCoverageFileForPid(result.pid));
85+
});
86+
87+
test(`test failing ${coverage.flag}`, () => {
7488
const result = spawnSync(process.execPath, [
75-
...neededArguments,
89+
'--test',
90+
'--experimental-test-coverage',
7691
`${coverage.flag}=99`,
92+
'--test-reporter', 'tap',
7793
fixture,
7894
]);
7995

@@ -84,9 +100,25 @@ for (const coverage of coverages) {
84100
assert(!findCoverageFileForPid(result.pid));
85101
});
86102

87-
test(`test out-of-range ${coverage.flag} (too high)`, async (t) => {
103+
test(`test failing ${coverage.flag} with custom reporter`, () => {
104+
const result = spawnSync(process.execPath, [
105+
'--test',
106+
'--experimental-test-coverage',
107+
`${coverage.flag}=99`,
108+
'--test-reporter', reporter,
109+
fixture,
110+
]);
111+
112+
const stdout = JSON.parse(result.stdout.toString());
113+
assert(!stdout.summary.thresholds[coverage.name]);
114+
assert.strictEqual(result.status, 1);
115+
assert(!findCoverageFileForPid(result.pid));
116+
});
117+
118+
test(`test out-of-range ${coverage.flag} (too high)`, () => {
88119
const result = spawnSync(process.execPath, [
89-
...neededArguments,
120+
'--test',
121+
'--experimental-test-coverage',
90122
`${coverage.flag}=101`,
91123
fixture,
92124
]);
@@ -96,9 +128,10 @@ for (const coverage of coverages) {
96128
assert(!findCoverageFileForPid(result.pid));
97129
});
98130

99-
test(`test out-of-range ${coverage.flag} (too low)`, async (t) => {
131+
test(`test out-of-range ${coverage.flag} (too low)`, () => {
100132
const result = spawnSync(process.execPath, [
101-
...neededArguments,
133+
'--test',
134+
'--experimental-test-coverage',
102135
`${coverage.flag}=-1`,
103136
fixture,
104137
]);

test/parallel/test-runner-coverage.js

+6
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ test('coverage reports on lines, functions, and branches', skipIfNoInspector, as
253253
const calledTwice = file.functions.find((f) => f.name === 'fnWithControlFlow');
254254
assert.strictEqual(calledTwice.count, 2);
255255
assert.strictEqual(calledTwice.line, 35);
256+
257+
assert.ok(coverage.summary.thresholds.function);
256258
});
257259

258260
await t.test('reports on branch coverage', () => {
@@ -261,6 +263,8 @@ test('coverage reports on lines, functions, and branches', skipIfNoInspector, as
261263

262264
const calledTwice = file.branches.find((b) => b.line === 35);
263265
assert.strictEqual(calledTwice.count, 2);
266+
267+
assert.ok(coverage.summary.thresholds.branch);
264268
});
265269

266270
await t.test('reports on line coverage', () => {
@@ -278,6 +282,8 @@ test('coverage reports on lines, functions, and branches', skipIfNoInspector, as
278282
const testLine = file.lines.find((l) => l.line === line.line);
279283
assert.strictEqual(testLine.count, line.count);
280284
});
285+
286+
assert.ok(coverage.summary.thresholds.line);
281287
});
282288
});
283289

0 commit comments

Comments
 (0)