diff --git a/doc/api/errors.md b/doc/api/errors.md index 78456d0d28e0f0..71671c33be47f0 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2573,6 +2573,12 @@ disconnected socket. A call was made and the UDP subsystem was not running. + + +### `ERR_SOURCE_MAP_MISSING_SOURCE` + +A file imported from a source map was not found. + ### `ERR_SQLITE_ERROR` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 87f7b1da094d93..a3275c4bcb50ac 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1704,6 +1704,7 @@ E('ERR_SOCKET_CONNECTION_TIMEOUT', E('ERR_SOCKET_DGRAM_IS_CONNECTED', 'Already connected', Error); E('ERR_SOCKET_DGRAM_NOT_CONNECTED', 'Not connected', Error); E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error); +E('ERR_SOURCE_MAP_MISSING_SOURCE', `Cannot find '%s' imported from the source map for '%s'`, Error); E('ERR_SRI_PARSE', 'Subresource Integrity string %j had an unexpected %j at position %d', SyntaxError); diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 7ef57020728302..1b5ba7912dcb7d 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -30,6 +30,11 @@ const { tmpdir } = require('os'); const { join, resolve, relative, matchesGlob } = require('path'); const { fileURLToPath } = require('internal/url'); const { kMappings, SourceMap } = require('internal/source_map/source_map'); +const { + codes: { + ERR_SOURCE_MAP_MISSING_SOURCE, + }, +} = require('internal/errors'); const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/; const kIgnoreRegex = /\/\* node:coverage ignore next (?\d+ )?\*\//; const kLineEndingRegex = /\r?\n$/u; @@ -390,6 +395,9 @@ class TestCoverage { newUrl ??= startEntry?.originalSource; const mappedLines = this.getLines(newUrl); + if (!mappedLines) { + throw new ERR_SOURCE_MAP_MISSING_SOURCE(newUrl, url); + } const mappedStartOffset = this.entryToOffset(startEntry, mappedLines); const mappedEndOffset = this.entryToOffset(endEntry, mappedLines) + 1; for (let l = startEntry.originalLine; l <= endEntry.originalLine; l++) { diff --git a/test/fixtures/test-runner/source-map-missing-sources/index.js b/test/fixtures/test-runner/source-map-missing-sources/index.js new file mode 100644 index 00000000000000..f8a711af8c0098 --- /dev/null +++ b/test/fixtures/test-runner/source-map-missing-sources/index.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/test/fixtures/test-runner/source-map-missing-sources/index.js.map b/test/fixtures/test-runner/source-map-missing-sources/index.js.map new file mode 100644 index 00000000000000..12cacc226ac5c1 --- /dev/null +++ b/test/fixtures/test-runner/source-map-missing-sources/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js.map","sourceRoot":"","sources":["nonexistent.js"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC"} \ No newline at end of file diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js index ba767283e672c4..77e0afadbdff95 100644 --- a/test/parallel/test-runner-coverage.js +++ b/test/parallel/test-runner-coverage.js @@ -6,6 +6,7 @@ const { readdirSync } = require('node:fs'); const { test } = require('node:test'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { pathToFileURL } = require('node:url'); const skipIfNoInspector = { skip: !process.features.inspector ? 'inspector disabled' : false }; @@ -320,6 +321,20 @@ test('coverage with source maps', skipIfNoInspector, () => { assert.strictEqual(result.status, 1); }); +test('coverage with source maps missing sources', skipIfNoInspector, () => { + const file = fixtures.path('test-runner', 'source-map-missing-sources', 'index.js'); + const missing = fixtures.path('test-runner', 'source-map-missing-sources', 'nonexistent.js'); + const result = spawnSync(process.execPath, [ + '--test', + '--experimental-test-coverage', + file, + ]); + + const error = `Cannot find '${pathToFileURL(missing)}' imported from the source map for '${pathToFileURL(file)}'`; + assert(result.stdout.toString().includes(error)); + assert.strictEqual(result.status, 1); +}); + test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => { let report = [ '# start of coverage report',