From 70bf2690ae77d4bc4d908fa65d74fffba4c04ccf Mon Sep 17 00:00:00 2001 From: cjihrig Date: Thu, 7 Mar 2024 21:19:37 -0500 Subject: [PATCH] test_runner: support source mapped test locations This commit adds support for source mapping test locations when the --enable-source-maps flag is present. Fixes: https://github.com/nodejs/node/issues/51392 PR-URL: https://github.com/nodejs/node/pull/52010 Fixes: https://github.com/nodejs/node/issues/51610 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Marco Ippolito Reviewed-By: Chengzhong Wu Reviewed-By: Chemi Atlow Reviewed-By: Moshe Atlow --- lib/internal/test_runner/test.js | 22 ++++++++++++- lib/internal/test_runner/utils.js | 2 ++ .../output/source_mapped_locations.mjs | 7 ++++ .../output/source_mapped_locations.mjs.map | 1 + .../output/source_mapped_locations.snapshot | 33 +++++++++++++++++++ .../output/source_mapped_locations.ts | 7 ++++ test/parallel/test-runner-output.mjs | 1 + 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/test-runner/output/source_mapped_locations.mjs create mode 100644 test/fixtures/test-runner/output/source_mapped_locations.mjs.map create mode 100644 test/fixtures/test-runner/output/source_mapped_locations.snapshot create mode 100644 test/fixtures/test-runner/output/source_mapped_locations.ts diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 7adf9c9db266e0..635520783dad9a 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -76,8 +76,17 @@ const kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']); const kUnwrapErrors = new SafeSet() .add(kTestCodeFailure).add(kHookFailure) .add('uncaughtException').add('unhandledRejection'); -const { testNamePatterns, testOnlyFlag } = parseCommandLine(); +const { sourceMaps, testNamePatterns, testOnlyFlag } = parseCommandLine(); let kResistStopPropagation; +let findSourceMap; + +function lazyFindSourceMap(file) { + if (findSourceMap === undefined) { + ({ findSourceMap } = require('internal/source_map/source_map_cache')); + } + + return findSourceMap(file); +} function stopTest(timeout, signal) { const deferred = createDeferredPromise(); @@ -363,6 +372,17 @@ class Test extends AsyncResource { column: loc[1], file: loc[2], }; + + if (sourceMaps === true) { + const map = lazyFindSourceMap(this.loc.file); + const entry = map?.findEntry(this.loc.line - 1, this.loc.column - 1); + + if (entry !== undefined) { + this.loc.line = entry.originalLine + 1; + this.loc.column = entry.originalColumn + 1; + this.loc.file = entry.originalSource; + } + } } } diff --git a/lib/internal/test_runner/utils.js b/lib/internal/test_runner/utils.js index 0a3fd9cad83edd..fda9f4334aa64e 100644 --- a/lib/internal/test_runner/utils.js +++ b/lib/internal/test_runner/utils.js @@ -193,6 +193,7 @@ function parseCommandLine() { const isTestRunner = getOptionValue('--test'); const coverage = getOptionValue('--experimental-test-coverage'); + const sourceMaps = getOptionValue('--enable-source-maps'); const isChildProcess = process.env.NODE_TEST_CONTEXT === 'child'; const isChildProcessV8 = process.env.NODE_TEST_CONTEXT === 'child-v8'; let destinations; @@ -244,6 +245,7 @@ function parseCommandLine() { __proto__: null, isTestRunner, coverage, + sourceMaps, testOnlyFlag, testNamePatterns, reporters, diff --git a/test/fixtures/test-runner/output/source_mapped_locations.mjs b/test/fixtures/test-runner/output/source_mapped_locations.mjs new file mode 100644 index 00000000000000..82e33499284171 --- /dev/null +++ b/test/fixtures/test-runner/output/source_mapped_locations.mjs @@ -0,0 +1,7 @@ +// Flags: --enable-source-maps +import { test } from 'node:test'; +import { strictEqual } from 'node:assert'; +test('fails', () => { + strictEqual(1, 2); +}); +//# sourceMappingURL=source_mapped_locations.mjs.map diff --git a/test/fixtures/test-runner/output/source_mapped_locations.mjs.map b/test/fixtures/test-runner/output/source_mapped_locations.mjs.map new file mode 100644 index 00000000000000..991dd95820c4ce --- /dev/null +++ b/test/fixtures/test-runner/output/source_mapped_locations.mjs.map @@ -0,0 +1 @@ +{"version":3,"file":"source_mapped_locations.mjs","sourceRoot":"","sources":["source_mapped_locations.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;IACjB,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"} diff --git a/test/fixtures/test-runner/output/source_mapped_locations.snapshot b/test/fixtures/test-runner/output/source_mapped_locations.snapshot new file mode 100644 index 00000000000000..c7baee728d0218 --- /dev/null +++ b/test/fixtures/test-runner/output/source_mapped_locations.snapshot @@ -0,0 +1,33 @@ +TAP version 13 +# Subtest: fails +not ok 1 - fails + --- + duration_ms: * + location: 'file:///test/fixtures/test-runner/output/source_mapped_locations.ts:5:1' + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly equal: + + 1 !== 2 + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: 2 + actual: 1 + operator: 'strictEqual' + stack: |- + * + * + * + * + * + ... +1..1 +# tests 1 +# suites 0 +# pass 0 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms * diff --git a/test/fixtures/test-runner/output/source_mapped_locations.ts b/test/fixtures/test-runner/output/source_mapped_locations.ts new file mode 100644 index 00000000000000..d237df16396bf0 --- /dev/null +++ b/test/fixtures/test-runner/output/source_mapped_locations.ts @@ -0,0 +1,7 @@ +// Flags: --enable-source-maps +import { test } from 'node:test'; +import { strictEqual } from 'node:assert'; + +test('fails', () => { + strictEqual(1, 2); +}); diff --git a/test/parallel/test-runner-output.mjs b/test/parallel/test-runner-output.mjs index c81a0f1160e7eb..61541e307b7114 100644 --- a/test/parallel/test-runner-output.mjs +++ b/test/parallel/test-runner-output.mjs @@ -107,6 +107,7 @@ const tests = [ { name: 'test-runner/output/spec_reporter_successful.js', transform: specTransform }, { name: 'test-runner/output/spec_reporter.js', transform: specTransform }, { name: 'test-runner/output/spec_reporter_cli.js', transform: specTransform }, + { name: 'test-runner/output/source_mapped_locations.mjs' }, process.features.inspector ? { name: 'test-runner/output/lcov_reporter.js', transform: lcovTransform } : false, { name: 'test-runner/output/output.js' }, { name: 'test-runner/output/output_cli.js' },