From 86fd625a8d58b97700a63aa1f3d3b8d46fd41c17 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 23 Jun 2019 12:58:27 +0200 Subject: [PATCH 01/26] feat: add support for v8 code coverage --- TestUtils.ts | 1 + .../__snapshots__/showConfig.test.ts.snap | 1 + e2e/__tests__/v8Coverage.test.ts | 21 +++++ e2e/v8-coverage/no-sourcemap/Thing.js | 3 + .../no-sourcemap/__tests__/Thing.test.js | 4 + e2e/v8-coverage/no-sourcemap/cssTransform.js | 4 + e2e/v8-coverage/no-sourcemap/package.json | 10 ++ e2e/v8-coverage/no-sourcemap/x.css | 0 package.json | 2 +- packages/jest-cli/src/cli/args.ts | 12 +++ packages/jest-config/src/Defaults.ts | 1 + packages/jest-config/src/ValidConfig.ts | 1 + packages/jest-config/src/index.ts | 1 + packages/jest-config/src/normalize.ts | 13 ++- packages/jest-core/src/TestScheduler.ts | 7 +- .../log_debug_messages.test.ts.snap | 1 + packages/jest-coverage/.npmignore | 3 + packages/jest-coverage/package.json | 22 +++++ packages/jest-coverage/src/index.ts | 76 +++++++++++++++ packages/jest-coverage/tsconfig.json | 7 ++ packages/jest-reporters/package.json | 2 +- .../jest-reporters/src/coverage_reporter.ts | 29 +++--- .../src/generateEmptyCoverage.ts | 1 + packages/jest-runner/src/runTest.ts | 10 +- packages/jest-runtime/package.json | 10 +- packages/jest-runtime/src/index.ts | 94 ++++++++++++++++++- packages/jest-runtime/tsconfig.json | 1 + .../jest-transform/src/ScriptTransformer.ts | 5 +- packages/jest-transform/src/index.ts | 1 + .../jest-transform/src/shouldInstrument.ts | 2 +- packages/jest-transform/src/types.ts | 6 +- packages/jest-types/src/Config.ts | 4 + yarn.lock | 21 +++-- 33 files changed, 342 insertions(+), 34 deletions(-) create mode 100644 e2e/__tests__/v8Coverage.test.ts create mode 100644 e2e/v8-coverage/no-sourcemap/Thing.js create mode 100644 e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js create mode 100644 e2e/v8-coverage/no-sourcemap/cssTransform.js create mode 100644 e2e/v8-coverage/no-sourcemap/package.json create mode 100644 e2e/v8-coverage/no-sourcemap/x.css create mode 100644 packages/jest-coverage/.npmignore create mode 100644 packages/jest-coverage/package.json create mode 100644 packages/jest-coverage/src/index.ts create mode 100644 packages/jest-coverage/tsconfig.json diff --git a/TestUtils.ts b/TestUtils.ts index aa4f3c345dca..ba901c2de4b9 100644 --- a/TestUtils.ts +++ b/TestUtils.ts @@ -58,6 +58,7 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { testTimeout: 5000, updateSnapshot: 'none', useStderr: false, + v8Coverage: false, verbose: false, watch: false, watchAll: false, diff --git a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap index e7bd2b9c3273..9bdfd06e8d09 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -118,6 +118,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` "testSequencer": "<>/jest-test-sequencer/build/index.js", "updateSnapshot": "all", "useStderr": false, + "v8Coverage": false, "watch": false, "watchAll": false, "watchman": true diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts new file mode 100644 index 000000000000..43bbe5528353 --- /dev/null +++ b/e2e/__tests__/v8Coverage.test.ts @@ -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. + */ + +import path from 'path'; +import runJest from '../runJest'; + +const DIR = path.resolve(__dirname, '../v8-coverage'); + +test('does not explode on missing sourcemap', () => { + const sourcemapDir = path.join(DIR, 'no-sourcemap'); + const {stderr, status} = runJest(sourcemapDir, ['--v8-coverage'], { + stripAnsi: true, + }); + + expect(stderr).not.toContain('no such file or directory'); + expect(status).toBe(0); +}); diff --git a/e2e/v8-coverage/no-sourcemap/Thing.js b/e2e/v8-coverage/no-sourcemap/Thing.js new file mode 100644 index 000000000000..bf3fd4cda78c --- /dev/null +++ b/e2e/v8-coverage/no-sourcemap/Thing.js @@ -0,0 +1,3 @@ +require('./x.css'); + +module.exports = 42; diff --git a/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js b/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js new file mode 100644 index 000000000000..c68cb23eae5a --- /dev/null +++ b/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js @@ -0,0 +1,4 @@ +const Thing = require('../Thing'); + +console.log(Thing); +test.todo('whatever'); diff --git a/e2e/v8-coverage/no-sourcemap/cssTransform.js b/e2e/v8-coverage/no-sourcemap/cssTransform.js new file mode 100644 index 000000000000..0d630053a37c --- /dev/null +++ b/e2e/v8-coverage/no-sourcemap/cssTransform.js @@ -0,0 +1,4 @@ +module.exports = { + getCacheKey: () => 'cssTransform', + process: () => 'module.exports = {};', +}; diff --git a/e2e/v8-coverage/no-sourcemap/package.json b/e2e/v8-coverage/no-sourcemap/package.json new file mode 100644 index 000000000000..96f7c313aa0c --- /dev/null +++ b/e2e/v8-coverage/no-sourcemap/package.json @@ -0,0 +1,10 @@ +{ + "name": "no-sourcemap", + "version": "1.0.0", + "jest": { + "transform": { + "^.+\\.[jt]sx?$": "babel-jest", + "^.+\\.css$": "/cssTransform.js" + } + } +} diff --git a/e2e/v8-coverage/no-sourcemap/x.css b/e2e/v8-coverage/no-sourcemap/x.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/package.json b/package.json index b3fae3325970..9cf31541677f 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "isbinaryfile": "^4.0.0", "istanbul-lib-coverage": "^3.0.0-alpha.1", "istanbul-lib-report": "^3.0.0-alpha.1", - "istanbul-reports": "^3.0.0-alpha.4", + "istanbul-reports": "^3.0.0-alpha.5", "jest-junit": "^9.0.0", "jest-silent-reporter": "^0.1.2", "jest-snapshot-serializer-raw": "^1.1.0", diff --git a/packages/jest-cli/src/cli/args.ts b/packages/jest-cli/src/cli/args.ts index 6bc9650b7a01..a43c4368cb55 100644 --- a/packages/jest-cli/src/cli/args.ts +++ b/packages/jest-cli/src/cli/args.ts @@ -17,6 +17,13 @@ export const check = (argv: Config.Argv) => { ); } + if (argv.collectCoverage && argv.v8Coverage) { + throw new Error( + 'Both --coverage and --v8Coverage were specified, but these two ' + + 'options do not make sense together. Which is it?', + ); + } + for (const key of [ 'onlyChanged', 'lastCommit', @@ -675,6 +682,11 @@ export const options = { description: 'Divert all output to stderr.', type: 'boolean', }, + v8Coverage: { + default: false, + description: 'Collect coverage using V8 instrumentation', + type: 'boolean' as 'boolean', + }, verbose: { default: undefined, description: diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index e096029b7795..24f18bdb367d 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -65,6 +65,7 @@ const defaultOptions: Config.DefaultOptions = { timers: 'real', transformIgnorePatterns: [NODE_MODULES_REGEXP], useStderr: false, + v8Coverage: false, watch: false, watchPathIgnorePatterns: [], watchman: true, diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 28178876d529..6e6f0a39ee31 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -124,6 +124,7 @@ const initialOptions: Config.InitialOptions = { unmockedModulePathPatterns: ['mock'], updateSnapshot: true, useStderr: false, + v8Coverage: false, verbose: false, watch: false, watchPathIgnorePatterns: ['/e2e/'], diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index eb392317c02b..44beb9ac4348 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -152,6 +152,7 @@ const groupOptions = ( testTimeout: options.testTimeout, updateSnapshot: options.updateSnapshot, useStderr: options.useStderr, + v8Coverage: options.v8Coverage, verbose: options.verbose, watch: options.watch, watchAll: options.watchAll, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 96111241f9d3..f90940a3b385 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -878,6 +878,7 @@ export default function normalize( case 'timers': case 'useStderr': case 'verbose': + case 'v8Coverage': case 'watch': case 'watchAll': case 'watchman': @@ -1009,7 +1010,10 @@ export default function normalize( // Is transformed to: `--findRelatedTests '/rootDir/file1.js' --coverage --collectCoverageFrom 'file1.js'` // where arguments to `--collectCoverageFrom` should be globs (or relative // paths to the rootDir) - if (newOptions.collectCoverage && argv.findRelatedTests) { + if ( + (newOptions.collectCoverage || newOptions.v8Coverage) && + argv.findRelatedTests + ) { let collectCoverageFrom = argv._.map(filename => { filename = replaceRootDirInPath(options.rootDir, filename); return path.isAbsolute(filename) @@ -1057,6 +1061,13 @@ export default function normalize( newOptions.logHeapUsage = false; } + if (newOptions.collectCoverage && newOptions.v8Coverage) { + throw createConfigError( + ` Configuration options ${chalk.bold('collectCoverage')} and` + + ` ${chalk.bold('v8Coverage')} cannot be used together.`, + ); + } + return { hasDeprecationWarnings, options: newOptions, diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index 58a141136ea4..e6a20d8f7be3 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -259,14 +259,15 @@ export default class TestScheduler { } private _setupReporters() { - const {collectCoverage, notify, reporters} = this._globalConfig; + const {collectCoverage, notify, reporters, v8Coverage} = this._globalConfig; const isDefault = this._shouldAddDefaultReporters(reporters); + const willCollectCoverage = collectCoverage || v8Coverage; if (isDefault) { - this._setupDefaultReporters(collectCoverage); + this._setupDefaultReporters(willCollectCoverage); } - if (!isDefault && collectCoverage) { + if (!isDefault && willCollectCoverage) { this.addReporter( new CoverageReporter(this._globalConfig, { changedFiles: this._context && this._context.changedFiles, diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap index 0f4ceaeb6a3b..8df22b2a5daa 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap @@ -115,6 +115,7 @@ exports[`prints the config object 1`] = ` "testTimeout": 5000, "updateSnapshot": "none", "useStderr": false, + "v8Coverage": false, "verbose": false, "watch": true, "watchAll": false, diff --git a/packages/jest-coverage/.npmignore b/packages/jest-coverage/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-coverage/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-coverage/package.json b/packages/jest-coverage/package.json new file mode 100644 index 000000000000..2cd0b0966c5c --- /dev/null +++ b/packages/jest-coverage/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jest/coverage", + "version": "24.9.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-coverage" + }, + "license": "MIT", + "main": "build/index.js", + "types": "build/index.d.ts", + "devDependencies": { + "@types/node": "*" + }, + "engines": { + "node": ">= 8" + }, + "publishConfig": { + "access": "public" + }, + "gitHead": "0efb1d7809cb96ae87a7601e7802f1dab3774280" +} diff --git a/packages/jest-coverage/src/index.ts b/packages/jest-coverage/src/index.ts new file mode 100644 index 000000000000..f7fe85709393 --- /dev/null +++ b/packages/jest-coverage/src/index.ts @@ -0,0 +1,76 @@ +/** + * 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 {Profiler, Session} from 'inspector'; + +export type V8Coverage = ReadonlyArray; + +export class CoverageInstrumenter { + private readonly session = new Session(); + + async startInstrumenting() { + this.session.connect(); + + await new Promise((resolve, reject) => { + this.session.post('Profiler.enable', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + + await new Promise((resolve, reject) => { + this.session.post( + 'Profiler.startPreciseCoverage', + {callCount: true, detailed: true}, + err => { + if (err) { + reject(err); + } else { + resolve(); + } + }, + ); + }); + } + + async stopInstrumenting(): Promise { + const preciseCoverage = await new Promise((resolve, reject) => { + this.session.post('Profiler.takePreciseCoverage', (err, res) => { + if (err) { + reject(err); + } else { + resolve(res.result); + } + }); + }); + + await new Promise((resolve, reject) => { + this.session.post('Profiler.stopPreciseCoverage', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + + await new Promise((resolve, reject) => { + this.session.post('Profiler.disable', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + + return preciseCoverage; + } +} diff --git a/packages/jest-coverage/tsconfig.json b/packages/jest-coverage/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-coverage/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index 4e24c60050af..01bee64ff2b4 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -17,7 +17,7 @@ "istanbul-lib-instrument": "^4.0.0-alpha.2", "istanbul-lib-report": "^3.0.0-alpha.1", "istanbul-lib-source-maps": "^4.0.0-alpha.4", - "istanbul-reports": "^3.0.0-alpha.4", + "istanbul-reports": "^3.0.0-alpha.5", "jest-haste-map": "^24.9.0", "jest-resolve": "^24.9.0", "jest-runtime": "^24.9.0", diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index 7fefd26508ac..d5cddfe1e69b 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -40,15 +40,15 @@ export default class CoverageReporter extends BaseReporter { this._options = options || {}; } - onTestResult( - _test: Test, - testResult: TestResult, - _aggregatedResults: AggregatedResult, - ) { + onTestResult(_test: Test, testResult: TestResult) { if (testResult.coverage) { this._coverageMap.merge(testResult.coverage); } + if (this._globalConfig.v8Coverage) { + return; + } + const sourceMaps = testResult.sourceMaps; if (sourceMaps) { Object.keys(sourceMaps).forEach(sourcePath => { @@ -76,7 +76,7 @@ export default class CoverageReporter extends BaseReporter { contexts: Set, aggregatedResults: AggregatedResult, ) { - await this._addUntestedFiles(this._globalConfig, contexts); + await this._addUntestedFiles(contexts); const map = await this._sourceMapStore.transformCoverage(this._coverageMap); try { @@ -92,7 +92,6 @@ export default class CoverageReporter extends BaseReporter { if (!this._globalConfig.useStderr && coverageReporters.length < 1) { coverageReporters.push('text-summary'); } - coverageReporters.forEach(reporter => { istanbulReports .create(reporter, {maxCols: process.stdout.columns || Infinity}) @@ -115,20 +114,20 @@ export default class CoverageReporter extends BaseReporter { this._checkThreshold(map); } - private async _addUntestedFiles( - globalConfig: Config.GlobalConfig, - contexts: Set, - ): Promise { + private async _addUntestedFiles(contexts: Set): Promise { const files: Array<{config: Config.ProjectConfig; path: string}> = []; contexts.forEach(context => { const config = context.config; if ( - globalConfig.collectCoverageFrom && - globalConfig.collectCoverageFrom.length + this._globalConfig.collectCoverageFrom && + this._globalConfig.collectCoverageFrom.length ) { context.hasteFS - .matchFilesWithGlob(globalConfig.collectCoverageFrom, config.rootDir) + .matchFilesWithGlob( + this._globalConfig.collectCoverageFrom, + config.rootDir, + ) .forEach(filePath => files.push({ config, @@ -168,7 +167,7 @@ export default class CoverageReporter extends BaseReporter { try { const result = await worker.worker({ config, - globalConfig, + globalConfig: this._globalConfig, options: { ...this._options, changedFiles: diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index c9420ff6bdc2..6e0c3acf61a3 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -27,6 +27,7 @@ export default function( collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, + v8Coverage: globalConfig.v8Coverage, }; let coverageWorkerResult: CoverageWorkerResult | null = null; if (shouldInstrument(filename, coverageOptions, config)) { diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 6786e0e172ae..d99f94dc0e72 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -157,6 +157,7 @@ async function runTestInternal( collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, + v8Coverage: globalConfig.v8Coverage, }); const start = Date.now(); @@ -224,6 +225,9 @@ async function runTestInternal( let result: TestResult; try { + if (globalConfig.v8Coverage) { + await runtime.collectV8Coverage(); + } result = await testFramework( globalConfig, config, @@ -231,6 +235,10 @@ async function runTestInternal( runtime, path, ); + + if (globalConfig.v8Coverage) { + await runtime.stopCollectingV8Coverage(); + } } catch (err) { // Access stack before uninstalling sourcemaps err.stack; @@ -252,7 +260,7 @@ async function runTestInternal( result.skipped = testCount === result.numPendingTests; result.displayName = config.displayName; - const coverage = runtime.getAllCoverageInfoCopy(); + const coverage = await runtime.getAllCoverageInfoCopy(); if (coverage) { const coverageKeys = Object.keys(coverage); if (coverageKeys.length) { diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 1915713a8dbf..44d01c730863 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -11,15 +11,18 @@ "types": "build/index.d.ts", "dependencies": { "@jest/console": "^24.7.1", + "@jest/coverage": "^24.9.0", "@jest/environment": "^24.9.0", "@jest/source-map": "^24.3.0", "@jest/transform": "^24.9.0", "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0", "@types/yargs": "^13.0.0", "chalk": "^3.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.3", + "istanbul-lib-coverage": "^3.0.0-alpha.1", "jest-config": "^24.9.0", "jest-haste-map": "^24.9.0", "jest-message-util": "^24.9.0", @@ -31,14 +34,17 @@ "jest-validate": "^24.9.0", "realpath-native": "^1.1.0", "slash": "^3.0.0", + "source-map": "^0.6.0", "strip-bom": "^4.0.0", - "yargs": "^15.0.0" + "yargs": "^15.0.0", + "v8-to-istanbul": "^4.0.0" }, "devDependencies": { "@types/exit": "^0.1.30", "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.2", - "jest-environment-node": "^24.9.0" + "jest-environment-node": "^24.9.0", + "source-map": "^0.6.0" }, "bin": { "jest-runtime": "./bin/jest-runtime.js" diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index a082739ba7fb..2ef4990b6a8c 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import {Script} from 'vm'; +import {fileURLToPath} from 'url'; import {Config} from '@jest/types'; import { Jest, @@ -26,17 +27,29 @@ import Snapshot = require('jest-snapshot'); import { ScriptTransformer, ShouldInstrumentOptions, + TransformResult, TransformationOptions, handlePotentialSyntaxError, shouldInstrument, } from '@jest/transform'; +import {CoverageInstrumenter, V8Coverage} from '@jest/coverage'; import * as fs from 'graceful-fs'; import stripBOM = require('strip-bom'); +import libCoverage = require('istanbul-lib-coverage'); +import v8toIstanbul = require('v8-to-istanbul'); +import {RawSourceMap} from 'source-map'; import {run as cliRun} from './cli'; import {options as cliOptions} from './cli/args'; import {findSiblingsWithFileExtension} from './helpers'; import {Context as JestContext} from './types'; +// This is fixed in a newer version, but that depends on Node 8 which is a +// breaking change (engine warning when installing) +interface FixedRawSourceMap extends Omit { + version: number; + file: string; +} + type HasteMapOptions = { console?: Console; maxWorkers: number; @@ -111,6 +124,9 @@ class Runtime { private _shouldUnmockTransitiveDependenciesCache: BooleanObject; private _sourceMapRegistry: SourceMapRegistry; private _scriptTransformer: ScriptTransformer; + private _fileTransforms: Map; + private _v8CoverageInstrumenter: CoverageInstrumenter | undefined; + private _v8CoverageResult: V8Coverage | undefined; private _transitiveShouldMock: BooleanObject; private _unmockList: RegExp | undefined; private _virtualMocks: BooleanObject; @@ -129,6 +145,7 @@ class Runtime { collectCoverage: false, collectCoverageFrom: [], collectCoverageOnlyFrom: undefined, + v8Coverage: false, }; this._currentlyExecutingModulePath = ''; this._environment = environment; @@ -147,6 +164,7 @@ class Runtime { this._scriptTransformer = new ScriptTransformer(config); this._shouldAutoMock = config.automock; this._sourceMapRegistry = Object.create(null); + this._fileTransforms = new Map(); this._virtualMocks = Object.create(null); this._mockMetaDataCache = Object.create(null); @@ -494,6 +512,7 @@ class Runtime { collectCoverage: this._coverageOptions.collectCoverage, collectCoverageFrom: this._coverageOptions.collectCoverageFrom, collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, + v8Coverage: this._coverageOptions.v8Coverage, }; } @@ -560,10 +579,81 @@ class Runtime { } } - getAllCoverageInfoCopy() { + async collectV8Coverage() { + this._v8CoverageInstrumenter = new CoverageInstrumenter(); + + await this._v8CoverageInstrumenter.startInstrumenting(); + } + + async stopCollectingV8Coverage() { + if (!this._v8CoverageInstrumenter) { + throw new Error('You need to call `collectV8Coverage` first.'); + } + this._v8CoverageResult = await this._v8CoverageInstrumenter.stopInstrumenting(); + } + + async getAllCoverageInfoCopy() { + if (this._v8CoverageResult) { + return this._mapV8CoverageToIstanbul(); + } return deepCyclicCopy(this._environment.global.__coverage__); } + private async _mapV8CoverageToIstanbul() { + if (!this._v8CoverageResult) { + throw new Error('You need to `stopCollectingV8Coverage` first'); + } + const filtered = this._v8CoverageResult + .filter(res => res.url.startsWith('file://')) + .map(res => ({...res, url: fileURLToPath(res.url)})) + .filter(res => this._fileTransforms.has(res.url)) + .filter(res => + shouldInstrument(res.url, this._coverageOptions, this._config), + ); + + const result = await Promise.all( + filtered.map(async res => { + // this is safe since we filter out those missing this above + const istanbulStuff = this._fileTransforms.get(res.url)!; + + let sourcemapContent: FixedRawSourceMap | undefined = undefined; + + if ( + istanbulStuff.sourceMapPath && + fs.existsSync(istanbulStuff.sourceMapPath) + ) { + sourcemapContent = JSON.parse( + fs.readFileSync(istanbulStuff.sourceMapPath, 'utf8'), + ); + } + + const converter = v8toIstanbul( + res.url, + istanbulStuff.scriptWrapperLength, + sourcemapContent + ? { + originalSource: istanbulStuff.rawContent, + source: istanbulStuff.scriptContent, + sourceMap: {sourcemap: sourcemapContent}, + } + : {source: istanbulStuff.scriptContent}, + ); + + await converter.load(); + + converter.applyCoverage(res.functions); + + return converter.toIstanbul(); + }), + ); + + const map = libCoverage.createCoverageMap({}); + + result.forEach(res => map.merge(res)); + + return map.toJSON(); + } + getSourceMapInfo(coveredFiles: Set) { return Object.keys(this._sourceMapRegistry).reduce<{ [path: string]: string; @@ -723,6 +813,8 @@ class Runtime { this._cacheFS[filename], ); + this._fileTransforms.set(filename, transformedFile); + if (transformedFile.sourceMapPath) { this._sourceMapRegistry[filename] = transformedFile.sourceMapPath; if (transformedFile.mapCoverage) { diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index 2892d4eb078d..81c5dedb488c 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -7,6 +7,7 @@ "references": [ {"path": "../jest-config"}, {"path": "../jest-console"}, + {"path": "../jest-coverage"}, {"path": "../jest-environment"}, {"path": "../jest-environment-node"}, {"path": "../jest-haste-map"}, diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 9ab22cac463e..ad8fd2292a23 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -373,6 +373,7 @@ export default class ScriptTransformer { return { code, + rawCode: content, mapCoverage, sourceMapPath, }; @@ -390,7 +391,9 @@ export default class ScriptTransformer { let instrument = false; if (!options.isCoreModule) { - instrument = shouldInstrument(filename, options, this._config); + instrument = + shouldInstrument(filename, options, this._config) && + !options.v8Coverage; scriptCacheKey = getScriptCacheKey(filename, instrument); const result = this._cache.transformedFiles.get(scriptCacheKey); if (result) { diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 392b79c9a410..73a36d70b1bf 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -14,5 +14,6 @@ export { Transformer, ShouldInstrumentOptions, Options as TransformationOptions, + TransformResult, } from './types'; export {default as handlePotentialSyntaxError} from './enhanceUnexpectedTokenMessage'; diff --git a/packages/jest-transform/src/shouldInstrument.ts b/packages/jest-transform/src/shouldInstrument.ts index 2d8b57da709a..305e30ab8868 100644 --- a/packages/jest-transform/src/shouldInstrument.ts +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -21,7 +21,7 @@ export default function shouldInstrument( options: ShouldInstrumentOptions, config: Config.ProjectConfig, ): boolean { - if (!options.collectCoverage) { + if (!options.collectCoverage && !options.v8Coverage) { return false; } diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 064ebf380cb9..ac7d0a90b0fb 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -10,7 +10,10 @@ import {Config} from '@jest/types'; export type ShouldInstrumentOptions = Pick< Config.GlobalConfig, - 'collectCoverage' | 'collectCoverageFrom' | 'collectCoverageOnlyFrom' + | 'collectCoverage' + | 'collectCoverageFrom' + | 'collectCoverageOnlyFrom' + | 'v8Coverage' > & {changedFiles?: Set}; export type Options = ShouldInstrumentOptions & @@ -35,6 +38,7 @@ export type TransformedSource = { export type TransformResult = { code: string; + rawCode: string; mapCoverage: boolean; sourceMapPath: string | null; }; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index aca89876a7c1..397576fd0178 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -76,6 +76,7 @@ export type DefaultOptions = { timers: 'real' | 'fake'; transformIgnorePatterns: Array; useStderr: boolean; + v8Coverage: boolean; watch: boolean; watchPathIgnorePatterns: Array; watchman: boolean; @@ -192,6 +193,7 @@ export type InitialOptions = Partial<{ [regex: string]: Path | TransformerConfig; }; transformIgnorePatterns: Array; + v8Coverage?: boolean; watchPathIgnorePatterns: Array; unmockedModulePathPatterns: Array; updateSnapshot: boolean; @@ -283,6 +285,7 @@ export type GlobalConfig = { updateSnapshot: SnapshotUpdateState; useStderr: boolean; verbose?: boolean; + v8Coverage: boolean; watch: boolean; watchAll: boolean; watchman: boolean; @@ -431,6 +434,7 @@ export type Argv = Arguments< useStderr: boolean; verbose: boolean; version: boolean; + v8Coverage: boolean; watch: boolean; watchAll: boolean; watchman: boolean; diff --git a/yarn.lock b/yarn.lock index 6c519f91318b..91209b69f1ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1944,7 +1944,7 @@ dependencies: "@types/ci-info" "*" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== @@ -4340,7 +4340,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.4.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -8212,10 +8212,10 @@ istanbul-lib-source-maps@^4.0.0-alpha.4: istanbul-lib-coverage "^3.0.0-alpha.1" source-map "^0.6.1" -istanbul-reports@^3.0.0-alpha.4: - version "3.0.0-alpha.4" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.0-alpha.4.tgz#f703c04cc76b27e89b88c4fa74efec7865582e73" - integrity sha512-TPIUdttWC1oUvTL163ZtS0FqTlYaaAQBQdlVpF9Enu+w6oPOUR0p//WRdRyT/hbDG83PoQoSyatYzV6FrICDKg== +istanbul-reports@^3.0.0-alpha.5: + version "3.0.0-alpha.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.0-alpha.5.tgz#e8eb61c0bfdc46cbac5857482c6fa40481c5068b" + integrity sha512-m70NY8mnytN+e3C/UN8xVZ3OXcWBT+yVtupNdYy7X/nCiSHV7O4ImojikbQ+EgqvKO6JaG+Qowk9lBq9j2pjbA== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0-alpha.1" @@ -14140,6 +14140,15 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== +v8-to-istanbul@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.0.0.tgz#6d420f519e82a5f9a74f2b7275ba9263e19ab80e" + integrity sha512-UqOveasq5AAXnhBo7Wg+PQUkNvCEOoocECuR6DA9CxfHEcCz24vnjH0lgeFGLVpGk12XEy8xup/MFBjxT6POrQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" From df460c71fa3b26b3274838a7915acebf59746e04 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 26 Nov 2019 09:53:04 +0100 Subject: [PATCH 02/26] merge all v8 coverage before transforming to istanbul data --- packages/jest-reporters/package.json | 5 +- .../jest-reporters/src/coverage_reporter.ts | 134 +++++++++++++++--- .../src/generateEmptyCoverage.ts | 37 ++++- packages/jest-reporters/tsconfig.json | 1 + packages/jest-runner/src/runTest.ts | 9 +- packages/jest-runtime/package.json | 10 +- packages/jest-runtime/src/index.ts | 66 ++------- packages/jest-runtime/tsconfig.json | 1 + packages/jest-test-result/package.json | 2 + packages/jest-test-result/src/index.ts | 1 + packages/jest-test-result/src/types.ts | 8 ++ packages/jest-test-result/tsconfig.json | 2 + yarn.lock | 5 + 13 files changed, 192 insertions(+), 89 deletions(-) diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index 01bee64ff2b4..0ab54b9ba8d6 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -5,7 +5,9 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^24.9.0", + "@jest/coverage": "^24.9.0", "@jest/environment": "^24.9.0", "@jest/test-result": "^24.9.0", "@jest/transform": "^24.9.0", @@ -26,7 +28,8 @@ "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^3.1.0", - "terminal-link": "^2.0.0" + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.0.0" }, "devDependencies": { "@types/exit": "^0.1.30", diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index d5cddfe1e69b..6d4416e0d78f 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -6,20 +6,35 @@ */ import * as path from 'path'; +import * as fs from 'fs'; import {Config} from '@jest/types'; -import {AggregatedResult, TestResult} from '@jest/test-result'; +import { + AggregatedResult, + TestResult, + V8CoverageResult, +} from '@jest/test-result'; import {clearLine, isInteractive} from 'jest-util'; import istanbulReport = require('istanbul-lib-report'); import istanbulReports = require('istanbul-reports'); import chalk = require('chalk'); import istanbulCoverage = require('istanbul-lib-coverage'); import libSourceMaps = require('istanbul-lib-source-maps'); +import {mergeProcessCovs} from '@bcoe/v8-coverage'; import Worker from 'jest-worker'; import glob = require('glob'); +import v8toIstanbul = require('v8-to-istanbul'); import {RawSourceMap} from 'source-map'; +import {TransformResult} from '@jest/transform'; import BaseReporter from './base_reporter'; import {Context, CoverageReporterOptions, CoverageWorker, Test} from './types'; +// This is fixed in a newer version, but that depends on Node 8 which is a +// breaking change (engine warning when installing) +interface FixedRawSourceMap extends Omit { + version: number; + file: string; +} + const FAIL_COLOR = chalk.bold.red; const RUNNING_TEST_COLOR = chalk.bold.dim; @@ -28,6 +43,7 @@ export default class CoverageReporter extends BaseReporter { private _globalConfig: Config.GlobalConfig; private _sourceMapStore: libSourceMaps.MapStore; private _options: CoverageReporterOptions; + private _v8CoverageResults: Array; constructor( globalConfig: Config.GlobalConfig, @@ -37,16 +53,18 @@ export default class CoverageReporter extends BaseReporter { this._coverageMap = istanbulCoverage.createCoverageMap({}); this._globalConfig = globalConfig; this._sourceMapStore = libSourceMaps.createSourceMapStore(); + this._v8CoverageResults = []; this._options = options || {}; } onTestResult(_test: Test, testResult: TestResult) { - if (testResult.coverage) { - this._coverageMap.merge(testResult.coverage); + if (testResult.v8Coverage) { + this._v8CoverageResults.push(testResult.v8Coverage); + return; } - if (this._globalConfig.v8Coverage) { - return; + if (testResult.coverage) { + this._coverageMap.merge(testResult.coverage); } const sourceMaps = testResult.sourceMaps; @@ -77,16 +95,9 @@ export default class CoverageReporter extends BaseReporter { aggregatedResults: AggregatedResult, ) { await this._addUntestedFiles(contexts); - const map = await this._sourceMapStore.transformCoverage(this._coverageMap); + const {map, reportContext} = await this._getCoverageResult(); try { - const reportContext = istanbulReport.createContext({ - // @ts-ignore - coverageMap: map, - dir: this._globalConfig.coverageDirectory, - // @ts-ignore - sourceFinder: this._sourceMapStore.sourceFinder, - }); const coverageReporters = this._globalConfig.coverageReporters || []; if (!this._globalConfig.useStderr && coverageReporters.length < 1) { @@ -178,10 +189,17 @@ export default class CoverageReporter extends BaseReporter { }); if (result) { - this._coverageMap.addFileCoverage(result.coverage); - - if (result.sourceMapPath) { - this._sourceMapStore.registerURL(filename, result.sourceMapPath); + if (this._globalConfig.v8Coverage) { + this._v8CoverageResults.push([{result}]); + } else { + this._coverageMap.addFileCoverage(result.coverage); + + if (result.sourceMapPath) { + this._sourceMapStore.registerURL( + filename, + result.sourceMapPath, + ); + } } } } catch (error) { @@ -404,8 +422,84 @@ export default class CoverageReporter extends BaseReporter { } } - // Only exposed for the internal runner. Should not be used - getCoverageMap(): istanbulCoverage.CoverageMap { - return this._coverageMap; + private async _getCoverageResult(): Promise<{ + map: istanbulCoverage.CoverageMap; + reportContext: istanbulReport.Context; + }> { + if (this._globalConfig.v8Coverage) { + const mergedCoverages = mergeProcessCovs( + this._v8CoverageResults.map(cov => ({result: cov.map(r => r.result)})), + ); + + const fileTransforms = new Map(); + + this._v8CoverageResults.forEach(res => + res.forEach(r => { + if (r.codeTransformResult && !fileTransforms.has(r.result.url)) { + fileTransforms.set(r.result.url, r.codeTransformResult); + } + }), + ); + + const transformedCoverage = await Promise.all( + mergedCoverages.result.map(async res => { + const fileTransform = fileTransforms.get(res.url); + + let sourcemapContent: FixedRawSourceMap | undefined = undefined; + + if ( + fileTransform && + fileTransform.sourceMapPath && + fs.existsSync(fileTransform.sourceMapPath) + ) { + sourcemapContent = JSON.parse( + fs.readFileSync(fileTransform.sourceMapPath, 'utf8'), + ); + } + + const converter = v8toIstanbul( + res.url, + fileTransform ? fileTransform.scriptWrapperLength : 0, + fileTransform && sourcemapContent + ? { + originalSource: fileTransform.rawContent, + source: fileTransform.scriptContent, + sourceMap: {sourcemap: sourcemapContent}, + } + : {source: fs.readFileSync(res.url, 'utf8')}, + ); + + await converter.load(); + + converter.applyCoverage(res.functions); + + return converter.toIstanbul(); + }), + ); + + const map = istanbulCoverage.createCoverageMap({}); + + transformedCoverage.forEach(res => map.merge(res)); + + const reportContext = istanbulReport.createContext({ + // @ts-ignore + coverageMap: map, + dir: this._globalConfig.coverageDirectory, + }); + + return {map, reportContext}; + } + + const map = await this._sourceMapStore.transformCoverage(this._coverageMap); + const reportContext = istanbulReport.createContext({ + // @ts-ignore + coverageMap: map, + dir: this._globalConfig.coverageDirectory, + // @ts-ignore + sourceFinder: this._sourceMapStore.sourceFinder, + }); + + // @ts-ignore + return {map, reportContext}; } } diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index 6e0c3acf61a3..dd830785187b 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -5,15 +5,21 @@ * LICENSE file in the root directory of this source tree. */ +import * as fs from 'fs'; import {Config} from '@jest/types'; import {readInitialCoverage} from 'istanbul-lib-instrument'; -import {createFileCoverage} from 'istanbul-lib-coverage'; +import {FileCoverage, createFileCoverage} from 'istanbul-lib-coverage'; import {ScriptTransformer, shouldInstrument} from '@jest/transform'; +import {V8Coverage} from '@jest/coverage'; -export type CoverageWorkerResult = { - coverage: any; - sourceMapPath?: string | null; -}; +type SingleV8Coverage = V8Coverage[number]; + +export type CoverageWorkerResult = + | { + coverage: FileCoverage; + sourceMapPath?: string | null; + } + | SingleV8Coverage; export default function( source: string, @@ -31,6 +37,27 @@ export default function( }; let coverageWorkerResult: CoverageWorkerResult | null = null; if (shouldInstrument(filename, coverageOptions, config)) { + if (coverageOptions.v8Coverage) { + const stat = fs.statSync(filename); + return { + functions: [ + { + functionName: '(empty-report)', + isBlockCoverage: true, + ranges: [ + { + count: 0, + endOffset: stat.size, + startOffset: 0, + }, + ], + }, + ], + scriptId: '0', + url: filename, + }; + } + // Transform file with instrumentation to make sure initial coverage data is well mapped to original code. const {code, mapCoverage, sourceMapPath} = new ScriptTransformer( config, diff --git a/packages/jest-reporters/tsconfig.json b/packages/jest-reporters/tsconfig.json index bdf1b1cdc9ee..d2bb28f2eb98 100644 --- a/packages/jest-reporters/tsconfig.json +++ b/packages/jest-reporters/tsconfig.json @@ -6,6 +6,7 @@ }, "references": [ {"path": "../jest-console"}, + {"path": "../jest-coverage"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-resolve"}, diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index d99f94dc0e72..5409ce3bd352 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -260,7 +260,7 @@ async function runTestInternal( result.skipped = testCount === result.numPendingTests; result.displayName = config.displayName; - const coverage = await runtime.getAllCoverageInfoCopy(); + const coverage = runtime.getAllCoverageInfoCopy(); if (coverage) { const coverageKeys = Object.keys(coverage); if (coverageKeys.length) { @@ -269,6 +269,13 @@ async function runTestInternal( } } + if (globalConfig.v8Coverage) { + const v8Coverage = runtime.getAllV8CoverageInfoCopy(); + if (v8Coverage && v8Coverage.length > 0) { + result.v8Coverage = v8Coverage; + } + } + if (globalConfig.logHeapUsage) { if (global.gc) { global.gc(); diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 44d01c730863..43106843c43d 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -14,15 +14,14 @@ "@jest/coverage": "^24.9.0", "@jest/environment": "^24.9.0", "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.9.0", "@jest/transform": "^24.9.0", "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0", "@types/yargs": "^13.0.0", "chalk": "^3.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.3", - "istanbul-lib-coverage": "^3.0.0-alpha.1", "jest-config": "^24.9.0", "jest-haste-map": "^24.9.0", "jest-message-util": "^24.9.0", @@ -34,17 +33,14 @@ "jest-validate": "^24.9.0", "realpath-native": "^1.1.0", "slash": "^3.0.0", - "source-map": "^0.6.0", "strip-bom": "^4.0.0", - "yargs": "^15.0.0", - "v8-to-istanbul": "^4.0.0" + "yargs": "^15.0.0" }, "devDependencies": { "@types/exit": "^0.1.30", "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.2", - "jest-environment-node": "^24.9.0", - "source-map": "^0.6.0" + "jest-environment-node": "^24.9.0" }, "bin": { "jest-runtime": "./bin/jest-runtime.js" diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 2ef4990b6a8c..c6a168c7cb96 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -32,24 +32,15 @@ import { handlePotentialSyntaxError, shouldInstrument, } from '@jest/transform'; +import {V8CoverageResult} from '@jest/test-result'; import {CoverageInstrumenter, V8Coverage} from '@jest/coverage'; import * as fs from 'graceful-fs'; import stripBOM = require('strip-bom'); -import libCoverage = require('istanbul-lib-coverage'); -import v8toIstanbul = require('v8-to-istanbul'); -import {RawSourceMap} from 'source-map'; import {run as cliRun} from './cli'; import {options as cliOptions} from './cli/args'; import {findSiblingsWithFileExtension} from './helpers'; import {Context as JestContext} from './types'; -// This is fixed in a newer version, but that depends on Node 8 which is a -// breaking change (engine warning when installing) -interface FixedRawSourceMap extends Omit { - version: number; - file: string; -} - type HasteMapOptions = { console?: Console; maxWorkers: number; @@ -592,17 +583,15 @@ class Runtime { this._v8CoverageResult = await this._v8CoverageInstrumenter.stopInstrumenting(); } - async getAllCoverageInfoCopy() { - if (this._v8CoverageResult) { - return this._mapV8CoverageToIstanbul(); - } + getAllCoverageInfoCopy() { return deepCyclicCopy(this._environment.global.__coverage__); } - private async _mapV8CoverageToIstanbul() { + getAllV8CoverageInfoCopy(): V8CoverageResult { if (!this._v8CoverageResult) { throw new Error('You need to `stopCollectingV8Coverage` first'); } + const filtered = this._v8CoverageResult .filter(res => res.url.startsWith('file://')) .map(res => ({...res, url: fileURLToPath(res.url)})) @@ -611,47 +600,14 @@ class Runtime { shouldInstrument(res.url, this._coverageOptions, this._config), ); - const result = await Promise.all( - filtered.map(async res => { - // this is safe since we filter out those missing this above - const istanbulStuff = this._fileTransforms.get(res.url)!; - - let sourcemapContent: FixedRawSourceMap | undefined = undefined; - - if ( - istanbulStuff.sourceMapPath && - fs.existsSync(istanbulStuff.sourceMapPath) - ) { - sourcemapContent = JSON.parse( - fs.readFileSync(istanbulStuff.sourceMapPath, 'utf8'), - ); - } - - const converter = v8toIstanbul( - res.url, - istanbulStuff.scriptWrapperLength, - sourcemapContent - ? { - originalSource: istanbulStuff.rawContent, - source: istanbulStuff.scriptContent, - sourceMap: {sourcemap: sourcemapContent}, - } - : {source: istanbulStuff.scriptContent}, - ); - - await converter.load(); - - converter.applyCoverage(res.functions); - - return converter.toIstanbul(); - }), - ); - - const map = libCoverage.createCoverageMap({}); - - result.forEach(res => map.merge(res)); + return filtered.map(result => { + const transformedFile = this._fileTransforms.get(result.url); - return map.toJSON(); + return { + codeTransformResult: transformedFile, + result, + }; + }); } getSourceMapInfo(coveredFiles: Set) { diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index 81c5dedb488c..2f18ec60dfad 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -17,6 +17,7 @@ {"path": "../jest-resolve"}, {"path": "../jest-snapshot"}, {"path": "../jest-source-map"}, + {"path": "../jest-test-result"}, {"path": "../jest-types"}, {"path": "../jest-util"}, {"path": "../jest-validate"}, diff --git a/packages/jest-test-result/package.json b/packages/jest-test-result/package.json index cf0b720e565c..4bada0fff0e1 100644 --- a/packages/jest-test-result/package.json +++ b/packages/jest-test-result/package.json @@ -11,6 +11,8 @@ "types": "build/index.d.ts", "dependencies": { "@jest/console": "^24.9.0", + "@jest/coverage": "^24.9.0", + "@jest/transform": "^24.9.0", "@jest/types": "^24.9.0", "@types/istanbul-lib-coverage": "^2.0.0" }, diff --git a/packages/jest-test-result/src/index.ts b/packages/jest-test-result/src/index.ts index b5feb16ae93e..335fc89d1932 100644 --- a/packages/jest-test-result/src/index.ts +++ b/packages/jest-test-result/src/index.ts @@ -24,4 +24,5 @@ export { Status, Suite, TestResult, + V8CoverageResult, } from './types'; diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index a9427cd7abb2..e46e3e4e2140 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -9,6 +9,13 @@ import {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage'; import {ConsoleBuffer} from '@jest/console'; import {Config} from '@jest/types'; +import {V8Coverage} from '@jest/coverage'; +import {TransformResult} from '@jest/transform'; + +export type V8CoverageResult = Array<{ + codeTransformResult: TransformResult | undefined; + result: V8Coverage[number]; +}>; export type SerializableError = { code?: unknown; @@ -132,6 +139,7 @@ export type TestResult = { testExecError?: SerializableError; testFilePath: string; testResults: Array; + v8Coverage?: V8CoverageResult; }; export type FormattedTestResult = { diff --git a/packages/jest-test-result/tsconfig.json b/packages/jest-test-result/tsconfig.json index 4a3022904a19..fa018f248c80 100644 --- a/packages/jest-test-result/tsconfig.json +++ b/packages/jest-test-result/tsconfig.json @@ -6,6 +6,8 @@ }, "references": [ {"path": "../jest-console"}, + {"path": "../jest-coverage"}, + {"path": "../jest-transform"}, {"path": "../jest-types"} ] } diff --git a/yarn.lock b/yarn.lock index 91209b69f1ea..8c1c30528d9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -941,6 +941,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" From 14f8b2c89c207bb3262f0fc0bd27060222dbca8b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 3 Dec 2019 00:05:08 +0100 Subject: [PATCH 03/26] update --- packages/jest-reporters/src/coverage_reporter.ts | 16 ++++++++++++---- packages/jest-runner/src/runTest.ts | 15 ++++++++++----- packages/jest-transform/src/ScriptTransformer.ts | 2 +- packages/jest-transform/src/types.ts | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index 6d4416e0d78f..cc5d7efe685d 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -174,7 +174,15 @@ export default class CoverageReporter extends BaseReporter { const filename = fileObj.path; const config = fileObj.config; - if (!this._coverageMap.data[filename] && 'worker' in worker) { + const hasCoverageData = this._v8CoverageResults.some(v8Res => + v8Res.some(innerRes => innerRes.result.url === filename), + ); + + if ( + !hasCoverageData && + !this._coverageMap.data[filename] && + 'worker' in worker + ) { try { const result = await worker.worker({ config, @@ -459,11 +467,11 @@ export default class CoverageReporter extends BaseReporter { const converter = v8toIstanbul( res.url, - fileTransform ? fileTransform.scriptWrapperLength : 0, + 0, fileTransform && sourcemapContent ? { - originalSource: fileTransform.rawContent, - source: fileTransform.scriptContent, + originalSource: fileTransform.originalCode, + source: fileTransform.code, sourceMap: {sourcemap: sourcemapContent}, } : {source: fs.readFileSync(res.url, 'utf8')}, diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 5409ce3bd352..51b8283bebce 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -219,13 +219,18 @@ async function runTestInternal( }; } + // if we don't have `compileFunction` on the env, skip coverage + const collectV8Coverage = + globalConfig.v8Coverage && + typeof environment.compileFunction === 'function'; + try { await environment.setup(); let result: TestResult; try { - if (globalConfig.v8Coverage) { + if (collectV8Coverage) { await runtime.collectV8Coverage(); } result = await testFramework( @@ -235,15 +240,15 @@ async function runTestInternal( runtime, path, ); - - if (globalConfig.v8Coverage) { - await runtime.stopCollectingV8Coverage(); - } } catch (err) { // Access stack before uninstalling sourcemaps err.stack; throw err; + } finally { + if (collectV8Coverage) { + await runtime.stopCollectingV8Coverage(); + } } freezeConsole(testConsole, config); diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index ad8fd2292a23..5a808f7aa08d 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -373,8 +373,8 @@ export default class ScriptTransformer { return { code, - rawCode: content, mapCoverage, + originalCode: content, sourceMapPath, }; } catch (e) { diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index ac7d0a90b0fb..0d013e64456e 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -38,7 +38,7 @@ export type TransformedSource = { export type TransformResult = { code: string; - rawCode: string; + originalCode: string; mapCoverage: boolean; sourceMapPath: string | null; }; From 66a1e7f11b23636f93796b00f51352ab20c72099 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 3 Dec 2019 00:25:24 +0100 Subject: [PATCH 04/26] fix type errors --- .../jest-reporters/src/coverage_reporter.ts | 8 ++-- .../src/generateEmptyCoverage.ts | 40 +++++++++++-------- packages/jest-runner/src/runTest.ts | 2 +- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index cc5d7efe685d..34304a0c0871 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -197,8 +197,10 @@ export default class CoverageReporter extends BaseReporter { }); if (result) { - if (this._globalConfig.v8Coverage) { - this._v8CoverageResults.push([{result}]); + if (result.kind === 'V8Coverage') { + this._v8CoverageResults.push([ + {codeTransformResult: undefined, result: result.result}, + ]); } else { this._coverageMap.addFileCoverage(result.coverage); @@ -235,7 +237,7 @@ export default class CoverageReporter extends BaseReporter { } if (worker && 'end' in worker && typeof worker.end === 'function') { - worker.end(); + await worker.end(); } } diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index dd830785187b..7738b72f9986 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -16,10 +16,14 @@ type SingleV8Coverage = V8Coverage[number]; export type CoverageWorkerResult = | { + kind: 'BabelCoverage'; coverage: FileCoverage; sourceMapPath?: string | null; } - | SingleV8Coverage; + | { + kind: 'V8Coverage'; + result: SingleV8Coverage; + }; export default function( source: string, @@ -40,21 +44,24 @@ export default function( if (coverageOptions.v8Coverage) { const stat = fs.statSync(filename); return { - functions: [ - { - functionName: '(empty-report)', - isBlockCoverage: true, - ranges: [ - { - count: 0, - endOffset: stat.size, - startOffset: 0, - }, - ], - }, - ], - scriptId: '0', - url: filename, + kind: 'V8Coverage', + result: { + functions: [ + { + functionName: '(empty-report)', + isBlockCoverage: true, + ranges: [ + { + count: 0, + endOffset: stat.size, + startOffset: 0, + }, + ], + }, + ], + scriptId: '0', + url: filename, + }, }; } @@ -68,6 +75,7 @@ export default function( if (extracted) { coverageWorkerResult = { coverage: createFileCoverage(extracted.coverageData), + kind: 'BabelCoverage', sourceMapPath: mapCoverage ? sourceMapPath : null, }; } diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 51b8283bebce..66c8cc29ef52 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -274,7 +274,7 @@ async function runTestInternal( } } - if (globalConfig.v8Coverage) { + if (collectV8Coverage) { const v8Coverage = runtime.getAllV8CoverageInfoCopy(); if (v8Coverage && v8Coverage.length > 0) { result.v8Coverage = v8Coverage; From 587bea3ab2325d1522e73646f126964c4abe8304 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 3 Dec 2019 00:36:29 +0100 Subject: [PATCH 05/26] copyright headers --- e2e/v8-coverage/no-sourcemap/Thing.js | 7 +++++++ e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js | 7 +++++++ e2e/v8-coverage/no-sourcemap/cssTransform.js | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/e2e/v8-coverage/no-sourcemap/Thing.js b/e2e/v8-coverage/no-sourcemap/Thing.js index bf3fd4cda78c..95f6bd8cc066 100644 --- a/e2e/v8-coverage/no-sourcemap/Thing.js +++ b/e2e/v8-coverage/no-sourcemap/Thing.js @@ -1,3 +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. + */ + require('./x.css'); module.exports = 42; diff --git a/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js b/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js index c68cb23eae5a..aa7f6940580f 100644 --- a/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js +++ b/e2e/v8-coverage/no-sourcemap/__tests__/Thing.test.js @@ -1,3 +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. + */ + const Thing = require('../Thing'); console.log(Thing); diff --git a/e2e/v8-coverage/no-sourcemap/cssTransform.js b/e2e/v8-coverage/no-sourcemap/cssTransform.js index 0d630053a37c..c973ad34fe44 100644 --- a/e2e/v8-coverage/no-sourcemap/cssTransform.js +++ b/e2e/v8-coverage/no-sourcemap/cssTransform.js @@ -1,3 +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. + */ + module.exports = { getCacheKey: () => 'cssTransform', process: () => 'module.exports = {};', From 4cbe3e5febce4a0354e81f510a4a112cc226fe92 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 3 Dec 2019 00:54:13 +0100 Subject: [PATCH 06/26] update test --- e2e/__tests__/v8Coverage.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 43bbe5528353..20cb8ce6b1e0 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -12,10 +12,10 @@ const DIR = path.resolve(__dirname, '../v8-coverage'); test('does not explode on missing sourcemap', () => { const sourcemapDir = path.join(DIR, 'no-sourcemap'); - const {stderr, status} = runJest(sourcemapDir, ['--v8-coverage'], { + const {stderr, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { stripAnsi: true, }); expect(stderr).not.toContain('no such file or directory'); - expect(status).toBe(0); + expect(exitCode).toBe(0); }); From 839bfabb10cfdffc67320555184873d1f1c8c0e5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 8 Dec 2019 10:11:11 +0100 Subject: [PATCH 07/26] add some tests --- e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap | 13 +++++++++++++ e2e/__tests__/v8Coverage.test.ts | 7 ++++--- e2e/v8-coverage/no-sourcemap/package.json | 2 ++ packages/jest-cli/src/__tests__/cli/args.test.ts | 7 +++++++ .../__tests__/__snapshots__/normalize.test.js.snap | 10 ++++++++++ .../jest-config/src/__tests__/normalize.test.js | 13 +++++++++++++ packages/jest-runtime/src/index.ts | 1 - 7 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap diff --git a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap new file mode 100644 index 000000000000..ef8a5c2fe6a7 --- /dev/null +++ b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`does not explode on missing sourcemap 1`] = ` + console.log __tests__/Thing.test.js:10 + 42 + +----------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +----------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 100 | 100 | + Thing.js | 100 | 100 | 100 | 100 | +----------|---------|----------|---------|---------|------------------- +`; diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 20cb8ce6b1e0..6d0c7b483fde 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -5,17 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -import path from 'path'; +import * as path from 'path'; +import {wrap} from 'jest-snapshot-serializer-raw'; import runJest from '../runJest'; const DIR = path.resolve(__dirname, '../v8-coverage'); test('does not explode on missing sourcemap', () => { const sourcemapDir = path.join(DIR, 'no-sourcemap'); - const {stderr, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { + const {stdout, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { stripAnsi: true, }); - expect(stderr).not.toContain('no such file or directory'); expect(exitCode).toBe(0); + expect(wrap(stdout)).toMatchSnapshot(); }); diff --git a/e2e/v8-coverage/no-sourcemap/package.json b/e2e/v8-coverage/no-sourcemap/package.json index 96f7c313aa0c..88ef243b361c 100644 --- a/e2e/v8-coverage/no-sourcemap/package.json +++ b/e2e/v8-coverage/no-sourcemap/package.json @@ -2,6 +2,8 @@ "name": "no-sourcemap", "version": "1.0.0", "jest": { + "collectCoverageFrom": ["Thing.js"], + "testEnvironment": "node", "transform": { "^.+\\.[jt]sx?$": "babel-jest", "^.+\\.css$": "/cssTransform.js" diff --git a/packages/jest-cli/src/__tests__/cli/args.test.ts b/packages/jest-cli/src/__tests__/cli/args.test.ts index dfe1c80b4f02..9147555d3cf2 100644 --- a/packages/jest-cli/src/__tests__/cli/args.test.ts +++ b/packages/jest-cli/src/__tests__/cli/args.test.ts @@ -65,6 +65,13 @@ describe('check', () => { 'The --config option requires a JSON string literal, or a file path with a .js or .json extension', ); }); + + it('raises an exception if both coverage and v8-coverage is specified', () => { + const argv = {collectCoverage: true, v8Coverage: true} as Config.Argv; + expect(() => check(argv)).toThrow( + 'Both --coverage and --v8Coverage were specified, but these two options do not make sense together. Which is it?', + ); + }); }); describe('buildArgv', () => { diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap index 29f8228c7c3d..d9fbbe328a1c 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap @@ -143,6 +143,16 @@ exports[`setupTestFrameworkScriptFile logs a deprecation warning when \`setupTes " `; +exports[`setupTestFrameworkScriptFile logs an error when \`collectCoverage\` and \`v8COverage\` are used 1`] = ` +"Validation Error: + + Configuration options collectCoverage and v8Coverage cannot be used together. + + Configuration Documentation: + https://jestjs.io/docs/configuration.html +" +`; + exports[`setupTestFrameworkScriptFile logs an error when \`setupTestFrameworkScriptFile\` and \`setupFilesAfterEnv\` are used 1`] = ` "Validation Error: diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index 26bf21bb3a8d..cfd5e18dbc34 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -458,6 +458,19 @@ describe('setupTestFrameworkScriptFile', () => { ), ).toThrowErrorMatchingSnapshot(); }); + + it('logs an error when `collectCoverage` and `v8COverage` are used', () => { + expect(() => + normalize( + { + collectCoverage: true, + rootDir: '/root/path/foo', + v8Coverage: true, + }, + {}, + ), + ).toThrowErrorMatchingSnapshot(); + }); }); describe('coveragePathIgnorePatterns', () => { diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index c6a168c7cb96..818582c70e48 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -595,7 +595,6 @@ class Runtime { const filtered = this._v8CoverageResult .filter(res => res.url.startsWith('file://')) .map(res => ({...res, url: fileURLToPath(res.url)})) - .filter(res => this._fileTransforms.has(res.url)) .filter(res => shouldInstrument(res.url, this._coverageOptions, this._config), ); From 00436c9365f7802e2e4c008a806b732e2b4aaf8b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 8 Dec 2019 10:25:21 +0100 Subject: [PATCH 08/26] docs --- docs/CLI.md | 12 ++++++++++++ docs/Configuration.md | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/docs/CLI.md b/docs/CLI.md index de9bee433d67..89138da4a458 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -152,6 +152,8 @@ Alias: `-c`. The path to a Jest config file specifying how to find and execute t Alias: `--collectCoverage`. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. +Cannot be used together with `--v8-coverage`. + ### `--debug` Print debugging info about your Jest config. @@ -317,6 +319,16 @@ Divert all output to stderr. Display individual test results with the test suite hierarchy. +### `--v8-coverage[=]` + +> _Experimental_ + +Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. + +This uses V8's builtin code coverage rather than one based on Babel. Note that it only works in Node test environments. + +Cannot be used together with `--coverage`. + ### `--version` Alias: `-v`. Print the version and exit. diff --git a/docs/Configuration.md b/docs/Configuration.md index 709686c84c38..2451418bef78 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -131,6 +131,8 @@ Default: `false` Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. +Cannot be used together with `v8Coverage`. + ### `collectCoverageFrom` [array] Default: `undefined` @@ -1121,6 +1123,16 @@ This is useful for some commonly used 'utility' modules that are almost always u It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. +### `v8Coverage` [boolean] + +> _Experimental_ + +Indicates whether the coverage information should be collected while executing the test. + +This uses V8's builtin code coverage rather than one based on Babel. Note that it only works in Node test environments. + +Cannot be used together with `collectCoverage`. + ### `verbose` [boolean] Default: `false` From c80ef46093b1b93409609a26b73e68e421805541 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 8 Dec 2019 10:55:52 +0100 Subject: [PATCH 09/26] skip test on old nodes --- .../__snapshots__/v8Coverage.test.ts.snap | 2 +- e2e/__tests__/v8Coverage.test.ts | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap index ef8a5c2fe6a7..c37264b2c57d 100644 --- a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap +++ b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`does not explode on missing sourcemap 1`] = ` +exports[`prints coverage 1`] = ` console.log __tests__/Thing.test.js:10 42 diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 6d0c7b483fde..b5fd11c3cfe0 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -7,16 +7,19 @@ import * as path from 'path'; import {wrap} from 'jest-snapshot-serializer-raw'; +import {onNodeVersions} from '@jest/test-utils'; import runJest from '../runJest'; const DIR = path.resolve(__dirname, '../v8-coverage'); -test('does not explode on missing sourcemap', () => { - const sourcemapDir = path.join(DIR, 'no-sourcemap'); - const {stdout, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { - stripAnsi: true, - }); +onNodeVersions('>=10', () => { + test('prints coverage', () => { + const sourcemapDir = path.join(DIR, 'no-sourcemap'); + const {stdout, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { + stripAnsi: true, + }); - expect(exitCode).toBe(0); - expect(wrap(stdout)).toMatchSnapshot(); + expect(exitCode).toBe(0); + expect(wrap(stdout)).toMatchSnapshot(); + }); }); From eec6fa2586395dd7d8fca81151d6a4fcedd58991 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 8 Dec 2019 11:00:51 +0100 Subject: [PATCH 10/26] clarify caveats --- docs/CLI.md | 5 ++++- docs/Configuration.md | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index 89138da4a458..8cd50c0abb2f 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -325,7 +325,10 @@ Display individual test results with the test suite hierarchy. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. -This uses V8's builtin code coverage rather than one based on Babel. Note that it only works in Node test environments. +This uses V8's builtin code coverage rather than one based on Babel. Note that there are a few caveats + +1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) +1. Tests needs to run in Node test environment (support for jsdom is in the works) Cannot be used together with `--coverage`. diff --git a/docs/Configuration.md b/docs/Configuration.md index 2451418bef78..a821888891e7 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1129,7 +1129,10 @@ It is possible to override this setting in individual tests by explicitly callin Indicates whether the coverage information should be collected while executing the test. -This uses V8's builtin code coverage rather than one based on Babel. Note that it only works in Node test environments. +This uses V8's builtin code coverage rather than one based on Babel. Note that there are a few caveats + +1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) +1. Tests needs to run in Node test environment (support for jsdom is in the works) Cannot be used together with `collectCoverage`. From b5ff1abb858f649c6a1ca01696285b7123cc05fc Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 9 Dec 2019 09:43:19 +0100 Subject: [PATCH 11/26] [wip] use coverageProvider option instead --- TestUtils.ts | 2 +- .../__snapshots__/showConfig.test.ts.snap | 2 +- e2e/__tests__/v8Coverage.test.ts | 10 +++++++--- .../jest-cli/src/__tests__/cli/args.test.ts | 7 ------- packages/jest-cli/src/cli/args.ts | 17 +++++------------ packages/jest-config/src/Defaults.ts | 2 +- packages/jest-config/src/ValidConfig.ts | 2 +- .../__snapshots__/normalize.test.js.snap | 10 ---------- .../jest-config/src/__tests__/normalize.test.js | 13 ------------- packages/jest-config/src/index.ts | 2 +- packages/jest-config/src/normalize.ts | 14 ++------------ packages/jest-core/src/TestScheduler.ts | 7 +++---- .../log_debug_messages.test.ts.snap | 2 +- .../jest-reporters/src/coverage_reporter.ts | 5 ++--- .../jest-reporters/src/generateEmptyCoverage.ts | 4 ++-- packages/jest-runner/src/runTest.ts | 4 ++-- packages/jest-runtime/src/index.ts | 4 ++-- .../jest-transform/src/ScriptTransformer.ts | 4 ++-- packages/jest-transform/src/shouldInstrument.ts | 2 +- packages/jest-transform/src/types.ts | 6 ++++-- packages/jest-types/src/Config.ts | 9 +++++---- 21 files changed, 43 insertions(+), 85 deletions(-) diff --git a/TestUtils.ts b/TestUtils.ts index ba901c2de4b9..1398da6b1ba3 100644 --- a/TestUtils.ts +++ b/TestUtils.ts @@ -16,6 +16,7 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { collectCoverageFrom: [], collectCoverageOnlyFrom: null, coverageDirectory: 'coverage', + coverageProvider: 'babel', coverageReporters: [], coverageThreshold: {global: {}}, detectLeaks: false, @@ -58,7 +59,6 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { testTimeout: 5000, updateSnapshot: 'none', useStderr: false, - v8Coverage: false, verbose: false, watch: false, watchAll: false, diff --git a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap index 9bdfd06e8d09..06206fe33f9f 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -12,6 +12,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` "coveragePathIgnorePatterns": [ "/node_modules/" ], + "coverageProvider": "babel", "cwd": "<>", "detectLeaks": false, "detectOpenHandles": false, @@ -118,7 +119,6 @@ exports[`--showConfig outputs config info and exits 1`] = ` "testSequencer": "<>/jest-test-sequencer/build/index.js", "updateSnapshot": "all", "useStderr": false, - "v8Coverage": false, "watch": false, "watchAll": false, "watchman": true diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index b5fd11c3cfe0..0d74fab85554 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -15,9 +15,13 @@ const DIR = path.resolve(__dirname, '../v8-coverage'); onNodeVersions('>=10', () => { test('prints coverage', () => { const sourcemapDir = path.join(DIR, 'no-sourcemap'); - const {stdout, exitCode} = runJest(sourcemapDir, ['--v8-coverage'], { - stripAnsi: true, - }); + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'v8'], + { + stripAnsi: true, + }, + ); expect(exitCode).toBe(0); expect(wrap(stdout)).toMatchSnapshot(); diff --git a/packages/jest-cli/src/__tests__/cli/args.test.ts b/packages/jest-cli/src/__tests__/cli/args.test.ts index 9147555d3cf2..dfe1c80b4f02 100644 --- a/packages/jest-cli/src/__tests__/cli/args.test.ts +++ b/packages/jest-cli/src/__tests__/cli/args.test.ts @@ -65,13 +65,6 @@ describe('check', () => { 'The --config option requires a JSON string literal, or a file path with a .js or .json extension', ); }); - - it('raises an exception if both coverage and v8-coverage is specified', () => { - const argv = {collectCoverage: true, v8Coverage: true} as Config.Argv; - expect(() => check(argv)).toThrow( - 'Both --coverage and --v8Coverage were specified, but these two options do not make sense together. Which is it?', - ); - }); }); describe('buildArgv', () => { diff --git a/packages/jest-cli/src/cli/args.ts b/packages/jest-cli/src/cli/args.ts index a43c4368cb55..a0d3f191b722 100644 --- a/packages/jest-cli/src/cli/args.ts +++ b/packages/jest-cli/src/cli/args.ts @@ -17,13 +17,6 @@ export const check = (argv: Config.Argv) => { ); } - if (argv.collectCoverage && argv.v8Coverage) { - throw new Error( - 'Both --coverage and --v8Coverage were specified, but these two ' + - 'options do not make sense together. Which is it?', - ); - } - for (const key of [ 'onlyChanged', 'lastCommit', @@ -209,6 +202,11 @@ export const options = { string: true, type: 'array', }, + coverageProvider: { + choices: ['babel', 'v8'], + default: 'babel', + description: 'Select between Babel and V8 to collect coverage', + }, coverageReporters: { description: 'A list of reporter names that Jest uses when writing ' + @@ -682,11 +680,6 @@ export const options = { description: 'Divert all output to stderr.', type: 'boolean', }, - v8Coverage: { - default: false, - description: 'Collect coverage using V8 instrumentation', - type: 'boolean' as 'boolean', - }, verbose: { default: undefined, description: diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index 24f18bdb367d..cf0e205cab59 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -23,6 +23,7 @@ const defaultOptions: Config.DefaultOptions = { collectCoverage: false, coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], coverageReporters: ['json', 'text', 'lcov', 'clover'], + coverageProvider: 'babel', errorOnDeprecated: false, expand: false, forceCoverageMatch: [], @@ -65,7 +66,6 @@ const defaultOptions: Config.DefaultOptions = { timers: 'real', transformIgnorePatterns: [NODE_MODULES_REGEXP], useStderr: false, - v8Coverage: false, watch: false, watchPathIgnorePatterns: [], watchman: true, diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 6e6f0a39ee31..b07cdb441f2c 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -28,6 +28,7 @@ const initialOptions: Config.InitialOptions = { }, coverageDirectory: 'coverage', coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], + coverageProvider: 'v8', coverageReporters: ['json', 'text', 'lcov', 'clover'], coverageThreshold: { global: { @@ -124,7 +125,6 @@ const initialOptions: Config.InitialOptions = { unmockedModulePathPatterns: ['mock'], updateSnapshot: true, useStderr: false, - v8Coverage: false, verbose: false, watch: false, watchPathIgnorePatterns: ['/e2e/'], diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap index d9fbbe328a1c..29f8228c7c3d 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap @@ -143,16 +143,6 @@ exports[`setupTestFrameworkScriptFile logs a deprecation warning when \`setupTes " `; -exports[`setupTestFrameworkScriptFile logs an error when \`collectCoverage\` and \`v8COverage\` are used 1`] = ` -"Validation Error: - - Configuration options collectCoverage and v8Coverage cannot be used together. - - Configuration Documentation: - https://jestjs.io/docs/configuration.html -" -`; - exports[`setupTestFrameworkScriptFile logs an error when \`setupTestFrameworkScriptFile\` and \`setupFilesAfterEnv\` are used 1`] = ` "Validation Error: diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index cfd5e18dbc34..26bf21bb3a8d 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -458,19 +458,6 @@ describe('setupTestFrameworkScriptFile', () => { ), ).toThrowErrorMatchingSnapshot(); }); - - it('logs an error when `collectCoverage` and `v8COverage` are used', () => { - expect(() => - normalize( - { - collectCoverage: true, - rootDir: '/root/path/foo', - v8Coverage: true, - }, - {}, - ), - ).toThrowErrorMatchingSnapshot(); - }); }); describe('coveragePathIgnorePatterns', () => { diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 44beb9ac4348..552ba860af37 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -110,6 +110,7 @@ const groupOptions = ( collectCoverageFrom: options.collectCoverageFrom, collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, coverageDirectory: options.coverageDirectory, + coverageProvider: options.coverageProvider, coverageReporters: options.coverageReporters, coverageThreshold: options.coverageThreshold, detectLeaks: options.detectLeaks, @@ -152,7 +153,6 @@ const groupOptions = ( testTimeout: options.testTimeout, updateSnapshot: options.updateSnapshot, useStderr: options.useStderr, - v8Coverage: options.v8Coverage, verbose: options.verbose, watch: options.watch, watchAll: options.watchAll, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index f90940a3b385..f0cf0213eee0 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -836,6 +836,7 @@ export default function normalize( case 'changedFilesWithAncestor': case 'clearMocks': case 'collectCoverage': + case 'coverageProvider': case 'coverageReporters': case 'coverageThreshold': case 'detectLeaks': @@ -878,7 +879,6 @@ export default function normalize( case 'timers': case 'useStderr': case 'verbose': - case 'v8Coverage': case 'watch': case 'watchAll': case 'watchman': @@ -1010,10 +1010,7 @@ export default function normalize( // Is transformed to: `--findRelatedTests '/rootDir/file1.js' --coverage --collectCoverageFrom 'file1.js'` // where arguments to `--collectCoverageFrom` should be globs (or relative // paths to the rootDir) - if ( - (newOptions.collectCoverage || newOptions.v8Coverage) && - argv.findRelatedTests - ) { + if (newOptions.collectCoverage && argv.findRelatedTests) { let collectCoverageFrom = argv._.map(filename => { filename = replaceRootDirInPath(options.rootDir, filename); return path.isAbsolute(filename) @@ -1061,13 +1058,6 @@ export default function normalize( newOptions.logHeapUsage = false; } - if (newOptions.collectCoverage && newOptions.v8Coverage) { - throw createConfigError( - ` Configuration options ${chalk.bold('collectCoverage')} and` + - ` ${chalk.bold('v8Coverage')} cannot be used together.`, - ); - } - return { hasDeprecationWarnings, options: newOptions, diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index e6a20d8f7be3..58a141136ea4 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -259,15 +259,14 @@ export default class TestScheduler { } private _setupReporters() { - const {collectCoverage, notify, reporters, v8Coverage} = this._globalConfig; + const {collectCoverage, notify, reporters} = this._globalConfig; const isDefault = this._shouldAddDefaultReporters(reporters); - const willCollectCoverage = collectCoverage || v8Coverage; if (isDefault) { - this._setupDefaultReporters(willCollectCoverage); + this._setupDefaultReporters(collectCoverage); } - if (!isDefault && willCollectCoverage) { + if (!isDefault && collectCoverage) { this.addReporter( new CoverageReporter(this._globalConfig, { changedFiles: this._context && this._context.changedFiles, diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap index 8df22b2a5daa..3be6c4ebb716 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.ts.snap @@ -71,6 +71,7 @@ exports[`prints the config object 1`] = ` "collectCoverageFrom": [], "collectCoverageOnlyFrom": null, "coverageDirectory": "coverage", + "coverageProvider": "babel", "coverageReporters": [], "coverageThreshold": { "global": {} @@ -115,7 +116,6 @@ exports[`prints the config object 1`] = ` "testTimeout": 5000, "updateSnapshot": "none", "useStderr": false, - "v8Coverage": false, "verbose": false, "watch": true, "watchAll": false, diff --git a/packages/jest-reporters/src/coverage_reporter.ts b/packages/jest-reporters/src/coverage_reporter.ts index 34304a0c0871..39025a4e5e51 100644 --- a/packages/jest-reporters/src/coverage_reporter.ts +++ b/packages/jest-reporters/src/coverage_reporter.ts @@ -28,8 +28,7 @@ import {TransformResult} from '@jest/transform'; import BaseReporter from './base_reporter'; import {Context, CoverageReporterOptions, CoverageWorker, Test} from './types'; -// This is fixed in a newer version, but that depends on Node 8 which is a -// breaking change (engine warning when installing) +// This is fixed in a newer versions of source-map, but our dependencies are still stuck on old versions interface FixedRawSourceMap extends Omit { version: number; file: string; @@ -436,7 +435,7 @@ export default class CoverageReporter extends BaseReporter { map: istanbulCoverage.CoverageMap; reportContext: istanbulReport.Context; }> { - if (this._globalConfig.v8Coverage) { + if (this._globalConfig.coverageProvider === 'v8') { const mergedCoverages = mergeProcessCovs( this._v8CoverageResults.map(cov => ({result: cov.map(r => r.result)})), ); diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index 7738b72f9986..633c18b2f7b2 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -37,11 +37,11 @@ export default function( collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, - v8Coverage: globalConfig.v8Coverage, + coverageProvider: globalConfig.coverageProvider, }; let coverageWorkerResult: CoverageWorkerResult | null = null; if (shouldInstrument(filename, coverageOptions, config)) { - if (coverageOptions.v8Coverage) { + if (coverageOptions.coverageProvider === 'v8') { const stat = fs.statSync(filename); return { kind: 'V8Coverage', diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 66c8cc29ef52..5d0232a28c08 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -157,7 +157,7 @@ async function runTestInternal( collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, - v8Coverage: globalConfig.v8Coverage, + coverageProvider: globalConfig.coverageProvider, }); const start = Date.now(); @@ -221,7 +221,7 @@ async function runTestInternal( // if we don't have `compileFunction` on the env, skip coverage const collectV8Coverage = - globalConfig.v8Coverage && + globalConfig.coverageProvider === 'v8' && typeof environment.compileFunction === 'function'; try { diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 818582c70e48..772371542936 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -136,7 +136,7 @@ class Runtime { collectCoverage: false, collectCoverageFrom: [], collectCoverageOnlyFrom: undefined, - v8Coverage: false, + coverageProvider: 'babel', }; this._currentlyExecutingModulePath = ''; this._environment = environment; @@ -503,7 +503,7 @@ class Runtime { collectCoverage: this._coverageOptions.collectCoverage, collectCoverageFrom: this._coverageOptions.collectCoverageFrom, collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, - v8Coverage: this._coverageOptions.v8Coverage, + coverageProvider: this._coverageOptions.coverageProvider, }; } diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 5a808f7aa08d..e8b30cce2500 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -392,8 +392,8 @@ export default class ScriptTransformer { if (!options.isCoreModule) { instrument = - shouldInstrument(filename, options, this._config) && - !options.v8Coverage; + options.coverageProvider === 'babel' && + shouldInstrument(filename, options, this._config); scriptCacheKey = getScriptCacheKey(filename, instrument); const result = this._cache.transformedFiles.get(scriptCacheKey); if (result) { diff --git a/packages/jest-transform/src/shouldInstrument.ts b/packages/jest-transform/src/shouldInstrument.ts index 305e30ab8868..2d8b57da709a 100644 --- a/packages/jest-transform/src/shouldInstrument.ts +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -21,7 +21,7 @@ export default function shouldInstrument( options: ShouldInstrumentOptions, config: Config.ProjectConfig, ): boolean { - if (!options.collectCoverage && !options.v8Coverage) { + if (!options.collectCoverage) { return false; } diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 0d013e64456e..f6d557b61db2 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -13,8 +13,10 @@ export type ShouldInstrumentOptions = Pick< | 'collectCoverage' | 'collectCoverageFrom' | 'collectCoverageOnlyFrom' - | 'v8Coverage' -> & {changedFiles?: Set}; + | 'coverageProvider' +> & { + changedFiles?: Set; +}; export type Options = ShouldInstrumentOptions & Partial<{ diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 397576fd0178..a69546d8b271 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -9,6 +9,8 @@ import {Arguments} from 'yargs'; import {ReportOptions} from 'istanbul-reports'; import chalk = require('chalk'); +type CoverageProvider = 'babel' | 'v8'; + export type Path = string; export type Glob = string; @@ -38,6 +40,7 @@ export type DefaultOptions = { collectCoverage: boolean; coveragePathIgnorePatterns: Array; coverageReporters: Array; + coverageProvider: CoverageProvider; errorOnDeprecated: boolean; expand: boolean; forceCoverageMatch: Array; @@ -76,7 +79,6 @@ export type DefaultOptions = { timers: 'real' | 'fake'; transformIgnorePatterns: Array; useStderr: boolean; - v8Coverage: boolean; watch: boolean; watchPathIgnorePatterns: Array; watchman: boolean; @@ -108,6 +110,7 @@ export type InitialOptions = Partial<{ }; coverageDirectory: string; coveragePathIgnorePatterns: Array; + coverageProvider: CoverageProvider; coverageReporters: Array; coverageThreshold: { global: { @@ -193,7 +196,6 @@ export type InitialOptions = Partial<{ [regex: string]: Path | TransformerConfig; }; transformIgnorePatterns: Array; - v8Coverage?: boolean; watchPathIgnorePatterns: Array; unmockedModulePathPatterns: Array; updateSnapshot: boolean; @@ -238,6 +240,7 @@ export type GlobalConfig = { }; coverageDirectory: string; coveragePathIgnorePatterns?: Array; + coverageProvider: CoverageProvider; coverageReporters: Array; coverageThreshold?: CoverageThreshold; detectLeaks: boolean; @@ -285,7 +288,6 @@ export type GlobalConfig = { updateSnapshot: SnapshotUpdateState; useStderr: boolean; verbose?: boolean; - v8Coverage: boolean; watch: boolean; watchAll: boolean; watchman: boolean; @@ -434,7 +436,6 @@ export type Argv = Arguments< useStderr: boolean; verbose: boolean; version: boolean; - v8Coverage: boolean; watch: boolean; watchAll: boolean; watchman: boolean; From aaec90d984a65dac75dc4d9c948428d4ed87d5f5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 9 Dec 2019 09:51:05 +0100 Subject: [PATCH 12/26] docs --- docs/CLI.md | 22 ++++++++-------------- docs/Configuration.md | 22 +++++++++------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index 8cd50c0abb2f..fac1bc1f0852 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -152,7 +152,14 @@ Alias: `-c`. The path to a Jest config file specifying how to find and execute t Alias: `--collectCoverage`. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. -Cannot be used together with `--v8-coverage`. +### `--coverageProvider=` + +Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. + +Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel and comes with a few caveats + +1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) +1. Tests needs to run in Node test environment (support for `jsdom` is in the works) ### `--debug` @@ -319,19 +326,6 @@ Divert all output to stderr. Display individual test results with the test suite hierarchy. -### `--v8-coverage[=]` - -> _Experimental_ - -Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. - -This uses V8's builtin code coverage rather than one based on Babel. Note that there are a few caveats - -1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) -1. Tests needs to run in Node test environment (support for jsdom is in the works) - -Cannot be used together with `--coverage`. - ### `--version` Alias: `-v`. Print the version and exit. diff --git a/docs/Configuration.md b/docs/Configuration.md index a821888891e7..f071d3a53bbf 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -187,6 +187,15 @@ An array of regexp pattern strings that are matched against all file paths befor These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. +### `coverageProvider` [string] + +Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. + +Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel and comes with a few caveats + +1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) +1. Tests needs to run in Node test environment (support for `jsdom` is in the works) + ### `coverageReporters` [array\] Default: `["json", "lcov", "text", "clover"]` @@ -1123,19 +1132,6 @@ This is useful for some commonly used 'utility' modules that are almost always u It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. -### `v8Coverage` [boolean] - -> _Experimental_ - -Indicates whether the coverage information should be collected while executing the test. - -This uses V8's builtin code coverage rather than one based on Babel. Note that there are a few caveats - -1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) -1. Tests needs to run in Node test environment (support for jsdom is in the works) - -Cannot be used together with `collectCoverage`. - ### `verbose` [boolean] Default: `false` From 4cc22324a9f7e818494ab223844344c91736a3b3 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 9 Dec 2019 09:54:54 +0100 Subject: [PATCH 13/26] try to keep coverage within rootDir --- .../__snapshots__/v8Coverage.test.ts.snap | 14 ++++++++------ e2e/v8-coverage/no-sourcemap/package.json | 1 - packages/jest-runtime/src/index.ts | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap index c37264b2c57d..db16b28abc41 100644 --- a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap +++ b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap @@ -4,10 +4,12 @@ exports[`prints coverage 1`] = ` console.log __tests__/Thing.test.js:10 42 -----------|---------|----------|---------|---------|------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s -----------|---------|----------|---------|---------|------------------- -All files | 100 | 100 | 100 | 100 | - Thing.js | 100 | 100 | 100 | 100 | -----------|---------|----------|---------|---------|------------------- +-----------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +-----------------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 50 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + cssTransform.js | 100 | 100 | 50 | 100 | + x.css | 100 | 100 | 100 | 100 | +-----------------|---------|----------|---------|---------|------------------- `; diff --git a/e2e/v8-coverage/no-sourcemap/package.json b/e2e/v8-coverage/no-sourcemap/package.json index 88ef243b361c..033719f58b9b 100644 --- a/e2e/v8-coverage/no-sourcemap/package.json +++ b/e2e/v8-coverage/no-sourcemap/package.json @@ -2,7 +2,6 @@ "name": "no-sourcemap", "version": "1.0.0", "jest": { - "collectCoverageFrom": ["Thing.js"], "testEnvironment": "node", "transform": { "^.+\\.[jt]sx?$": "babel-jest", diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 772371542936..c0fe2fd525ca 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -595,6 +595,8 @@ class Runtime { const filtered = this._v8CoverageResult .filter(res => res.url.startsWith('file://')) .map(res => ({...res, url: fileURLToPath(res.url)})) + // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways + .filter(res => res.url.startsWith(this._config.rootDir)) .filter(res => shouldInstrument(res.url, this._coverageOptions, this._config), ); From 718a1a1eab984f5ba90135e5f4d2b1d65d5d2b32 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 9 Dec 2019 09:59:22 +0100 Subject: [PATCH 14/26] inline snapshot so skipping tests work --- .../__snapshots__/v8Coverage.test.ts.snap | 15 --------------- e2e/__tests__/v8Coverage.test.ts | 14 +++++++++++++- 2 files changed, 13 insertions(+), 16 deletions(-) delete mode 100644 e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap diff --git a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap b/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap deleted file mode 100644 index db16b28abc41..000000000000 --- a/e2e/__tests__/__snapshots__/v8Coverage.test.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`prints coverage 1`] = ` - console.log __tests__/Thing.test.js:10 - 42 - ------------------|---------|----------|---------|---------|------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ------------------|---------|----------|---------|---------|------------------- -All files | 100 | 100 | 50 | 100 | - Thing.js | 100 | 100 | 100 | 100 | - cssTransform.js | 100 | 100 | 50 | 100 | - x.css | 100 | 100 | 100 | 100 | ------------------|---------|----------|---------|---------|------------------- -`; diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 0d74fab85554..7be176800d6a 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -24,6 +24,18 @@ onNodeVersions('>=10', () => { ); expect(exitCode).toBe(0); - expect(wrap(stdout)).toMatchSnapshot(); + expect(wrap(stdout)).toMatchInlineSnapshot(` + console.log __tests__/Thing.test.js:10 + 42 + + -----------------|---------|----------|---------|---------|------------------- + File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s + -----------------|---------|----------|---------|---------|------------------- + All files | 100 | 100 | 50 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + cssTransform.js | 100 | 100 | 50 | 100 | + x.css | 100 | 100 | 100 | 100 | + -----------------|---------|----------|---------|---------|------------------- + `); }); }); From af4482bc4b254d6fe9a31744f3bb36634a7103f2 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 10 Dec 2019 09:34:26 +0100 Subject: [PATCH 15/26] update showconfig snap --- e2e/__tests__/__snapshots__/showConfig.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap index 06206fe33f9f..7f3d73efee7c 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -12,7 +12,6 @@ exports[`--showConfig outputs config info and exits 1`] = ` "coveragePathIgnorePatterns": [ "/node_modules/" ], - "coverageProvider": "babel", "cwd": "<>", "detectLeaks": false, "detectOpenHandles": false, @@ -85,6 +84,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` "collectCoverage": false, "collectCoverageFrom": [], "coverageDirectory": "<>/coverage", + "coverageProvider": "babel", "coverageReporters": [ "json", "text", From 70ee018a3b15620fc2e45f5ba89544c1f6b40a2f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 10 Dec 2019 09:38:33 +0100 Subject: [PATCH 16/26] maybe this? --- e2e/__tests__/v8Coverage.test.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 7be176800d6a..bece5204e588 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -8,8 +8,23 @@ import * as path from 'path'; import {wrap} from 'jest-snapshot-serializer-raw'; import {onNodeVersions} from '@jest/test-utils'; +import {toMatchInlineSnapshot} from 'jest-snapshot'; import runJest from '../runJest'; +expect.extend({ + toMatchRightTrimmedInlineSnapshot(received: string) { + return toMatchInlineSnapshot.call( + this, + wrap( + received + .split('\n') + .map(s => s.trimRight()) + .join('\n'), + ), + ); + }, +}); + const DIR = path.resolve(__dirname, '../v8-coverage'); onNodeVersions('>=10', () => { @@ -24,17 +39,17 @@ onNodeVersions('>=10', () => { ); expect(exitCode).toBe(0); - expect(wrap(stdout)).toMatchInlineSnapshot(` + expect(stdout).toMatchRightTrimmedInlineSnapshot(` console.log __tests__/Thing.test.js:10 42 -----------------|---------|----------|---------|---------|------------------- - File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s + File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s -----------------|---------|----------|---------|---------|------------------- - All files | 100 | 100 | 50 | 100 | - Thing.js | 100 | 100 | 100 | 100 | - cssTransform.js | 100 | 100 | 50 | 100 | - x.css | 100 | 100 | 100 | 100 | + All files | 100 | 100 | 50 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + cssTransform.js | 100 | 100 | 50 | 100 | + x.css | 100 | 100 | 100 | 100 | -----------------|---------|----------|---------|---------|------------------- `); }); From c190ae63363dc405173c48854e6253ad5299f996 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 10 Dec 2019 09:42:35 +0100 Subject: [PATCH 17/26] equal and ignore in watcher --- e2e/__tests__/v8Coverage.test.ts | 47 +++++++++++++------------------- jest.config.js | 1 + 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index bece5204e588..1ef7e14ae19e 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -6,25 +6,9 @@ */ import * as path from 'path'; -import {wrap} from 'jest-snapshot-serializer-raw'; import {onNodeVersions} from '@jest/test-utils'; -import {toMatchInlineSnapshot} from 'jest-snapshot'; import runJest from '../runJest'; -expect.extend({ - toMatchRightTrimmedInlineSnapshot(received: string) { - return toMatchInlineSnapshot.call( - this, - wrap( - received - .split('\n') - .map(s => s.trimRight()) - .join('\n'), - ), - ); - }, -}); - const DIR = path.resolve(__dirname, '../v8-coverage'); onNodeVersions('>=10', () => { @@ -39,18 +23,25 @@ onNodeVersions('>=10', () => { ); expect(exitCode).toBe(0); - expect(stdout).toMatchRightTrimmedInlineSnapshot(` - console.log __tests__/Thing.test.js:10 - 42 + expect( + '\n' + + stdout + .split('\n') + .map(s => s.trimRight()) + .join('\n') + + '\n', + ).toEqual(` + console.log __tests__/Thing.test.js:10 + 42 - -----------------|---------|----------|---------|---------|------------------- - File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s - -----------------|---------|----------|---------|---------|------------------- - All files | 100 | 100 | 50 | 100 | - Thing.js | 100 | 100 | 100 | 100 | - cssTransform.js | 100 | 100 | 50 | 100 | - x.css | 100 | 100 | 100 | 100 | - -----------------|---------|----------|---------|---------|------------------- - `); +-----------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +-----------------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 50 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + cssTransform.js | 100 | 100 | 50 | 100 | + x.css | 100 | 100 | 100 | 100 | +-----------------|---------|----------|---------|---------|------------------- +`); }); }); diff --git a/jest.config.js b/jest.config.js index a39a9f4789f5..cb189da42604 100644 --- a/jest.config.js +++ b/jest.config.js @@ -65,6 +65,7 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': '/packages/babel-jest', }, + watchPathIgnorePatterns: ['coverage'], watchPlugins: [ 'jest-watch-typeahead/filename', 'jest-watch-typeahead/testname', From 6bade4ab87c20c502af673c61079670871cb768e Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 11 Dec 2019 10:03:59 +0100 Subject: [PATCH 18/26] get only files we have transformed from the runtime --- e2e/__tests__/v8Coverage.test.ts | 15 ++++++------- packages/jest-runtime/src/index.ts | 36 ++++++++++++++++-------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/e2e/__tests__/v8Coverage.test.ts b/e2e/__tests__/v8Coverage.test.ts index 1ef7e14ae19e..97dfea44252f 100644 --- a/e2e/__tests__/v8Coverage.test.ts +++ b/e2e/__tests__/v8Coverage.test.ts @@ -34,14 +34,13 @@ onNodeVersions('>=10', () => { console.log __tests__/Thing.test.js:10 42 ------------------|---------|----------|---------|---------|------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ------------------|---------|----------|---------|---------|------------------- -All files | 100 | 100 | 50 | 100 | - Thing.js | 100 | 100 | 100 | 100 | - cssTransform.js | 100 | 100 | 50 | 100 | - x.css | 100 | 100 | 100 | 100 | ------------------|---------|----------|---------|---------|------------------- +----------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +----------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 100 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + x.css | 100 | 100 | 100 | 100 | +----------|---------|----------|---------|---------|------------------- `); }); }); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index c0fe2fd525ca..dcc872a95db1 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -592,23 +592,25 @@ class Runtime { throw new Error('You need to `stopCollectingV8Coverage` first'); } - const filtered = this._v8CoverageResult - .filter(res => res.url.startsWith('file://')) - .map(res => ({...res, url: fileURLToPath(res.url)})) - // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways - .filter(res => res.url.startsWith(this._config.rootDir)) - .filter(res => - shouldInstrument(res.url, this._coverageOptions, this._config), - ); - - return filtered.map(result => { - const transformedFile = this._fileTransforms.get(result.url); - - return { - codeTransformResult: transformedFile, - result, - }; - }); + return ( + this._v8CoverageResult + .filter(res => res.url.startsWith('file://')) + .map(res => ({...res, url: fileURLToPath(res.url)})) + // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways + .filter(res => res.url.startsWith(this._config.rootDir)) + .filter(res => this._fileTransforms.has(res.url)) + .filter(res => + shouldInstrument(res.url, this._coverageOptions, this._config), + ) + .map(result => { + const transformedFile = this._fileTransforms.get(result.url); + + return { + codeTransformResult: transformedFile, + result, + }; + }) + ); } getSourceMapInfo(coveredFiles: Set) { From 98b4f49ca59e8f8693d0bcfa4fe9481688e91e72 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 11 Dec 2019 10:44:48 +0100 Subject: [PATCH 19/26] lint --- packages/jest-config/src/Defaults.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index cf0e205cab59..af912c569d2c 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -22,8 +22,8 @@ const defaultOptions: Config.DefaultOptions = { clearMocks: false, collectCoverage: false, coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], - coverageReporters: ['json', 'text', 'lcov', 'clover'], coverageProvider: 'babel', + coverageReporters: ['json', 'text', 'lcov', 'clover'], errorOnDeprecated: false, expand: false, forceCoverageMatch: [], From cf34642a54a1172ec363f4436c1475f32ddca22e Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 11 Dec 2019 11:04:12 +0100 Subject: [PATCH 20/26] remove old doc change --- docs/Configuration.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index f071d3a53bbf..98349e39344f 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -131,8 +131,6 @@ Default: `false` Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. -Cannot be used together with `v8Coverage`. - ### `collectCoverageFrom` [array] Default: `undefined` From c9949549e11bc6b5e576f75dd35d74dc103c8549 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 12 Dec 2019 09:10:51 +0100 Subject: [PATCH 21/26] combine filters --- packages/jest-runtime/src/index.ts | 40 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index dcc872a95db1..6b9e4a8e0412 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -592,25 +592,24 @@ class Runtime { throw new Error('You need to `stopCollectingV8Coverage` first'); } - return ( - this._v8CoverageResult - .filter(res => res.url.startsWith('file://')) - .map(res => ({...res, url: fileURLToPath(res.url)})) - // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways - .filter(res => res.url.startsWith(this._config.rootDir)) - .filter(res => this._fileTransforms.has(res.url)) - .filter(res => + return this._v8CoverageResult + .filter(res => res.url.startsWith('file://')) + .map(res => ({...res, url: fileURLToPath(res.url)})) + .filter( + res => + // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways + res.url.startsWith(this._config.rootDir) && + this._fileTransforms.has(res.url) && shouldInstrument(res.url, this._coverageOptions, this._config), - ) - .map(result => { - const transformedFile = this._fileTransforms.get(result.url); - - return { - codeTransformResult: transformedFile, - result, - }; - }) - ); + ) + .map(result => { + const transformedFile = this._fileTransforms.get(result.url); + + return { + codeTransformResult: transformedFile, + result, + }; + }); } getSourceMapInfo(coveredFiles: Set) { @@ -772,7 +771,10 @@ class Runtime { this._cacheFS[filename], ); - this._fileTransforms.set(filename, transformedFile); + // we only care about non-internal modules + if (!options || !options.isInternalModule) { + this._fileTransforms.set(filename, transformedFile); + } if (transformedFile.sourceMapPath) { this._sourceMapRegistry[filename] = transformedFile.sourceMapPath; From c2ff7d8360a57e164f1968050172c22bde414b83 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Dec 2019 17:13:24 +0100 Subject: [PATCH 22/26] bump v8-to-istanbul --- packages/jest-reporters/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index 0ab54b9ba8d6..9b6d280e85b4 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -29,7 +29,7 @@ "source-map": "^0.6.0", "string-length": "^3.1.0", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.0.0" + "v8-to-istanbul": "^4.0.1" }, "devDependencies": { "@types/exit": "^0.1.30", diff --git a/yarn.lock b/yarn.lock index 8c1c30528d9b..5d58c466ed90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14145,10 +14145,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== -v8-to-istanbul@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.0.0.tgz#6d420f519e82a5f9a74f2b7275ba9263e19ab80e" - integrity sha512-UqOveasq5AAXnhBo7Wg+PQUkNvCEOoocECuR6DA9CxfHEcCz24vnjH0lgeFGLVpGk12XEy8xup/MFBjxT6POrQ== +v8-to-istanbul@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.0.1.tgz#d6a2a3823b8ff49bdf2167ff2a45d82dff81d02f" + integrity sha512-x0yZvZAkjJwdD3fPiJzYP37aod0ati4LlmD2RmpKjqewjKAov/u/ytZ8ViIZb07cN4cePKzl9ijiUi7C1LQ8hQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" From c5c1de6ee562e7a93939abc1f7ae465b4a495b4f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Dec 2019 21:11:44 +0100 Subject: [PATCH 23/26] add note about newer nodes --- docs/CLI.md | 1 + docs/Configuration.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/CLI.md b/docs/CLI.md index fac1bc1f0852..3a549bcfa7c7 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -160,6 +160,7 @@ Note that using `v8` is considered experimental. This uses V8's builtin code cov 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) 1. Tests needs to run in Node test environment (support for `jsdom` is in the works) +1. V8 has way better data in the later versions, so using the latets versions of node (v13 at the time of this writing) will yield better results ### `--debug` diff --git a/docs/Configuration.md b/docs/Configuration.md index 98349e39344f..29b9867f88fe 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -193,6 +193,7 @@ Note that using `v8` is considered experimental. This uses V8's builtin code cov 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) 1. Tests needs to run in Node test environment (support for `jsdom` is in the works) +1. V8 has way better data in the later versions, so using the latets versions of node (v13 at the time of this writing) will yield better results ### `coverageReporters` [array\] From 78ceba8c74f827f41ee5df660c83469d0681a755 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Dec 2019 21:36:54 +0100 Subject: [PATCH 24/26] extract v8 profile code --- packages/jest-coverage/.npmignore | 3 - packages/jest-coverage/package.json | 22 ------ packages/jest-coverage/src/index.ts | 76 ------------------- packages/jest-coverage/tsconfig.json | 7 -- packages/jest-reporters/package.json | 2 +- .../src/generateEmptyCoverage.ts | 2 +- packages/jest-reporters/tsconfig.json | 1 - packages/jest-runtime/package.json | 2 +- packages/jest-runtime/src/index.ts | 2 +- packages/jest-runtime/tsconfig.json | 1 - packages/jest-test-result/package.json | 4 +- packages/jest-test-result/src/types.ts | 2 +- packages/jest-test-result/tsconfig.json | 1 - yarn.lock | 5 ++ 14 files changed, 12 insertions(+), 118 deletions(-) delete mode 100644 packages/jest-coverage/.npmignore delete mode 100644 packages/jest-coverage/package.json delete mode 100644 packages/jest-coverage/src/index.ts delete mode 100644 packages/jest-coverage/tsconfig.json diff --git a/packages/jest-coverage/.npmignore b/packages/jest-coverage/.npmignore deleted file mode 100644 index 85e48fe7b0a4..000000000000 --- a/packages/jest-coverage/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -**/__mocks__/** -**/__tests__/** -src diff --git a/packages/jest-coverage/package.json b/packages/jest-coverage/package.json deleted file mode 100644 index 2cd0b0966c5c..000000000000 --- a/packages/jest-coverage/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@jest/coverage", - "version": "24.9.0", - "repository": { - "type": "git", - "url": "https://github.com/facebook/jest.git", - "directory": "packages/jest-coverage" - }, - "license": "MIT", - "main": "build/index.js", - "types": "build/index.d.ts", - "devDependencies": { - "@types/node": "*" - }, - "engines": { - "node": ">= 8" - }, - "publishConfig": { - "access": "public" - }, - "gitHead": "0efb1d7809cb96ae87a7601e7802f1dab3774280" -} diff --git a/packages/jest-coverage/src/index.ts b/packages/jest-coverage/src/index.ts deleted file mode 100644 index f7fe85709393..000000000000 --- a/packages/jest-coverage/src/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 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 {Profiler, Session} from 'inspector'; - -export type V8Coverage = ReadonlyArray; - -export class CoverageInstrumenter { - private readonly session = new Session(); - - async startInstrumenting() { - this.session.connect(); - - await new Promise((resolve, reject) => { - this.session.post('Profiler.enable', err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - - await new Promise((resolve, reject) => { - this.session.post( - 'Profiler.startPreciseCoverage', - {callCount: true, detailed: true}, - err => { - if (err) { - reject(err); - } else { - resolve(); - } - }, - ); - }); - } - - async stopInstrumenting(): Promise { - const preciseCoverage = await new Promise((resolve, reject) => { - this.session.post('Profiler.takePreciseCoverage', (err, res) => { - if (err) { - reject(err); - } else { - resolve(res.result); - } - }); - }); - - await new Promise((resolve, reject) => { - this.session.post('Profiler.stopPreciseCoverage', err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - - await new Promise((resolve, reject) => { - this.session.post('Profiler.disable', err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - - return preciseCoverage; - } -} diff --git a/packages/jest-coverage/tsconfig.json b/packages/jest-coverage/tsconfig.json deleted file mode 100644 index 7bb06bce6d20..000000000000 --- a/packages/jest-coverage/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "build" - } -} diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index 9b6d280e85b4..aae31a9d81b9 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -7,12 +7,12 @@ "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^24.9.0", - "@jest/coverage": "^24.9.0", "@jest/environment": "^24.9.0", "@jest/test-result": "^24.9.0", "@jest/transform": "^24.9.0", "@jest/types": "^24.9.0", "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", "istanbul-lib-coverage": "^3.0.0-alpha.1", diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index 633c18b2f7b2..b026e9861b3e 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -10,7 +10,7 @@ import {Config} from '@jest/types'; import {readInitialCoverage} from 'istanbul-lib-instrument'; import {FileCoverage, createFileCoverage} from 'istanbul-lib-coverage'; import {ScriptTransformer, shouldInstrument} from '@jest/transform'; -import {V8Coverage} from '@jest/coverage'; +import {V8Coverage} from 'collect-v8-coverage'; type SingleV8Coverage = V8Coverage[number]; diff --git a/packages/jest-reporters/tsconfig.json b/packages/jest-reporters/tsconfig.json index d2bb28f2eb98..bdf1b1cdc9ee 100644 --- a/packages/jest-reporters/tsconfig.json +++ b/packages/jest-reporters/tsconfig.json @@ -6,7 +6,6 @@ }, "references": [ {"path": "../jest-console"}, - {"path": "../jest-coverage"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-resolve"}, diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 43106843c43d..0f2785202dd4 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -11,7 +11,6 @@ "types": "build/index.d.ts", "dependencies": { "@jest/console": "^24.7.1", - "@jest/coverage": "^24.9.0", "@jest/environment": "^24.9.0", "@jest/source-map": "^24.3.0", "@jest/test-result": "^24.9.0", @@ -19,6 +18,7 @@ "@jest/types": "^24.9.0", "@types/yargs": "^13.0.0", "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.3", diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 6b9e4a8e0412..aa224ccf8d55 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -33,7 +33,7 @@ import { shouldInstrument, } from '@jest/transform'; import {V8CoverageResult} from '@jest/test-result'; -import {CoverageInstrumenter, V8Coverage} from '@jest/coverage'; +import {CoverageInstrumenter, V8Coverage} from 'collect-v8-coverage'; import * as fs from 'graceful-fs'; import stripBOM = require('strip-bom'); import {run as cliRun} from './cli'; diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index 2f18ec60dfad..23c5279939a3 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -7,7 +7,6 @@ "references": [ {"path": "../jest-config"}, {"path": "../jest-console"}, - {"path": "../jest-coverage"}, {"path": "../jest-environment"}, {"path": "../jest-environment-node"}, {"path": "../jest-haste-map"}, diff --git a/packages/jest-test-result/package.json b/packages/jest-test-result/package.json index 4bada0fff0e1..69c636029eb0 100644 --- a/packages/jest-test-result/package.json +++ b/packages/jest-test-result/package.json @@ -11,10 +11,10 @@ "types": "build/index.d.ts", "dependencies": { "@jest/console": "^24.9.0", - "@jest/coverage": "^24.9.0", "@jest/transform": "^24.9.0", "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { "node": ">= 8" diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index e46e3e4e2140..7a19b782d6d0 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -9,7 +9,7 @@ import {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage'; import {ConsoleBuffer} from '@jest/console'; import {Config} from '@jest/types'; -import {V8Coverage} from '@jest/coverage'; +import {V8Coverage} from 'collect-v8-coverage'; import {TransformResult} from '@jest/transform'; export type V8CoverageResult = Array<{ diff --git a/packages/jest-test-result/tsconfig.json b/packages/jest-test-result/tsconfig.json index fa018f248c80..c8ba8adff346 100644 --- a/packages/jest-test-result/tsconfig.json +++ b/packages/jest-test-result/tsconfig.json @@ -6,7 +6,6 @@ }, "references": [ {"path": "../jest-console"}, - {"path": "../jest-coverage"}, {"path": "../jest-transform"}, {"path": "../jest-types"} ] diff --git a/yarn.lock b/yarn.lock index 5d58c466ed90..0639a766001e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4014,6 +4014,11 @@ collapse-white-space@^1.0.2: resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.5.tgz#c2495b699ab1ed380d29a1091e01063e75dbbe3a" integrity sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ== +collect-v8-coverage@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" + integrity sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" From 881fd82818e00589bf38360060b5b9a09c00d70f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 17 Dec 2019 09:21:10 +0100 Subject: [PATCH 25/26] Apply suggestions from code review Co-Authored-By: Tim Seckinger --- docs/CLI.md | 2 +- docs/Configuration.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index 3a549bcfa7c7..da40fd18a699 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -160,7 +160,7 @@ Note that using `v8` is considered experimental. This uses V8's builtin code cov 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) 1. Tests needs to run in Node test environment (support for `jsdom` is in the works) -1. V8 has way better data in the later versions, so using the latets versions of node (v13 at the time of this writing) will yield better results +1. V8 has way better data in the later versions, so using the latest versions of node (v13 at the time of this writing) will yield better results ### `--debug` diff --git a/docs/Configuration.md b/docs/Configuration.md index 29b9867f88fe..efce8c57354f 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -193,7 +193,7 @@ Note that using `v8` is considered experimental. This uses V8's builtin code cov 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) 1. Tests needs to run in Node test environment (support for `jsdom` is in the works) -1. V8 has way better data in the later versions, so using the latets versions of node (v13 at the time of this writing) will yield better results +1. V8 has way better data in the later versions, so using the latest versions of node (v13 at the time of this writing) will yield better results ### `coverageReporters` [array\] From 9c7c353b6041db56b050996cfae7de09cc367157 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 17 Dec 2019 09:27:52 +0100 Subject: [PATCH 26/26] link to tracking issue --- docs/CLI.md | 2 +- docs/Configuration.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index da40fd18a699..09432df11646 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -159,7 +159,7 @@ Indicates which provider should be used to instrument code for coverage. Allowed Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel and comes with a few caveats 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) -1. Tests needs to run in Node test environment (support for `jsdom` is in the works) +1. Tests needs to run in Node test environment (support for `jsdom` is in the works, see [#9315](https://github.com/facebook/jest/issues/9315)) 1. V8 has way better data in the later versions, so using the latest versions of node (v13 at the time of this writing) will yield better results ### `--debug` diff --git a/docs/Configuration.md b/docs/Configuration.md index efce8c57354f..266196ff85c6 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -192,7 +192,7 @@ Indicates which provider should be used to instrument code for coverage. Allowed Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel and comes with a few caveats 1. Your node version must include `vm.compileFunction`, which was introduced in [node 10.10](https://nodejs.org/dist/latest-v12.x/docs/api/vm.html#vm_vm_compilefunction_code_params_options) -1. Tests needs to run in Node test environment (support for `jsdom` is in the works) +1. Tests needs to run in Node test environment (support for `jsdom` is in the works, see [#9315](https://github.com/facebook/jest/issues/9315)) 1. V8 has way better data in the later versions, so using the latest versions of node (v13 at the time of this writing) will yield better results ### `coverageReporters` [array\]