diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 42fa2b87b778f1..d8b4cfd8b5e7e0 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -408,6 +408,10 @@ class TestCoverage { } const mappedStartOffset = this.entryToOffset(startEntry, mappedLines); const mappedEndOffset = this.entryToOffset(endEntry, mappedLines) + 1; + if (mappedStartOffset < 0 || mappedEndOffset < 1) { + // The range is not mappable. Skip it. + continue; + } for (let l = startEntry.originalLine; l <= endEntry.originalLine; l++) { mappedLines[l].count = count; } @@ -432,7 +436,12 @@ class TestCoverage { entryToOffset(entry, lines) { const line = MathMax(entry.originalLine, 0); - return MathMin(lines[line].startOffset + entry.originalColumn, lines[line].endOffset); + const mappedLine = lines[line]; + if (!mappedLine) { + // Return -1 if the line is not mappable. + return -1; + } + return MathMin(mappedLine.startOffset + entry.originalColumn, mappedLine.endOffset); } mergeCoverage(merged, coverage) { diff --git a/test/fixtures/test-runner/coverage/bar.mts b/test/fixtures/test-runner/coverage/bar.mts new file mode 100644 index 00000000000000..19251773e5ead3 --- /dev/null +++ b/test/fixtures/test-runner/coverage/bar.mts @@ -0,0 +1,3 @@ +export default function bar() { + return 'original bar'; +} diff --git a/test/fixtures/test-runner/coverage/foo.mts b/test/fixtures/test-runner/coverage/foo.mts new file mode 100644 index 00000000000000..a66961a27a9c26 --- /dev/null +++ b/test/fixtures/test-runner/coverage/foo.mts @@ -0,0 +1,3 @@ +import bar from './bar.mts'; + +export const foo = () => bar(); diff --git a/test/fixtures/test-runner/output/typescript-coverage.mts b/test/fixtures/test-runner/output/typescript-coverage.mts new file mode 100644 index 00000000000000..c26ddcac9b33f9 --- /dev/null +++ b/test/fixtures/test-runner/output/typescript-coverage.mts @@ -0,0 +1,25 @@ +import assert from 'node:assert/strict'; +import { before, describe, it, mock } from 'node:test'; + +describe('foo', { concurrency: true }, () => { + let barMock = mock.fn(); + let foo; + + before(async () => { + const barNamedExports = await import('../coverage/bar.mts') + .then(({ default: _, ...rest }) => rest); + + mock.module('../coverage/bar.mts', { + defaultExport: barMock, + namedExports: barNamedExports, + }); + + ({ foo } = await import('../coverage/foo.mts')); + }); + + it('should do the thing', () => { + barMock.mock.mockImplementationOnce(() => 42); + + assert.equal(foo(), 42); + }); +}); diff --git a/test/fixtures/test-runner/output/typescript-coverage.snapshot b/test/fixtures/test-runner/output/typescript-coverage.snapshot new file mode 100644 index 00000000000000..eaeba2bfa11b12 --- /dev/null +++ b/test/fixtures/test-runner/output/typescript-coverage.snapshot @@ -0,0 +1,39 @@ +TAP version 13 +# Subtest: foo + # Subtest: should do the thing + ok 1 - should do the thing + --- + duration_ms: * + type: 'test' + ... + 1..1 +ok 1 - foo + --- + duration_ms: * + type: 'suite' + ... +1..1 +# tests 1 +# suites 1 +# pass 1 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms * +# start of coverage report +# ---------------------------------------------------------------------------- +# file | line % | branch % | funcs % | uncovered lines +# ---------------------------------------------------------------------------- +# test | | | | +# fixtures | | | | +# test-runner | | | | +# coverage | | | | +# bar.mts | 0.00 | 100.00 | 100.00 | 1-3 +# foo.mts | 100.00 | 100.00 | 100.00 | +# output | | | | +# typescript-coverage.mts | 100.00 | 100.00 | 100.00 | +# ---------------------------------------------------------------------------- +# all files | 85.29 | 100.00 | 85.71 | +# ---------------------------------------------------------------------------- +# end of coverage report diff --git a/test/parallel/test-runner-output.mjs b/test/parallel/test-runner-output.mjs index 4e04a71193e78a..081d4673ed313d 100644 --- a/test/parallel/test-runner-output.mjs +++ b/test/parallel/test-runner-output.mjs @@ -313,6 +313,15 @@ const tests = [ flags: ['--test-reporter=tap', '--test-coverage-exclude=../output/**'], cwd: fixtures.path('test-runner/coverage-snap'), } : false, + process.features.inspector ? { + name: 'test-runner/output/typescript-coverage.mts', + flags: ['--disable-warning=ExperimentalWarning', + '--test-reporter=tap', + '--experimental-transform-types', + '--experimental-test-module-mocks', + '--experimental-test-coverage', + '--test-coverage-exclude=!test/**'] + } : false, ] .filter(Boolean) .map(({ flags, name, tty, transform, cwd }) => ({