Search code examples
jestjscode-coveragev8test-coverage

Is it possible to collect coverage on code loaded with vm.runInContext with Jest?


I'm working on a legacy JS project which is not using any require/import. When deploying, the files are just concatenated and the result is sent to a server.

In order to write tests with jest, I created a custom environment to load all the JS files in the global context so that I can call the functions in the test file.

For example:

src/index.js

function sum(x, y) {
  return x + y;
}

src/index.spec.js

it('should sum two numbers', () => {
  expect(sum(1, 2)).toBe(3);
});

jest.config.js

module.exports = {
  clearMocks: true,
  collectCoverage: true,
  collectCoverageFrom: [
    "src/**/*.js",
  ],
  coverageDirectory: "coverage",
  coverageProvider: "v8",
  testEnvironment: "./jest.env.js",
};

jest.env.js

const NodeEnvironment = require('jest-environment-node').TestEnvironment;
const fs = require('fs');
const vm = require("vm");
const path = require("path");

class CustomEnv extends NodeEnvironment {
  constructor(config, context) {
    super(config, context);
    this.loadContext();
  }

  loadContext() {
    const js = fs.readFileSync('./src/index.js', 'utf8');
    const context = vm.createContext(this.global);
    vm.runInContext(js, context, {
      filename: path.resolve('./src/index.js'),
      displayErrors: true,
    });
    Object.assign(this.global, context);
  }
}

module.exports = CustomEnv;

When I run npx jest, the test is executed but the coverage is empty... enter image description here

Any idea on how to fix the coverage?

I've created a minimal reproducible repo here: https://github.com/GP4cK/jest-coverage-run-in-context/tree/main. You can just clone it, run npm i and npm t. Note: I'm happy to change v8 to babel or load the context differently if it makes it easier.


Solution

  • The issue you're encountering has been reported in the Jest repository. You can find more information on this issue at: https://github.com/facebook/jest/issues/9349 https://github.com/facebook/jest/issues/10645.

    Although a fix for the issue was implemented, it was never merged and has become outdated. However, there is an easy fix available that you can contribute yourself as a pull request. You can find the suggested fix at: https://github.com/facebook/jest/pull/10657. The fix has not been completed yet, so it needs someone to finish it.

    To implement the suggested fix, you need to remove the one of the culprits at: https://github.com/facebook/jest/blob/main/packages/jest-runtime/src/index.ts#L1268

    Specifically, you can comment out the following line:

    this._v8CoverageSources.has(res.url) &&

    You can quick test the fix locally by commenting out the same line in node_modules:

    nodes_modules/jest-runtime/build/index.js:1281

    Hope this helps!