lib: fix sourcemaps with ts module mocking

PR-URL: https://github.com/nodejs/node/pull/58193
Fixes: https://github.com/nodejs/node/issues/58119
Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
This commit is contained in:
Marco Ippolito
2025-05-08 17:14:22 +02:00
committed by GitHub
parent e38ce27f3c
commit 8ccfcb5adc
6 changed files with 89 additions and 1 deletions

View File

@@ -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) {

View File

@@ -0,0 +1,3 @@
export default function bar() {
return 'original bar';
}

View File

@@ -0,0 +1,3 @@
import bar from './bar.mts';
export const foo = () => bar();

View File

@@ -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);
});
});

View File

@@ -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

View File

@@ -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 }) => ({