From 9174c600f8607b278de45b07d94e568ae83b4e79 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Tue, 14 Apr 2020 15:30:04 -0500 Subject: [PATCH 01/14] Improve source map handling when instrumenting transformed code (#5739) --- .../jest-transform/src/ScriptTransformer.ts | 65 ++++++++------ .../script_transformer.test.js.snap | 12 ++- .../src/__tests__/script_transformer.test.js | 84 +++++++++++++++++++ 3 files changed, 130 insertions(+), 31 deletions(-) diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 1ad4109cd83a..2b92994b503a 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -205,11 +205,15 @@ export default class ScriptTransformer { private _instrumentFile( filename: Config.Path, - content: string, + input: TransformedSource, supportsDynamicImport: boolean, supportsStaticESM: boolean, - ): string { - const result = babelTransform(content, { + canMapToInput: boolean, + ): TransformedSource { + const inputCode = typeof input === 'string' ? input : input.code; + const inputMap = typeof input === 'string' ? null : input.map; + + const result = babelTransform(inputCode, { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, caller: { @@ -228,21 +232,19 @@ export default class ScriptTransformer { cwd: this._config.rootDir, exclude: [], extension: false, + inputSourceMap: inputMap, useInlineSourceMaps: false, }, ], ], + sourceMaps: canMapToInput ? 'both' : false, }); - if (result) { - const {code} = result; - - if (code) { - return code; - } + if (result && result.code) { + return result as TransformResult; } - return content; + return input; } private _getRealPath(filepath: Config.Path): Config.Path { @@ -287,10 +289,6 @@ export default class ScriptTransformer { const transformWillInstrument = shouldCallTransform && transform && transform.canInstrument; - // If we handle the coverage instrumentation, we should try to map code - // coverage against original source with any provided source map - const mapCoverage = instrument && !transformWillInstrument; - if (code) { // This is broken: we return the code, and a path for the source map // directly from the cache. But, nothing ensures the source map actually @@ -298,7 +296,7 @@ export default class ScriptTransformer { // two separate processes write concurrently to the same cache files. return { code, - mapCoverage, + mapCoverage: false, originalCode: content, sourceMapPath, }; @@ -333,9 +331,8 @@ export default class ScriptTransformer { //Could be a potential freeze here. //See: https://github.com/facebook/jest/pull/5177#discussion_r158883570 const inlineSourceMap = sourcemapFromSource(transformed.code); - if (inlineSourceMap) { - transformed.map = inlineSourceMap.toJSON(); + transformed.map = inlineSourceMap.toObject(); } } catch (e) { const transformPath = this._getTransformPath(filename); @@ -347,22 +344,38 @@ export default class ScriptTransformer { } } + // Apply instrumentation to the code if necessary, keeping the instrumented code and new map + let map = transformed.map; if (!transformWillInstrument && instrument) { - code = this._instrumentFile( + /** + * We can map the original source code to the instrumented code ONLY if + * - the process of transforming the code produced a source map e.g. ts-jest + * - we did not transform the source code + * + * Otherwise we cannot make any statements about how the instrumented code corresponds to the original code, + * and we should NOT emit any source maps + * + */ + const shouldEmitSourceMaps = (!!transform && !!map) || !transform; + + const instrumented = this._instrumentFile( filename, - transformed.code, + transformed, supportsDynamicImport, supportsStaticESM, + shouldEmitSourceMaps, ); + + code = + typeof instrumented === 'string' ? instrumented : instrumented.code; + map = typeof instrumented === 'string' ? null : instrumented.map; } else { code = transformed.code; } - if (transformed.map) { + if (map) { const sourceMapContent = - typeof transformed.map === 'string' - ? transformed.map - : JSON.stringify(transformed.map); + typeof map === 'string' ? map : JSON.stringify(map); writeCacheFile(sourceMapPath, sourceMapContent); } else { sourceMapPath = null; @@ -372,7 +385,7 @@ export default class ScriptTransformer { return { code, - mapCoverage, + mapCoverage: false, originalCode: content, sourceMapPath, }; @@ -396,7 +409,6 @@ export default class ScriptTransformer { let code = content; let sourceMapPath: string | null = null; - let mapCoverage = false; const willTransform = !isInternalModule && @@ -415,12 +427,11 @@ export default class ScriptTransformer { code = transformedSource.code; sourceMapPath = transformedSource.sourceMapPath; - mapCoverage = transformedSource.mapCoverage; } return { code, - mapCoverage, + mapCoverage: false, originalCode: content, sourceMapPath, }; diff --git a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap index 19c563f8e376..13584d3f6693 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap @@ -80,7 +80,7 @@ exports[`ScriptTransformer transforms a file properly 1`] = ` /* istanbul ignore next */ function cov_25u22311x4() { var path = "/fruits/banana.js"; - var hash = "4be0f6184160be573fc43f7c2a5877c28b7ce249"; + var hash = "3f8e915bed83285455a8a16aa04dc0cf5242d755"; var global = new Function("return this")(); var gcv = "__coverage__"; var coverageData = { @@ -104,8 +104,9 @@ function cov_25u22311x4() { }, f: {}, b: {}, + inputSourceMap: null, _coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9", - hash: "4be0f6184160be573fc43f7c2a5877c28b7ce249" + hash: "3f8e915bed83285455a8a16aa04dc0cf5242d755" }; var coverage = global[gcv] || (global[gcv] = {}); @@ -125,13 +126,14 @@ function cov_25u22311x4() { cov_25u22311x4(); cov_25u22311x4().s[0]++; module.exports = "banana"; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhbmFuYS5qcyJdLCJuYW1lcyI6WyJtb2R1bGUiLCJleHBvcnRzIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQUEsTUFBTSxDQUFDQyxPQUFQLEdBQWlCLFFBQWpCIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSBcImJhbmFuYVwiOyJdfQ== `; exports[`ScriptTransformer transforms a file properly 2`] = ` /* istanbul ignore next */ function cov_23yvu8etmu() { var path = "/fruits/kiwi.js"; - var hash = "7705dd5fcfbc884dcea7062944cfb8cc5d141d1a"; + var hash = "8b5afd38d79008f13ebc229b89ef82b12ee9447a"; var global = new Function("return this")(); var gcv = "__coverage__"; var coverageData = { @@ -193,8 +195,9 @@ function cov_23yvu8etmu() { "0": 0 }, b: {}, + inputSourceMap: null, _coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9", - hash: "7705dd5fcfbc884dcea7062944cfb8cc5d141d1a" + hash: "8b5afd38d79008f13ebc229b89ef82b12ee9447a" }; var coverage = global[gcv] || (global[gcv] = {}); @@ -220,6 +223,7 @@ module.exports = () => { cov_23yvu8etmu().s[1]++; return "kiwi"; }; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImtpd2kuanMiXSwibmFtZXMiOlsibW9kdWxlIiwiZXhwb3J0cyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUFBLE1BQU0sQ0FBQ0MsT0FBUCxHQUFpQixNQUFNO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBTSxDQUE3QiIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5leHBvcnRzID0gKCkgPT4gXCJraXdpXCI7Il19 `; exports[`ScriptTransformer uses multiple preprocessors 1`] = ` diff --git a/packages/jest-transform/src/__tests__/script_transformer.test.js b/packages/jest-transform/src/__tests__/script_transformer.test.js index 9069861c2420..b6570ee1c404 100644 --- a/packages/jest-transform/src/__tests__/script_transformer.test.js +++ b/packages/jest-transform/src/__tests__/script_transformer.test.js @@ -535,6 +535,90 @@ describe('ScriptTransformer', () => { expect(writeFileAtomic.sync).toHaveBeenCalledTimes(1); }); + it('should write a source map for the instrumented file when transformed', () => { + const transformerConfig = { + ...config, + transform: [['^.+\\.js$', 'preprocessor-with-sourcemaps']], + }; + const scriptTransformer = new ScriptTransformer(transformerConfig); + + const map = { + mappings: ';AAAA', + version: 3, + }; + + // A map from the original source to the instrumented output + /* eslint-disable sort-keys */ + const instrumentedCodeMap = { + version: 3, + sources: ['banana.js'], + names: ['content'], + mappings: ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAAA,OAAO', + sourcesContent: ['content'], + }; + /* eslint-enable */ + + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: 'content', + map, + }); + + const result = scriptTransformer.transform( + '/fruits/banana.js', + makeGlobalConfig({ + collectCoverage: true, + }), + ); + expect(result.sourceMapPath).toEqual(expect.any(String)); + expect(writeFileAtomic.sync).toBeCalledTimes(2); + expect(writeFileAtomic.sync).toBeCalledWith( + result.sourceMapPath, + JSON.stringify(instrumentedCodeMap), + expect.anything(), + ); + + // Inline source map allows debugging of original source when running instrumented code + expect(result.code).toContain('//# sourceMappingURL'); + }); + + it('should write a source map for the instrumented file when not transformed', () => { + const scriptTransformer = new ScriptTransformer(config); + + // A map from the original source to the instrumented output + /* eslint-disable sort-keys */ + const instrumentedCodeMap = { + version: 3, + sources: ['banana.js'], + names: ['module', 'exports'], + mappings: + ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAAA,MAAM,CAACC,OAAP,GAAiB,QAAjB', + sourcesContent: ['module.exports = "banana";'], + }; + /* eslint-enable */ + + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: 'content', + map: null, + }); + + const result = scriptTransformer.transform( + '/fruits/banana.js', + makeGlobalConfig({ + collectCoverage: true, + }), + ); + expect(result.sourceMapPath).toEqual(expect.any(String)); + expect(writeFileAtomic.sync).toBeCalledTimes(2); + expect(writeFileAtomic.sync).toBeCalledWith( + result.sourceMapPath, + JSON.stringify(instrumentedCodeMap), + expect.anything(), + ); + + // Inline source map allows debugging of original source when running instrumented code + expect(result.code).toContain('//# sourceMappingURL'); + }); + it('passes expected transform options to getCacheKey', () => { config = {...config, transform: [['^.+\\.js$', 'test_preprocessor']]}; const scriptTransformer = new ScriptTransformer(config); From 118c951c77d2b5e5f9ab0fd121ac1360c585edda Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Sat, 18 Apr 2020 09:03:35 -0500 Subject: [PATCH 02/14] Remove the concept of using Jest's stored sourceMaps to map BabelCoverage - inputSourceMap should always be available. --- packages/jest-reporters/src/coverage_reporter.ts | 7 ------- packages/jest-reporters/src/generateEmptyCoverage.ts | 10 +++++----- packages/jest-transform/src/ScriptTransformer.ts | 3 --- packages/jest-types/src/Transform.ts | 1 - 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index b769743e2ff6..68ba5ab5bfc0 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -215,13 +215,6 @@ export default class CoverageReporter extends BaseReporter { ]); } else { this._coverageMap.addFileCoverage(result.coverage); - - if (result.sourceMapPath) { - this._sourceMapStore.registerURL( - filename, - result.sourceMapPath, - ); - } } } } catch (error) { diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index 62ab4c023f2c..b6c270060fdd 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -18,7 +18,6 @@ export type CoverageWorkerResult = | { kind: 'BabelCoverage'; coverage: FileCoverage; - sourceMapPath?: string | null; } | { kind: 'V8Coverage'; @@ -66,9 +65,11 @@ export default function ( } // Transform file with instrumentation to make sure initial coverage data is well mapped to original code. - const {code, mapCoverage, sourceMapPath} = new ScriptTransformer( - config, - ).transformSource(filename, source, true); + const {code} = new ScriptTransformer(config).transformSource( + filename, + source, + true, + ); // TODO: consider passing AST const extracted = readInitialCoverage(code); // Check extracted initial coverage is not null, this can happen when using /* istanbul ignore file */ @@ -76,7 +77,6 @@ export default function ( coverageWorkerResult = { coverage: createFileCoverage(extracted.coverageData), kind: 'BabelCoverage', - sourceMapPath: mapCoverage ? sourceMapPath : null, }; } } diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 2b92994b503a..cb5d9e1d9e79 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -296,7 +296,6 @@ export default class ScriptTransformer { // two separate processes write concurrently to the same cache files. return { code, - mapCoverage: false, originalCode: content, sourceMapPath, }; @@ -385,7 +384,6 @@ export default class ScriptTransformer { return { code, - mapCoverage: false, originalCode: content, sourceMapPath, }; @@ -431,7 +429,6 @@ export default class ScriptTransformer { return { code, - mapCoverage: false, originalCode: content, sourceMapPath, }; diff --git a/packages/jest-types/src/Transform.ts b/packages/jest-types/src/Transform.ts index 9c59e7b34489..43e9ff986eb1 100644 --- a/packages/jest-types/src/Transform.ts +++ b/packages/jest-types/src/Transform.ts @@ -9,6 +9,5 @@ export type TransformResult = { code: string; originalCode: string; - mapCoverage: boolean; sourceMapPath: string | null; }; From 40fcece91d7208f9361e5a9ffde31322f1a04bd4 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Mon, 20 Apr 2020 16:13:27 -0500 Subject: [PATCH 03/14] Remove use of mapCoverage in jest-runtime. --- packages/jest-runtime/src/index.ts | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 001de2f98ec9..9b56a6343662 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -134,7 +134,6 @@ class Runtime { private _isolatedModuleRegistry: ModuleRegistry | null; private _moduleRegistry: ModuleRegistry; private _esmoduleRegistry: Map; - private _needsCoverageMapped: Set; private _resolver: Resolver; private _shouldAutoMock: boolean; private _shouldMockModuleCache: BooleanObject; @@ -178,7 +177,6 @@ class Runtime { this._isolatedMockRegistry = null; this._moduleRegistry = new Map(); this._esmoduleRegistry = new Map(); - this._needsCoverageMapped = new Set(); this._resolver = resolver; this._scriptTransformer = new ScriptTransformer(config); this._shouldAutoMock = config.automock; @@ -765,20 +763,9 @@ class Runtime { }); } - getSourceMapInfo(coveredFiles: Set): Record { - return Object.keys(this._sourceMapRegistry).reduce>( - (result, sourcePath) => { - if ( - coveredFiles.has(sourcePath) && - this._needsCoverageMapped.has(sourcePath) && - fs.existsSync(this._sourceMapRegistry[sourcePath]) - ) { - result[sourcePath] = this._sourceMapRegistry[sourcePath]; - } - return result; - }, - {}, - ); + // TODO - remove in Jest 26 + getSourceMapInfo(_coveredFiles: Set): Record { + return {}; } getSourceMaps(): SourceMapRegistry { @@ -1026,9 +1013,6 @@ class Runtime { if (transformedFile.sourceMapPath) { this._sourceMapRegistry[filename] = transformedFile.sourceMapPath; - if (transformedFile.mapCoverage) { - this._needsCoverageMapped.add(filename); - } } return transformedFile; } From 6adade95d383dd03063a39d64552428bd8140f8f Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Mon, 20 Apr 2020 16:19:50 -0500 Subject: [PATCH 04/14] Restore 'mapCoverage' as a nullable field on TransformResult. --- packages/jest-types/src/Transform.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jest-types/src/Transform.ts b/packages/jest-types/src/Transform.ts index 43e9ff986eb1..2e60af1e489d 100644 --- a/packages/jest-types/src/Transform.ts +++ b/packages/jest-types/src/Transform.ts @@ -9,5 +9,6 @@ export type TransformResult = { code: string; originalCode: string; + mapCoverage?: boolean; // TODO - Remove in Jest 26 sourceMapPath: string | null; }; From 5a02bb17847f4b43308c74580972596b6e8aff79 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Mon, 20 Apr 2020 16:28:48 -0500 Subject: [PATCH 05/14] Removing more code downstream of 'mapCoverage' in jest-runtime. --- .../jest-reporters/src/coverage_reporter.ts | 22 ------------------- packages/jest-runner/src/runTest.ts | 1 - packages/jest-test-result/src/types.ts | 1 + 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index 68ba5ab5bfc0..4ec8d75961ac 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -71,28 +71,6 @@ export default class CoverageReporter extends BaseReporter { if (testResult.coverage) { this._coverageMap.merge(testResult.coverage); } - - const sourceMaps = testResult.sourceMaps; - if (sourceMaps) { - Object.keys(sourceMaps).forEach(sourcePath => { - let inputSourceMap: RawSourceMap | undefined; - try { - const coverage: istanbulCoverage.FileCoverage = this._coverageMap.fileCoverageFor( - sourcePath, - ); - inputSourceMap = (coverage.toJSON() as any).inputSourceMap; - } finally { - if (inputSourceMap) { - this._sourceMapStore.registerMap(sourcePath, inputSourceMap); - } else { - this._sourceMapStore.registerURL( - sourcePath, - sourceMaps[sourcePath], - ); - } - } - }); - } } async onRunComplete( diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 36754d3f086e..fb3e93ea0eda 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -275,7 +275,6 @@ async function runTestInternal( const coverageKeys = Object.keys(coverage); if (coverageKeys.length) { result.coverage = coverage; - result.sourceMaps = runtime.getSourceMapInfo(new Set(coverageKeys)); } } diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index 304b358c143b..ffd9c23735b6 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -104,6 +104,7 @@ export type TestResult = { unmatched: number; updated: number; }; + // TODO - Remove in Jest 26 sourceMaps?: { [sourcePath: string]: string; }; From fcc73834cec1a2382234ce013a48068324aacd5b Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Mon, 20 Apr 2020 21:20:36 -0500 Subject: [PATCH 06/14] Add test arounds stack traces for code which is both transformed and instrumented. --- .../stackTraceSourceMapsWithCoverage.test.ts | 23 +++++++++++++++++++ .../__tests__/fails.ts | 11 +++++++++ .../lib.ts | 14 +++++++++++ .../package.json | 13 +++++++++++ .../preprocessor.js | 21 +++++++++++++++++ .../yarn.lock | 8 +++++++ 6 files changed, 90 insertions(+) create mode 100644 e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts create mode 100644 e2e/stack-trace-source-maps-with-coverage/__tests__/fails.ts create mode 100644 e2e/stack-trace-source-maps-with-coverage/lib.ts create mode 100644 e2e/stack-trace-source-maps-with-coverage/package.json create mode 100644 e2e/stack-trace-source-maps-with-coverage/preprocessor.js create mode 100644 e2e/stack-trace-source-maps-with-coverage/yarn.lock diff --git a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts new file mode 100644 index 000000000000..8b2f8d909f5f --- /dev/null +++ b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import * as path from 'path'; +import {run} from '../Utils'; +import runJest from '../runJest'; + +it('processes stack traces and code frames with source maps with coverage', () => { + const dir = path.resolve( + __dirname, + '../stack-trace-source-maps-with-coverage', + ); + run('yarn', dir); + const {stderr} = runJest(dir, ['--no-cache', '--coverage']); + + // Should report an error at source line 13 in lib.ts at line 10 of the test + expect(stderr).toMatch("13 | throw new Error('This did not work!');\n"); + expect(stderr).toMatch(`at Object.error (lib.ts:13:9) + at Object. (__tests__/fails.ts:10:3)`); +}); diff --git a/e2e/stack-trace-source-maps-with-coverage/__tests__/fails.ts b/e2e/stack-trace-source-maps-with-coverage/__tests__/fails.ts new file mode 100644 index 000000000000..583175c89ed7 --- /dev/null +++ b/e2e/stack-trace-source-maps-with-coverage/__tests__/fails.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import {error} from '../lib'; + +it('fails', () => { + error(); +}); diff --git a/e2e/stack-trace-source-maps-with-coverage/lib.ts b/e2e/stack-trace-source-maps-with-coverage/lib.ts new file mode 100644 index 000000000000..9fffd6978ab1 --- /dev/null +++ b/e2e/stack-trace-source-maps-with-coverage/lib.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +interface NotUsedButTakesUpLines { + a: number; + b: string; +} + +export function error() { + throw new Error('This did not work!'); +} diff --git a/e2e/stack-trace-source-maps-with-coverage/package.json b/e2e/stack-trace-source-maps-with-coverage/package.json new file mode 100644 index 000000000000..f8fbad2d8bca --- /dev/null +++ b/e2e/stack-trace-source-maps-with-coverage/package.json @@ -0,0 +1,13 @@ +{ + "jest": { + "rootDir": "./", + "transform": { + "^.+\\.(ts)$": "/preprocessor.js" + }, + "testEnvironment": "node", + "testRegex": "fails" + }, + "dependencies": { + "typescript": "^3.7.4" + } +} diff --git a/e2e/stack-trace-source-maps-with-coverage/preprocessor.js b/e2e/stack-trace-source-maps-with-coverage/preprocessor.js new file mode 100644 index 000000000000..133d42ec44a2 --- /dev/null +++ b/e2e/stack-trace-source-maps-with-coverage/preprocessor.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const tsc = require('typescript'); + +module.exports = { + process(src, path) { + return tsc.transpileModule(src, { + compilerOptions: { + inlineSourceMap: true, + module: tsc.ModuleKind.CommonJS, + target: 'es5', + }, + fileName: path, + }).outputText; + }, +}; diff --git a/e2e/stack-trace-source-maps-with-coverage/yarn.lock b/e2e/stack-trace-source-maps-with-coverage/yarn.lock new file mode 100644 index 000000000000..e9f19f98faf0 --- /dev/null +++ b/e2e/stack-trace-source-maps-with-coverage/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +typescript@^3.7.4: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== From 472b8826c0d322fe5e27e89ef9279e951aa71bd8 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Mon, 20 Apr 2020 21:31:17 -0500 Subject: [PATCH 07/14] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02728ab400e6..80f89d411320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes - `[jest-runtime]` Support importing CJS from ESM using `import` statements ([#9850](https://github.com/facebook/jest/pull/9850)) +- `[jest-transform]` Improve source map handling when instrumenting transformed code ([#9811](https://github.com/facebook/jest/pull/9811)) ### Chore & Maintenance From a24fbe6373e68b685c8fd9eb659378a0a96bdf77 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Tue, 21 Apr 2020 15:13:56 -0500 Subject: [PATCH 08/14] Feedback - add Jest 26 TODO for restructuring TransformedSource. --- packages/jest-transform/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 30075298bc26..138bab7f5406 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -34,6 +34,7 @@ interface FixedRawSourceMap extends SourceMapWithVersion { version: number; } +// TODO: For Jest 26 normalize this (always structured data, never a string) export type TransformedSource = | {code: string; map?: FixedRawSourceMap | string | null} | string; From 4cc5905af3793ea53f57db3850ce1b5bdd31cc97 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Tue, 21 Apr 2020 16:18:41 -0500 Subject: [PATCH 09/14] Feedback - rephrase determination of 'shouldEmitSourceMap' --- packages/jest-transform/src/ScriptTransformer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index cb5d9e1d9e79..46ae3afb049f 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -355,7 +355,8 @@ export default class ScriptTransformer { * and we should NOT emit any source maps * */ - const shouldEmitSourceMaps = (!!transform && !!map) || !transform; + const shouldEmitSourceMaps = + (transform != null && map != null) || transform == null; const instrumented = this._instrumentFile( filename, From 7aff54665925090ee7124907b9edb47e7e13db7b Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Thu, 23 Apr 2020 13:38:08 -0500 Subject: [PATCH 10/14] Remove unused __needsCoverageMapped property. --- packages/jest-runtime/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index f46ad3d5f89a..363c18ba846f 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -140,7 +140,6 @@ class Runtime { private _isolatedModuleRegistry: ModuleRegistry | null; private _moduleRegistry: ModuleRegistry; private _esmoduleRegistry: Map>; - private _needsCoverageMapped: Set; private _resolver: Resolver; private _shouldAutoMock: boolean; private _shouldMockModuleCache: BooleanObject; From 63324f376c80e4357c91fde31b9ce736ef751200 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Thu, 23 Apr 2020 13:42:53 -0500 Subject: [PATCH 11/14] Convert stack trace test to a snapshot test. --- ...ckTraceSourceMapsWithCoverage.test.ts.snap | 27 +++++++++++++++++++ .../stackTraceSourceMapsWithCoverage.test.ts | 8 ++---- 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap diff --git a/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap new file mode 100644 index 000000000000..3c18e16a93f8 --- /dev/null +++ b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`processes stack traces and code frames with source maps with coverage 1`] = ` +Object { + "rest": "FAIL __tests__/fails.ts + ✕ fails + + ● fails + + This did not work! + + 11 | + 12 | export function error() { + > 13 | throw new Error('This did not work!'); + | ^ + 14 | } + 15 | + + at Object.error (lib.ts:13:9) + at Object. (__tests__/fails.ts:10:3)", + "summary": "Test Suites: 1 failed, 1 total +Tests: 1 failed, 1 total +Snapshots: 0 total +Time: <> +Ran all test suites.", +} +`; diff --git a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts index 8b2f8d909f5f..9d7d96284000 100644 --- a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts +++ b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ import * as path from 'path'; -import {run} from '../Utils'; +import {extractSummary, run} from '../Utils'; import runJest from '../runJest'; it('processes stack traces and code frames with source maps with coverage', () => { @@ -15,9 +15,5 @@ it('processes stack traces and code frames with source maps with coverage', () = ); run('yarn', dir); const {stderr} = runJest(dir, ['--no-cache', '--coverage']); - - // Should report an error at source line 13 in lib.ts at line 10 of the test - expect(stderr).toMatch("13 | throw new Error('This did not work!');\n"); - expect(stderr).toMatch(`at Object.error (lib.ts:13:9) - at Object. (__tests__/fails.ts:10:3)`); + expect(extractSummary(stderr)).toMatchSnapshot(); }); From 30ec3c511a7100c62961dcd7addb462d677cf8e2 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Thu, 23 Apr 2020 14:23:29 -0500 Subject: [PATCH 12/14] Restore comment in stack trace test. --- e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts index 9d7d96284000..6f1e2dd6eb37 100644 --- a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts +++ b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts @@ -15,5 +15,7 @@ it('processes stack traces and code frames with source maps with coverage', () = ); run('yarn', dir); const {stderr} = runJest(dir, ['--no-cache', '--coverage']); + + // Should report an error at source line 13 in lib.ts at line 10 of the test expect(extractSummary(stderr)).toMatchSnapshot(); }); From f0e256f38bfbd02f9ff97c56458b3c475f1d87cd Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Thu, 23 Apr 2020 14:33:56 -0500 Subject: [PATCH 13/14] Update snapshot to extract 'rest' from summary and wrap. --- .../stackTraceSourceMapsWithCoverage.test.ts.snap | 11 ++--------- .../stackTraceSourceMapsWithCoverage.test.ts | 3 ++- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap index 3c18e16a93f8..fa3a2f7871ab 100644 --- a/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap +++ b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap @@ -1,8 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`processes stack traces and code frames with source maps with coverage 1`] = ` -Object { - "rest": "FAIL __tests__/fails.ts +FAIL __tests__/fails.ts ✕ fails ● fails @@ -17,11 +16,5 @@ Object { 15 | at Object.error (lib.ts:13:9) - at Object. (__tests__/fails.ts:10:3)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 1 failed, 1 total -Snapshots: 0 total -Time: <> -Ran all test suites.", -} + at Object. (__tests__/fails.ts:10:3) `; diff --git a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts index 6f1e2dd6eb37..2c815feb1ac0 100644 --- a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts +++ b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import {extractSummary, run} from '../Utils'; import runJest from '../runJest'; +import wrap from 'jest-snapshot-serializer-raw'; it('processes stack traces and code frames with source maps with coverage', () => { const dir = path.resolve( @@ -17,5 +18,5 @@ it('processes stack traces and code frames with source maps with coverage', () = const {stderr} = runJest(dir, ['--no-cache', '--coverage']); // Should report an error at source line 13 in lib.ts at line 10 of the test - expect(extractSummary(stderr)).toMatchSnapshot(); + expect(wrap(extractSummary(stderr).rest)).toMatchSnapshot(); }); From 3a30ae1602d7663cff972bcbe164125c29ed8e10 Mon Sep 17 00:00:00 2001 From: Matthew Preble Date: Thu, 23 Apr 2020 14:43:09 -0500 Subject: [PATCH 14/14] Lint fix - import order. --- e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts index 2c815feb1ac0..4ac65f38e890 100644 --- a/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts +++ b/e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ import * as path from 'path'; +import wrap from 'jest-snapshot-serializer-raw'; import {extractSummary, run} from '../Utils'; import runJest from '../runJest'; -import wrap from 'jest-snapshot-serializer-raw'; it('processes stack traces and code frames with source maps with coverage', () => { const dir = path.resolve(