Skip to content

Commit 896ebe4

Browse files
committed
test_runner: added coverage support with watch mode
1 parent 89ddc98 commit 896ebe4

File tree

4 files changed

+98
-6
lines changed

4 files changed

+98
-6
lines changed

lib/internal/main/test_runner.js

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const options = {
6969
setup: setupTestReporters,
7070
timeout,
7171
shard,
72+
coverage: getOptionValue('--experimental-test-coverage'),
7273
};
7374
debug('test runner configuration:', options);
7475
run(options).on('test:fail', (data) => {

lib/internal/test_runner/runner.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ function filterExecArgv(arg, i, arr) {
112112
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
113113
}
114114

115-
function getRunArgs(path, { inspectPort, testNamePatterns, only }) {
115+
function getRunArgs(path, { inspectPort, testNamePatterns, only, coverage }) {
116116
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
117117
if (isUsingInspector()) {
118118
ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
@@ -123,6 +123,9 @@ function getRunArgs(path, { inspectPort, testNamePatterns, only }) {
123123
if (only === true) {
124124
ArrayPrototypePush(argv, '--test-only');
125125
}
126+
if (coverage === true) {
127+
ArrayPrototypePush(argv, '--experimental-test-coverage');
128+
}
126129
ArrayPrototypePush(argv, path);
127130

128131
return argv;
@@ -440,7 +443,7 @@ function run(options = kEmptyObject) {
440443
validateObject(options, 'options');
441444

442445
let { testNamePatterns, shard } = options;
443-
const { concurrency, timeout, signal, files, inspectPort, watch, setup, only } = options;
446+
const { concurrency, timeout, signal, files, inspectPort, watch, setup, only, coverage } = options;
444447

445448
if (files != null) {
446449
validateArray(files, 'options.files');
@@ -503,8 +506,8 @@ function run(options = kEmptyObject) {
503506
let filesWatcher;
504507
const opts = { __proto__: null, root, signal, inspectPort, testNamePatterns, only };
505508
if (watch) {
506-
filesWatcher = watchFiles(testFiles, opts);
507-
postRun = undefined;
509+
filesWatcher = watchFiles(testFiles, { __proto__: null, ...opts, coverage });
510+
postRun = () => root.postRun(null, false);
508511
}
509512
const runFiles = () => {
510513
root.harness.bootstrapComplete = true;

lib/internal/test_runner/test.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ class Test extends AsyncResource {
674674
}
675675
}
676676

677-
postRun(pendingSubtestsError) {
677+
postRun(pendingSubtestsError, endReporter = true) {
678678
// If the test was failed before it even started, then the end time will
679679
// be earlier than the start time. Correct that here.
680680
if (this.endTime < this.startTime) {
@@ -756,7 +756,9 @@ class Test extends AsyncResource {
756756
reporter.coverage(nesting, loc, coverage);
757757
}
758758

759-
reporter.end();
759+
if (endReporter === true) {
760+
reporter.end();
761+
}
760762
}
761763
}
762764

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Flags: --expose-internals
2+
import * as common from '../common/index.mjs';
3+
import { describe, it } from 'node:test';
4+
import { spawn } from 'node:child_process';
5+
import { writeFileSync } from 'node:fs';
6+
import util from 'internal/util';
7+
import tmpdir from '../common/tmpdir.js';
8+
import assert from 'node:assert';
9+
10+
11+
if (common.isIBMi)
12+
common.skip('IBMi does not support `fs.watch()`');
13+
14+
tmpdir.refresh();
15+
16+
function getCoverageFixtureReport() {
17+
18+
const report = [
19+
'# start of coverage report',
20+
'# ---------------------------------------------------------------',
21+
'# file | line % | branch % | funcs % | uncovered lines',
22+
'# ---------------------------------------------------------------',
23+
'# dependency.js | 100.00 | 100.00 | 100.00 | ',
24+
'# dependency.mjs | 100.00 | 100.00 | 100.00 | ',
25+
'# test.js | 100.00 | 100.00 | 100.00 | ',
26+
'# ---------------------------------------------------------------',
27+
'# all files | 100.00 | 100.00 | 100.00 |',
28+
'# ---------------------------------------------------------------',
29+
'# end of coverage report',
30+
].join('\n');
31+
32+
33+
if (common.isWindows) {
34+
return report.replaceAll('/', '\\');
35+
}
36+
37+
return report;
38+
}
39+
40+
const fixtureContent = {
41+
'dependency.js': 'module.exports = {};',
42+
'dependency.mjs': 'export const a = 1;',
43+
'test.js': `
44+
const test = require('node:test');
45+
require('./dependency.js');
46+
import('./dependency.mjs');
47+
import('data:text/javascript,');
48+
test('test has ran');`,
49+
};
50+
const fixturePaths = Object.keys(fixtureContent)
51+
.reduce((acc, file) => ({ ...acc, [file]: tmpdir.resolve(file) }), {});
52+
Object.entries(fixtureContent)
53+
.forEach(([file, content]) => writeFileSync(fixturePaths[file], content));
54+
55+
async function testWatch({ fileToUpdate, file }) {
56+
const ran1 = util.createDeferredPromise();
57+
const ran2 = util.createDeferredPromise();
58+
const child = spawn(process.execPath,
59+
['--watch', '--test', '--experimental-test-coverage',
60+
file ? fixturePaths[file] : undefined].filter(Boolean),
61+
{ encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path });
62+
let stdout = '';
63+
64+
child.stdout.on('data', (data) => {
65+
stdout += data.toString();
66+
const testRuns = stdout.match(/ - test has ran/g);
67+
if (testRuns?.length >= 1) ran1.resolve();
68+
if (testRuns?.length >= 2) ran2.resolve();
69+
});
70+
71+
await ran1.promise;
72+
const content = fixtureContent[fileToUpdate];
73+
const path = fixturePaths[fileToUpdate];
74+
const interval = setInterval(() => writeFileSync(path, content), common.platformTimeout(1000));
75+
await ran2.promise;
76+
clearInterval(interval);
77+
child.kill();
78+
return stdout;
79+
}
80+
81+
describe('test runner watch mode with coverage report', () => {
82+
it('should support running test file', async () => {
83+
const stdout = await testWatch({ file: 'test.js', fileToUpdate: 'test.js' });
84+
assert(stdout.includes(getCoverageFixtureReport()));
85+
});
86+
});

0 commit comments

Comments
 (0)