Skip to content

Commit

Permalink
fix(jest-runtime): correctly report V8 coverage with `resetModules: t…
Browse files Browse the repository at this point in the history
…rue` (#12912)
  • Loading branch information
mrazauskas committed Jun 5, 2022
1 parent c4a01bd commit 3a8696b
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@
- `[@jest/expect-utils]` Fix deep equality of ImmutableJS OrderedMaps ([#12763](https://github.com/facebook/jest/pull/12899))
- `[jest-docblock]` Handle multiline comments in parseWithComments ([#12845](https://github.com/facebook/jest/pull/12845))
- `[jest-mock]` Improve `spyOn` error messages ([#12901](https://github.com/facebook/jest/pull/12901))
- `[jest-runtime]` Correctly report V8 coverage with `resetModules: true` ([#12912](https://github.com/facebook/jest/pull/12912))
- `[jest-worker]` Make `JestWorkerFarm` helper type to include methods of worker module that take more than one argument ([#12839](https://github.com/facebook/jest/pull/12839))

### Chore & Maintenance
Expand Down
20 changes: 20 additions & 0 deletions e2e/__tests__/__snapshots__/coverageProviderV8.test.ts.snap
Expand Up @@ -85,3 +85,23 @@ All files | 100 | 100 | 100 | 100 |
x.css | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------"
`;

exports[`reports coverage with \`resetModules\` 1`] = `
" console.log
this will print
at log (module.js:11:11)
console.log
this will print
at log (module.js:11:11)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 59.37 | 33.33 | 33.33 | 59.37 |
module.js | 79.16 | 50 | 50 | 79.16 | 14-16,19-20
uncovered.js | 0 | 0 | 0 | 0 | 1-8
--------------|---------|----------|---------|---------|-------------------"
`;
13 changes: 13 additions & 0 deletions e2e/__tests__/coverageProviderV8.test.ts
Expand Up @@ -37,6 +37,19 @@ test('prints coverage with empty sourcemaps', () => {
expect(stdout).toMatchSnapshot();
});

test('reports coverage with `resetModules`', () => {
const sourcemapDir = path.join(DIR, 'with-resetModules');

const {stdout, exitCode} = runJest(
sourcemapDir,
['--coverage', '--coverage-provider', 'v8'],
{stripAnsi: true},
);

expect(exitCode).toBe(0);
expect(stdout).toMatchSnapshot();
});

test('prints correct coverage report, if a CJS module is put under test without transformation', () => {
const sourcemapDir = path.join(DIR, 'cjs-native-without-sourcemap');

Expand Down
16 changes: 16 additions & 0 deletions e2e/coverage-provider-v8/with-resetModules/__tests__/test.js
@@ -0,0 +1,16 @@
/**
* 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.
*/

test('dummy', () => {
const {value} = require('../module');
expect(value).toBe('abc');
});

test('reset dummy', () => {
const {value} = require('../module');
expect(value).toBe('abc');
});
24 changes: 24 additions & 0 deletions e2e/coverage-provider-v8/with-resetModules/module.js
@@ -0,0 +1,24 @@
/**
* 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 value = 'abc';

function covered() {
console.log('this will print');
}

function uncovered() {
console.log('this will not');
}

if (value !== 'abc') {
uncovered();
}

covered();

module.exports = {value};
8 changes: 8 additions & 0 deletions e2e/coverage-provider-v8/with-resetModules/package.json
@@ -0,0 +1,8 @@
{
"jest": {
"collectCoverageFrom": [
"*.js"
],
"resetModules": true
}
}
8 changes: 8 additions & 0 deletions e2e/coverage-provider-v8/with-resetModules/uncovered.js
@@ -0,0 +1,8 @@
/**
* 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 = {};
29 changes: 24 additions & 5 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -221,6 +221,7 @@ export default class Runtime {
private readonly _fileTransformsMutex: Map<string, Promise<void>>;
private _v8CoverageInstrumenter: CoverageInstrumenter | undefined;
private _v8CoverageResult: V8Coverage | undefined;
private _v8CoverageSources: Map<string, RuntimeTransformResult> | undefined;
private readonly _transitiveShouldMock: Map<string, boolean>;
private _unmockList: RegExp | undefined;
private readonly _virtualMocks: Map<string, boolean>;
Expand Down Expand Up @@ -1155,6 +1156,18 @@ export default class Runtime {
this._cjsNamedExports.clear();
this._moduleMockRegistry.clear();
this._cacheFS.clear();

if (
this._coverageOptions.collectCoverage &&
this._coverageOptions.coverageProvider === 'v8' &&
this._v8CoverageSources
) {
this._v8CoverageSources = new Map([
...this._v8CoverageSources,
...this._fileTransforms,
]);
}

this._fileTransforms.clear();

if (this._environment) {
Expand Down Expand Up @@ -1182,25 +1195,30 @@ export default class Runtime {

async collectV8Coverage(): Promise<void> {
this._v8CoverageInstrumenter = new CoverageInstrumenter();
this._v8CoverageSources = new Map();

await this._v8CoverageInstrumenter.startInstrumenting();
}

async stopCollectingV8Coverage(): Promise<void> {
if (!this._v8CoverageInstrumenter) {
if (!this._v8CoverageInstrumenter || !this._v8CoverageSources) {
throw new Error('You need to call `collectV8Coverage` first.');
}
this._v8CoverageResult =
await this._v8CoverageInstrumenter.stopInstrumenting();
this._v8CoverageSources = new Map([
...this._v8CoverageSources,
...this._fileTransforms,
]);
}

getAllCoverageInfoCopy(): JestEnvironment['global']['__coverage__'] {
return deepCyclicCopy(this._environment.global.__coverage__);
}

getAllV8CoverageInfoCopy(): V8CoverageResult {
if (!this._v8CoverageResult) {
throw new Error('You need to `stopCollectingV8Coverage` first');
if (!this._v8CoverageResult || !this._v8CoverageSources) {
throw new Error('You need to call `stopCollectingV8Coverage` first.');
}

return this._v8CoverageResult
Expand All @@ -1210,11 +1228,11 @@ export default class Runtime {
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) &&
this._v8CoverageSources!.has(res.url) &&
shouldInstrument(res.url, this._coverageOptions, this._config),
)
.map(result => {
const transformedFile = this._fileTransforms.get(result.url);
const transformedFile = this._v8CoverageSources!.get(result.url);

return {
codeTransformResult: transformedFile,
Expand Down Expand Up @@ -1307,6 +1325,7 @@ export default class Runtime {
this._fileTransformsMutex.clear();
this.jestObjectCaches.clear();

this._v8CoverageSources?.clear();
this._v8CoverageResult = [];
this._v8CoverageInstrumenter = undefined;
this._moduleImplementation = undefined;
Expand Down

0 comments on commit 3a8696b

Please sign in to comment.