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