diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 50f9f94172ef4d..427a467ba0072b 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -177,7 +177,7 @@ class TestCoverage { if (isBlockCoverage) { ArrayPrototypePush(branchReports, { __proto__: null, - line: range.lines[0].line, + line: range.lines[0]?.line, count: range.count, }); @@ -197,7 +197,7 @@ class TestCoverage { __proto__: null, name: functions[j].functionName, count: maxCountPerFunction, - line: range.lines[0].line, + line: range.lines[0]?.line, }); if (range.count !== 0 || range.ignoredLines === range.lines.length) { diff --git a/test/fixtures/test-runner/coverage-loader/hooks.mjs b/test/fixtures/test-runner/coverage-loader/hooks.mjs new file mode 100644 index 00000000000000..1aa04d0b45589d --- /dev/null +++ b/test/fixtures/test-runner/coverage-loader/hooks.mjs @@ -0,0 +1,11 @@ +const source = ` + import { test } from 'node:test'; + test('test', async () => {}); +`; + +export async function load(url, context, nextLoad) { + if (url.endsWith('virtual.js')) { + return { format: "module", source, shortCircuit: true }; + } + return nextLoad(url, context); +} diff --git a/test/fixtures/test-runner/coverage-loader/register-hooks.js b/test/fixtures/test-runner/coverage-loader/register-hooks.js new file mode 100644 index 00000000000000..cbbac61264112b --- /dev/null +++ b/test/fixtures/test-runner/coverage-loader/register-hooks.js @@ -0,0 +1,4 @@ +const { register } = require('node:module'); +const { pathToFileURL } = require('node:url'); + +register('./hooks.mjs', pathToFileURL(__filename)); diff --git a/test/fixtures/test-runner/coverage-loader/virtual.js b/test/fixtures/test-runner/coverage-loader/virtual.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js index c059e0ed8df936..05057bf40838e8 100644 --- a/test/parallel/test-runner-coverage.js +++ b/test/parallel/test-runner-coverage.js @@ -273,3 +273,33 @@ test('coverage with source maps', skipIfNoInspector, () => { assert(result.stdout.toString().includes(report)); assert.strictEqual(result.status, 1); }); + +test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => { + let report = [ + '# start of coverage report', + '# ------------------------------------------------------------------', + '# file | line % | branch % | funcs % | uncovered lines', + '# ------------------------------------------------------------------', + '# hooks.mjs | 100.00 | 100.00 | 100.00 | ', + '# register-hooks.js | 100.00 | 100.00 | 100.00 | ', + '# virtual.js | 100.00 | 100.00 | 100.00 | ', + '# ------------------------------------------------------------------', + '# all files | 100.00 | 100.00 | 100.00 |', + '# ------------------------------------------------------------------', + '# end of coverage report', + ].join('\n'); + + if (common.isWindows) { + report = report.replaceAll('/', '\\'); + } + + const fixture = fixtures.path('test-runner', 'coverage-loader'); + const args = [ + '--import', './register-hooks.js', '--test', '--experimental-test-coverage', '--test-reporter', 'tap', 'virtual.js', + ]; + const result = spawnSync(process.execPath, args, { cwd: fixture }); + + assert.strictEqual(result.stderr.toString(), ''); + assert(result.stdout.toString().includes(report)); + assert.strictEqual(result.status, 0); +});