• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3const assert = require('node:assert');
4const { spawnSync } = require('node:child_process');
5const { readdirSync } = require('node:fs');
6const { test } = require('node:test');
7const fixtures = require('../common/fixtures');
8const tmpdir = require('../common/tmpdir');
9const skipIfNoInspector = {
10  skip: !process.features.inspector ? 'inspector disabled' : false
11};
12
13tmpdir.refresh();
14
15function findCoverageFileForPid(pid) {
16  const pattern = `^coverage\\-${pid}\\-(\\d{13})\\-(\\d+)\\.json$`;
17  const regex = new RegExp(pattern);
18
19  return readdirSync(tmpdir.path).find((file) => {
20    return regex.test(file);
21  });
22}
23
24function getTapCoverageFixtureReport() {
25  const report = [
26    '# start of coverage report',
27    '# file | line % | branch % | funcs % | uncovered lines',
28    '# test/fixtures/test-runner/coverage.js | 78.65 | 38.46 | 60.00 | 12, ' +
29    '13, 16, 17, 18, 19, 20, 21, 22, 27, 39, 43, 44, 61, 62, 66, 67, 71, 72',
30    '# test/fixtures/test-runner/invalid-tap.js | 100.00 | 100.00 | 100.00 | ',
31    '# test/fixtures/v8-coverage/throw.js | 71.43 | 50.00 | 100.00 | 5, 6',
32    '# all files | 78.35 | 43.75 | 60.00 |',
33    '# end of coverage report',
34  ].join('\n');
35
36  if (common.isWindows) {
37    return report.replaceAll('/', '\\');
38  }
39
40  return report;
41}
42
43function getSpecCoverageFixtureReport() {
44  /* eslint-disable max-len */
45  const report = [
46    '\u2139 start of coverage report',
47    '\u2139 -------------------------------------------------------------------------------------------------------------------',
48    '\u2139 file                                     | line % | branch % | funcs % | uncovered lines',
49    '\u2139 -------------------------------------------------------------------------------------------------------------------',
50    '\u2139 test/fixtures/test-runner/coverage.js    |  78.65 |    38.46 |   60.00 | 12-13 16-22 27 39 43-44 61-62 66-67 71-72',
51    '\u2139 test/fixtures/test-runner/invalid-tap.js | 100.00 |   100.00 |  100.00 | ',
52    '\u2139 test/fixtures/v8-coverage/throw.js       |  71.43 |    50.00 |  100.00 | 5-6',
53    '\u2139 -------------------------------------------------------------------------------------------------------------------',
54    '\u2139 all files                                |  78.35 |    43.75 |   60.00 |',
55    '\u2139 -------------------------------------------------------------------------------------------------------------------',
56    '\u2139 end of coverage report',
57  ].join('\n');
58  /* eslint-enable max-len */
59
60  if (common.isWindows) {
61    return report.replaceAll('/', '\\');
62  }
63
64  return report;
65}
66
67test('test coverage report', async (t) => {
68  await t.test('handles the inspector not being available', (t) => {
69    if (process.features.inspector) {
70      return;
71    }
72
73    const fixture = fixtures.path('test-runner', 'coverage.js');
74    const args = ['--experimental-test-coverage', fixture];
75    const result = spawnSync(process.execPath, args);
76
77    assert(!result.stdout.toString().includes('# start of coverage report'));
78    assert(result.stderr.toString().includes('coverage could not be collected'));
79    assert.strictEqual(result.status, 0);
80    assert(!findCoverageFileForPid(result.pid));
81  });
82});
83
84test('test tap coverage reporter', skipIfNoInspector, async (t) => {
85  await t.test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => {
86    const fixture = fixtures.path('test-runner', 'coverage.js');
87    const args = ['--experimental-test-coverage', '--test-reporter', 'tap', fixture];
88    const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } };
89    const result = spawnSync(process.execPath, args, options);
90    const report = getTapCoverageFixtureReport();
91
92    assert(result.stdout.toString().includes(report));
93    assert.strictEqual(result.stderr.toString(), '');
94    assert.strictEqual(result.status, 0);
95    assert(findCoverageFileForPid(result.pid));
96  });
97
98  await t.test('coverage is reported without NODE_V8_COVERAGE present', (t) => {
99    const fixture = fixtures.path('test-runner', 'coverage.js');
100    const args = ['--experimental-test-coverage', '--test-reporter', 'tap', fixture];
101    const result = spawnSync(process.execPath, args);
102    const report = getTapCoverageFixtureReport();
103
104    assert(result.stdout.toString().includes(report));
105    assert.strictEqual(result.stderr.toString(), '');
106    assert.strictEqual(result.status, 0);
107    assert(!findCoverageFileForPid(result.pid));
108  });
109});
110
111test('test spec coverage reporter', skipIfNoInspector, async (t) => {
112  await t.test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => {
113    const fixture = fixtures.path('test-runner', 'coverage.js');
114    const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture];
115    const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } };
116    const result = spawnSync(process.execPath, args, options);
117    const report = getSpecCoverageFixtureReport();
118
119    assert(result.stdout.toString().includes(report));
120    assert.strictEqual(result.stderr.toString(), '');
121    assert.strictEqual(result.status, 0);
122    assert(findCoverageFileForPid(result.pid));
123  });
124
125  await t.test('coverage is reported without NODE_V8_COVERAGE present', (t) => {
126    const fixture = fixtures.path('test-runner', 'coverage.js');
127    const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture];
128    const result = spawnSync(process.execPath, args);
129    const report = getSpecCoverageFixtureReport();
130
131    assert(result.stdout.toString().includes(report));
132    assert.strictEqual(result.stderr.toString(), '');
133    assert.strictEqual(result.status, 0);
134    assert(!findCoverageFileForPid(result.pid));
135  });
136});
137
138test('single process coverage is the same with --test', skipIfNoInspector, () => {
139  const fixture = fixtures.path('test-runner', 'coverage.js');
140  const args = [
141    '--test', '--experimental-test-coverage', '--test-reporter', 'tap', fixture,
142  ];
143  const result = spawnSync(process.execPath, args);
144  const report = getTapCoverageFixtureReport();
145
146  assert.strictEqual(result.stderr.toString(), '');
147  assert(result.stdout.toString().includes(report));
148  assert.strictEqual(result.status, 0);
149  assert(!findCoverageFileForPid(result.pid));
150});
151
152test('coverage is combined for multiple processes', skipIfNoInspector, () => {
153  let report = [
154    '# start of coverage report',
155    '# file | line % | branch % | funcs % | uncovered lines',
156    '# test/fixtures/v8-coverage/combined_coverage/common.js | 89.86 | ' +
157    '62.50 | 100.00 | 8, 13, 14, 18, 34, 35, 53',
158    '# test/fixtures/v8-coverage/combined_coverage/first.test.js | 83.33 | ' +
159    '100.00 | 50.00 | 5, 6',
160    '# test/fixtures/v8-coverage/combined_coverage/second.test.js | 100.00 ' +
161    '| 100.00 | 100.00 | ',
162    '# test/fixtures/v8-coverage/combined_coverage/third.test.js | 100.00 | ' +
163    '100.00 | 100.00 | ',
164    '# all files | 92.11 | 72.73 | 88.89 |',
165    '# end of coverage report',
166  ].join('\n');
167
168  if (common.isWindows) {
169    report = report.replaceAll('/', '\\');
170  }
171
172  const fixture = fixtures.path('v8-coverage', 'combined_coverage');
173  const args = [
174    '--test', '--experimental-test-coverage', '--test-reporter', 'tap', fixture,
175  ];
176  const result = spawnSync(process.execPath, args, {
177    env: { ...process.env, NODE_TEST_TMPDIR: tmpdir.path }
178  });
179
180  assert.strictEqual(result.stderr.toString(), '');
181  assert(result.stdout.toString().includes(report));
182  assert.strictEqual(result.status, 0);
183});
184