diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b14d26488af..194e30b17b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ ### Fixes -- `[jest-circus]` Fix type ellision of jest-runtime imports ([#9717](https://github.com/facebook/jest/pull/9717)) +- `[jest-circus]` Fix type elision of jest-runtime imports ([#9717](https://github.com/facebook/jest/pull/9717)) +- `[@jest/transform]` Fix coverage reporter for uncovered files without transformers, reverting ([#9460](https://github.com/facebook/jest/pull/9460)) ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/coverageWithoutTransform.test.ts.snap b/e2e/__tests__/__snapshots__/coverageWithoutTransform.test.ts.snap new file mode 100644 index 000000000000..0232699a6c55 --- /dev/null +++ b/e2e/__tests__/__snapshots__/coverageWithoutTransform.test.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`produces code coverage for uncovered files without transformer 1`] = ` +---------------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +---------------------|---------|----------|---------|---------|------------------- +All files | 0 | 100 | 0 | 0 | + some-random-file.js | 0 | 100 | 0 | 0 | 8,10,11 +---------------------|---------|----------|---------|---------|------------------- +`; diff --git a/e2e/__tests__/coverageWithoutTransform.test.ts b/e2e/__tests__/coverageWithoutTransform.test.ts new file mode 100644 index 000000000000..200c330f39cf --- /dev/null +++ b/e2e/__tests__/coverageWithoutTransform.test.ts @@ -0,0 +1,29 @@ +/** + * 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 {wrap} from 'jest-snapshot-serializer-raw'; +import {cleanup} from '../Utils'; +import runJest from '../runJest'; + +const dir = path.resolve(__dirname, '../coverage-without-transform'); +const coverageDir = path.join(dir, 'coverage'); + +beforeAll(() => { + cleanup(coverageDir); +}); + +afterAll(() => { + cleanup(coverageDir); +}); + +it('produces code coverage for uncovered files without transformer', () => { + const {exitCode, stdout} = runJest(dir, ['--coverage', '--no-cache']); + + expect(exitCode).toBe(0); + expect(wrap(stdout)).toMatchSnapshot(); +}); diff --git a/e2e/coverage-without-transform/__tests__/test.js b/e2e/coverage-without-transform/__tests__/test.js new file mode 100644 index 000000000000..56dfeefe0ba8 --- /dev/null +++ b/e2e/coverage-without-transform/__tests__/test.js @@ -0,0 +1,10 @@ +/** + * 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. + */ + +it('dummy test', () => { + expect(1).toBe(1); +}); diff --git a/e2e/coverage-without-transform/package.json b/e2e/coverage-without-transform/package.json new file mode 100644 index 000000000000..1453c0e1c96b --- /dev/null +++ b/e2e/coverage-without-transform/package.json @@ -0,0 +1,7 @@ +{ + "jest": { + "collectCoverageFrom": ["*.js"], + "transform": {}, + "testEnvironment": "node" + } +} diff --git a/e2e/coverage-without-transform/some-random-file.js b/e2e/coverage-without-transform/some-random-file.js new file mode 100644 index 000000000000..40ac1552e7ee --- /dev/null +++ b/e2e/coverage-without-transform/some-random-file.js @@ -0,0 +1,12 @@ +/** + * 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. + */ + +module.exports = function doES6Stuff(testObj, multiplier) { + // eslint-disable-next-line no-unused-vars + const {someNumber, ...others} = testObj; + return someNumber * multiplier; +}; diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index a3658842f164..d522d114c059 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -191,12 +191,8 @@ export default class ScriptTransformer { return transform; } - private _instrumentFile( - filename: Config.Path, - input: TransformedSource, - canMapToInput: boolean, - ): TransformedSource { - const result = babelTransform(input.code, { + private _instrumentFile(filename: Config.Path, content: string): string { + const result = babelTransform(content, { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, caller: { @@ -214,27 +210,21 @@ export default class ScriptTransformer { cwd: this._config.rootDir, exclude: [], extension: false, - // Needed for correct coverage as soon as we start storing a source map of the instrumented code - inputSourceMap: input.map, useInlineSourceMaps: false, }, ], ], - /** - * It's necessary to be able to map back to original source from the instrumented code. - * The inline map is needed for debugging functionality, and exposing it as a separate file is needed - * for mapping stack traces. It's convenient to use 'both' here and avoid extracting the source map. - * - * Previous behavior of emitting no map when we can't map back to original source is preserved. - */ - sourceMaps: canMapToInput ? 'both' : false, }); - if (result && result.code) { - return result as TransformResult; + if (result) { + const {code} = result; + + if (code) { + return code; + } } - return {code: input.code}; + return content; } private _getRealPath(filepath: Config.Path): Config.Path { @@ -328,36 +318,17 @@ 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) { - /** - * 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, - shouldEmitSourceMaps, - ); - code = instrumented.code; - - if (instrumented.map) { - map = instrumented.map; - } + code = this._instrumentFile(filename, transformed.code); } else { code = transformed.code; } - if (map) { + if (transformed.map) { const sourceMapContent = - typeof map === 'string' ? map : JSON.stringify(map); + typeof transformed.map === 'string' + ? transformed.map + : JSON.stringify(transformed.map); writeCacheFile(sourceMapPath, sourceMapContent); } else { sourceMapPath = null; 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 fe9bb5ceb3ec..0200ad87482c 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 @@ -78,7 +78,7 @@ exports[`ScriptTransformer transforms a file properly 1`] = ` /* istanbul ignore next */ function cov_25u22311x4() { var path = "/fruits/banana.js"; - var hash = "3f8e915bed83285455a8a16aa04dc0cf5242d755"; + var hash = "4be0f6184160be573fc43f7c2a5877c28b7ce249"; var global = new Function("return this")(); var gcv = "__coverage__"; var coverageData = { @@ -102,9 +102,8 @@ function cov_25u22311x4() { }, f: {}, b: {}, - inputSourceMap: null, _coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9", - hash: "3f8e915bed83285455a8a16aa04dc0cf5242d755" + hash: "4be0f6184160be573fc43f7c2a5877c28b7ce249" }; var coverage = global[gcv] || (global[gcv] = {}); @@ -124,14 +123,13 @@ 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 = "8b5afd38d79008f13ebc229b89ef82b12ee9447a"; + var hash = "7705dd5fcfbc884dcea7062944cfb8cc5d141d1a"; var global = new Function("return this")(); var gcv = "__coverage__"; var coverageData = { @@ -193,9 +191,8 @@ function cov_23yvu8etmu() { "0": 0 }, b: {}, - inputSourceMap: null, _coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9", - hash: "8b5afd38d79008f13ebc229b89ef82b12ee9447a" + hash: "7705dd5fcfbc884dcea7062944cfb8cc5d141d1a" }; var coverage = global[gcv] || (global[gcv] = {}); @@ -221,7 +218,6 @@ 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 596116eb3441..9069861c2420 100644 --- a/packages/jest-transform/src/__tests__/script_transformer.test.js +++ b/packages/jest-transform/src/__tests__/script_transformer.test.js @@ -535,96 +535,6 @@ 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), - { - encoding: 'utf8', - fsync: false, - }, - ); - - // 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), - { - encoding: 'utf8', - fsync: false, - }, - ); - - // 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);