diff --git a/integration_tests/__tests__/__snapshots__/coverage_remapping.test.js.snap b/integration_tests/__tests__/__snapshots__/coverage_remapping.test.js.snap index 00cebd87ecb8..70b45b87638c 100644 --- a/integration_tests/__tests__/__snapshots__/coverage_remapping.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/coverage_remapping.test.js.snap @@ -3,6 +3,7 @@ exports[`maps code coverage against original source 1`] = ` Object { "covered.ts": Object { + "_coverageSchema": "332fd63041d2c1bcb487cc26dd0d5f7d97098a6c", "b": Object { "0": Array [ 1, @@ -24,34 +25,35 @@ Object { }, "branchMap": Object { "0": Object { + "line": 4, "loc": Object { "end": Object { - "column": 9, - "line": 5, + "column": 11, + "line": 6, }, "start": Object { - "column": 8, - "line": 5, + "column": 18, + "line": 4, }, }, "locations": Array [ Object { "end": Object { - "column": 9, + "column": 11, "line": 5, }, "start": Object { - "column": 8, + "column": 10, "line": 5, }, }, Object { "end": Object { - "column": 9, + "column": 11, "line": 6, }, "start": Object { - "column": 8, + "column": 10, "line": 6, }, }, @@ -59,34 +61,35 @@ Object { "type": "cond-expr", }, "1": Object { + "line": 7, "loc": Object { "end": Object { - "column": 37, + "column": 30, "line": 7, }, "start": Object { - "column": 36, + "column": 18, "line": 7, }, }, "locations": Array [ Object { "end": Object { - "column": 37, + "column": 26, "line": 7, }, "start": Object { - "column": 36, + "column": 25, "line": 7, }, }, Object { "end": Object { - "column": 41, + "column": 30, "line": 7, }, "start": Object { - "column": 40, + "column": 29, "line": 7, }, }, @@ -94,44 +97,45 @@ Object { "type": "cond-expr", }, "2": Object { + "line": 8, "loc": Object { "end": Object { - "column": 33, + "column": 39, "line": 8, }, "start": Object { - "column": 29, + "column": 18, "line": 8, }, }, "locations": Array [ Object { "end": Object { - "column": 33, + "column": 22, "line": 8, }, "start": Object { - "column": 29, + "column": 18, "line": 8, }, }, Object { "end": Object { - "column": 41, + "column": 30, "line": 8, }, "start": Object { - "column": 37, + "column": 26, "line": 8, }, }, Object { "end": Object { - "column": 50, + "column": 39, "line": 8, }, "start": Object { - "column": 45, + "column": 34, "line": 8, }, }, @@ -139,34 +143,35 @@ Object { "type": "binary-expr", }, "3": Object { + "line": 9, "loc": Object { "end": Object { - "column": 42, + "column": 79, "line": 9, }, "start": Object { - "column": 32, + "column": 13, "line": 9, }, }, "locations": Array [ Object { "end": Object { - "column": 42, + "column": 48, "line": 9, }, "start": Object { - "column": 32, + "column": 20, "line": 9, }, }, Object { "end": Object { - "column": 55, + "column": 79, "line": 9, }, "start": Object { - "column": 45, + "column": 51, "line": 9, }, }, @@ -183,21 +188,22 @@ Object { "0": Object { "decl": Object { "end": Object { - "column": 28, + "column": 36, "line": 3, }, "start": Object { - "column": 9, + "column": 26, "line": 3, }, }, + "line": 3, "loc": Object { "end": Object { "column": 1, - "line": 12, + "line": 11, }, "start": Object { - "column": 49, + "column": 43, "line": 3, }, }, @@ -206,17 +212,18 @@ Object { "1": Object { "decl": Object { "end": Object { - "column": 37, + "column": 21, "line": 9, }, "start": Object { - "column": 32, + "column": 20, "line": 9, }, }, + "line": 9, "loc": Object { "end": Object { - "column": 42, + "column": 48, "line": 9, }, "start": Object { @@ -229,21 +236,22 @@ Object { "2": Object { "decl": Object { "end": Object { - "column": 50, + "column": 52, "line": 9, }, "start": Object { - "column": 45, + "column": 51, "line": 9, }, }, + "line": 9, "loc": Object { "end": Object { - "column": 55, + "column": 79, "line": 9, }, "start": Object { - "column": 45, + "column": 63, "line": 9, }, }, @@ -264,8 +272,8 @@ Object { "statementMap": Object { "0": Object { "end": Object { - "column": 1, - "line": 12, + "column": 2, + "line": 11, }, "start": Object { "column": 0, @@ -274,72 +282,72 @@ Object { }, "1": Object { "end": Object { - "column": 9, + "column": 11, "line": 6, }, "start": Object { - "column": 29, + "column": 18, "line": 4, }, }, "2": Object { "end": Object { - "column": 41, + "column": 30, "line": 7, }, "start": Object { - "column": 29, + "column": 18, "line": 7, }, }, "3": Object { "end": Object { - "column": 50, + "column": 39, "line": 8, }, "start": Object { - "column": 29, + "column": 18, "line": 8, }, }, "4": Object { "end": Object { - "column": 55, + "column": 79, "line": 9, }, "start": Object { - "column": 25, + "column": 13, "line": 9, }, }, "5": Object { "end": Object { - "column": 42, + "column": 46, "line": 9, }, "start": Object { - "column": 38, + "column": 34, "line": 9, }, }, "6": Object { "end": Object { - "column": 55, + "column": 77, "line": 9, }, "start": Object { - "column": 51, + "column": 65, "line": 9, }, }, "7": Object { "end": Object { "column": 17, - "line": 11, + "line": 10, }, "start": Object { "column": 4, - "line": 11, + "line": 10, }, }, }, diff --git a/integration_tests/__tests__/coverage_remapping.test.js b/integration_tests/__tests__/coverage_remapping.test.js index 1799f3e20454..91d68fc73353 100644 --- a/integration_tests/__tests__/coverage_remapping.test.js +++ b/integration_tests/__tests__/coverage_remapping.test.js @@ -31,6 +31,7 @@ it('maps code coverage against original source', () => { // reduce absolute paths embedded in the coverage map to just filenames Object.keys(coverageMap).forEach(filename => { coverageMap[filename].path = path.basename(coverageMap[filename].path); + delete coverageMap[filename].hash; coverageMap[path.basename(filename)] = coverageMap[filename]; delete coverageMap[filename]; }); diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index b8f5865e2999..9dba0d52e551 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -15,7 +15,8 @@ "jest-matcher-utils": "^21.2.1", "jest-message-util": "^21.2.1", "jest-snapshot": "^21.2.1", - "p-cancelable": "^0.3.0" + "p-cancelable": "^0.3.0", + "source-map-support": "^0.5.0" }, "devDependencies": { "jest-runtime": "^21.2.1" diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index 31bc67a9b646..5937678104d7 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -15,6 +15,7 @@ import type {TestResult} from 'types/TestResult'; import type Runtime from 'jest-runtime'; import path from 'path'; +import fs from 'fs'; import JasmineReporter from './reporter'; import {install as jasmineAsyncInstall} from './jasmine_async'; @@ -93,6 +94,25 @@ async function jasmine2( runtime.requireModule(config.setupTestFrameworkScriptFile); } + runtime + .requireModule(require.resolve('source-map-support'), 'source-map-support') + .install({ + handleUncaughtExceptions: false, + retrieveSourceMap: source => { + if (runtime._sourceMapRegistry[source]) { + try { + return { + map: JSON.parse( + fs.readFileSync(runtime._sourceMapRegistry[source]), + ), + url: source, + }; + } catch (e) {} + } + return null; + }, + }); + if (globalConfig.testNamePattern) { const testNameRegex = new RegExp(globalConfig.testNamePattern, 'i'); env.specFilter = spec => testNameRegex.test(spec.getFullName()); diff --git a/packages/jest-runtime/src/__tests__/Runtime-sourceMaps-test.js b/packages/jest-runtime/src/__tests__/Runtime-sourceMaps-test.js new file mode 100644 index 000000000000..55148cfc5a7b --- /dev/null +++ b/packages/jest-runtime/src/__tests__/Runtime-sourceMaps-test.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails oncall+jsinfra + */ +'use strict'; + +let createRuntime; + +describe('Runtime', () => { + beforeEach(() => { + createRuntime = require('createRuntime'); + }); + + describe('requireModule', () => { + it('installs source maps if available', () => + createRuntime(__filename).then(runtime => { + let hasThrown = false; + const sum = runtime.requireModule( + runtime.__mockRootPath, + './sourcemaps/out/throwing-mapped-fn.js', + ).sum; + + try { + sum(); + } catch (err) { + hasThrown = true; + /* eslint-disable max-len */ + if (process.platform === 'win32') { + expect(err.stack).toMatch( + /^Error: throwing fn\s+at sum.+\\__tests__\\test_root\\sourcemaps\\throwing-mapped-fn.js:10:9/, + ); + } else { + expect(err.stack).toMatch( + /^Error: throwing fn\s+at sum.+\/__tests__\/test_root\/sourcemaps\/throwing-mapped-fn.js:10:9/, + ); + } + /* eslint-enable max-len */ + } + expect(hasThrown).toBe(true); + })); + }); +}); diff --git a/packages/jest-runtime/src/__tests__/script_transformer.test.js b/packages/jest-runtime/src/__tests__/script_transformer.test.js index 10a04b63bc11..eb5cf3dc60ab 100644 --- a/packages/jest-runtime/src/__tests__/script_transformer.test.js +++ b/packages/jest-runtime/src/__tests__/script_transformer.test.js @@ -334,7 +334,7 @@ describe('ScriptTransformer', () => { ).toBeCalledWith(result.sourceMapPath, sourceMap, {encoding: 'utf8'}); }); - it('does not write source map if mapCoverage option is false', () => { + it('writes source maps if given by the transformer', () => { config = Object.assign(config, { transform: [['^.+\\.js$', 'preprocessor-with-sourcemaps']], }); @@ -350,6 +350,29 @@ describe('ScriptTransformer', () => { map, }); + const result = scriptTransformer.transform('/fruits/banana.js', { + collectCoverage: true, + mapCoverage: false, + }); + expect(result.sourceMapPath).toEqual(expect.any(String)); + expect( + writeFileAtomic.sync, + ).toBeCalledWith(result.sourceMapPath, JSON.stringify(map), { + encoding: 'utf8', + }); + }); + + it('does not write source map if not given by the transformer', () => { + config = Object.assign(config, { + transform: [['^.+\\.js$', 'preprocessor-with-sourcemaps']], + }); + const scriptTransformer = new ScriptTransformer(config); + + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: 'content', + map: null, + }); + const result = scriptTransformer.transform('/fruits/banana.js', { collectCoverage: true, mapCoverage: false, diff --git a/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js new file mode 100644 index 000000000000..398191b191e3 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js @@ -0,0 +1,18 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true, +}); +exports.sum = sum; +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +function sum() { + throw new Error('throwing fn'); +} +//# sourceMappingURL=throwing-mapped-fn.js.map diff --git a/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js.map b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js.map new file mode 100644 index 000000000000..2caa28803e83 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/out/throwing-mapped-fn.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../throwing-mapped-fn.js"],"names":["sum","Error"],"mappings":";;;;;QAQgBA,G,GAAAA,G;AARhB;;;;;;;;AAQO,SAASA,GAAT,GAAe;AACpB,QAAM,IAAIC,KAAJ,CAAU,aAAV,CAAN;AACD","file":"throwing-mapped-fn.js","sourcesContent":["/**\n * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nexport function sum() {\n throw new Error('throwing fn');\n};\n"]} \ No newline at end of file diff --git a/packages/jest-runtime/src/__tests__/test_root/sourcemaps/throwing-mapped-fn.js b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/throwing-mapped-fn.js new file mode 100644 index 000000000000..bceeffafaf2d --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/sourcemaps/throwing-mapped-fn.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +export function sum() { + throw new Error('throwing fn'); +} diff --git a/packages/jest-runtime/src/script_transformer.js b/packages/jest-runtime/src/script_transformer.js index a8d76d16dcaf..cea308731189 100644 --- a/packages/jest-runtime/src/script_transformer.js +++ b/packages/jest-runtime/src/script_transformer.js @@ -238,8 +238,6 @@ export default class ScriptTransformer { transformed.map = inlineSourceMap.toJSON(); } } - } else { - transformed.map = null; } // That means that the transform has a custom instrumentation @@ -252,7 +250,7 @@ export default class ScriptTransformer { code = transformed.code; } - if (instrument && transformed.map && mapCoverage) { + if (transformed.map) { const sourceMapContent = typeof transformed.map === 'string' ? transformed.map diff --git a/yarn.lock b/yarn.lock index d1eade80989c..4e9df37dfd19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5432,6 +5432,12 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" +source-map-support@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.0.tgz#2018a7ad2bdf8faf2691e5fddab26bed5a2bacab" + dependencies: + source-map "^0.6.0" + source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" @@ -5442,6 +5448,10 @@ source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"