Skip to content

Commit

Permalink
Dev: Fix coverage
Browse files Browse the repository at this point in the history
Closes #471.
  • Loading branch information
overlookmotel committed Dec 30, 2022
1 parent 34fbcd8 commit 20f85ef
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 36 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[![NPM version](https://img.shields.io/npm/v/livepack.svg)](https://www.npmjs.com/package/livepack)
[![Build Status](https://img.shields.io/github/actions/workflow/status/overlookmotel/livepack/test.yml?branch=master)](https://github.com/overlookmotel/livepack/actions)
[![Coverage Status](https://img.shields.io/coveralls/overlookmotel/livepack/master.svg)](https://coveralls.io/r/overlookmotel/livepack)

# Serialize live running code to Javascript

Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

module.exports = {
testEnvironment: 'node',
runner: 'jest-light-runner',
runner: '<rootDir>/test/support/runner.mjs',
coverageDirectory: 'coverage',
coverageProvider: 'v8',
collectCoverageFrom: ['*.js', '!.eslintrc.js', '!jest.config.js', 'lib/**/*.js'],
Expand Down
87 changes: 53 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,17 @@
"@overlookmotel/eslint-config-jest": "^6.0.1",
"@overlookmotel/eslint-config-node": "^4.1.0",
"@overlookmotel/jest-extended": "^3.2.0",
"collect-v8-coverage": "^1.0.1",
"eslint": "^8.30.0",
"expect": "^29.3.1",
"jest": "^29.3.1",
"jest-expect-arguments": "^1.0.0",
"jest-light-runner": "^0.4.1",
"jest-matcher-utils": "^29.3.1",
"npm-run-all": "^4.1.5"
"jest-util": "^29.3.1",
"npm-run-all": "^4.1.5",
"supports-color": "^9.3.1",
"tinypool": "^0.3.0"
},
"keywords": [
"build",
Expand Down
107 changes: 107 additions & 0 deletions test/support/coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* --------------------
* livepack
* Capture V8 coverage
* ------------------*/

/* eslint-disable import/order, import/newline-after-import */

'use strict';

// Use internal module cache because some of same modules (e.g. `pirates`)
// are also used by `register.js`. This avoids loading them twice.
const {
useInternalModuleCache, useGlobalModuleCache, usingInternalModuleCache
} = require('../../lib/shared/moduleCache.js');
useInternalModuleCache();

// Modules
const {join: pathJoin, relative: relativePath} = require('path'),
{fileURLToPath} = require('url'),
{CoverageInstrumenter} = require('collect-v8-coverage'),
{globsToMatcher, replacePathSepForGlob} = require('jest-util'),
{addHook} = require('pirates'),
EXTS = require('@babel/core').DEFAULT_EXTENSIONS;

// Imports
const {collectCoverageFrom} = require('../../jest.config.js');

useGlobalModuleCache();

// Constants
const TESTS_DIR = pathJoin(__dirname, '../'),
ROOT_DIR = pathJoin(TESTS_DIR, '../');

// Exports

module.exports = startCoverage;
startCoverage.applyAfterAllHook = applyAfterAllHook;

let instrumenter;

/**
* Run by `jest-light-runner` before `./register.js`.
* Start capturing V8 coverage.
* @async
* @returns {undefined}
*/
async function startCoverage() {
instrumenter = new CoverageInstrumenter();
await instrumenter.startInstrumenting();
}

/**
* Register `afterAll` test hook to record V8 coverage data to `global.__coverage__`.
* `jest-light-runner` collects this and records it in the test result object as Babel coverage data.
* Custom runner `./runner.mjs` then moves it to the `.v8Coverage` property of result object.
*
* Code to call this function is added to bottom of every test file by pirates hook below.
*
* V8 coverage data is filtered to only files being assessed for coverage.
* This is done here rather than in the custom runner to minimise data transfer between
* worker thread running the test and the runner main thread.
*
* @returns {undefined}
*/
function applyAfterAllHook() {
afterAll(async () => {
global.__coverage__ = filterCoverageData(await instrumenter.stopInstrumenting());
});
}

/**
* Filter coverage data to files being monitored for coverage only.
* Based on:
* https://github.com/facebook/jest/blob/fb2de8a10f8e808b080af67aa771f67b5ea537ce/packages/jest-runtime/src/index.ts#L1217
*
* @param {Array<Object>} coverage - Coverage data captured by V8
* @returns {Array<Object>|undefined} - Conformed coverage data
*/
function filterCoverageData(coverage) {
if (collectCoverageFrom && collectCoverageFrom.length === 0) return undefined;

const filenameMatcher = globsToMatcher(collectCoverageFrom);

coverage = coverage
.filter(res => res.url.startsWith('file://'))
.map(res => ({...res, url: fileURLToPath(res.url)}))
.filter(
res => res.url.startsWith(ROOT_DIR)
&& filenameMatcher(replacePathSepForGlob(relativePath(ROOT_DIR, res.url)))
)
.map(result => ({result}));

return coverage.length > 0 ? coverage : undefined;
}

// Install extra require hook to add code to end of test files to install `afterAll` hook.
// This code is added to file before Livepack's instrumentation rather than after.
// This is a bit sloppy, but makes no difference in this case as code is at bottom of the file
// (so doesn't affect sourcemaps) and doesn't contain any functions which will be instrumented.
addHook(
(code, path) => (
(!usingInternalModuleCache() && path.startsWith(TESTS_DIR) && path.endsWith('.test.js'))
? `${code}\nrequire(${JSON.stringify(__filename)}).applyAfterAllHook();\n`
: code
),
{ignoreNodeModules: false, exts: EXTS}
);
Loading

0 comments on commit 20f85ef

Please sign in to comment.