cypress-native-coverage
Version:
156 lines • 6.72 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.nativeCoveragePlugin = void 0;
/// <reference types="cypress" />
const chrome_remote_interface_1 = __importDefault(require("chrome-remote-interface"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const v8_to_istanbul_1 = __importDefault(require("v8-to-istanbul"));
const istanbul_lib_coverage_1 = __importDefault(require("istanbul-lib-coverage"));
const istanbul_lib_report_1 = __importDefault(require("istanbul-lib-report"));
const istanbul_reports_1 = __importDefault(require("istanbul-reports"));
const picomatch = require('picomatch');
const debug = require('debug')('cypress-native-coverage');
function nativeCoveragePlugin(on, options = {}) {
if (typeof options !== 'object') {
throw new Error('cypress-native-coverage plugin options must be an object');
}
const ELECTRON_DEBUG_PORT = /remote-debugging-port\s*=\s*(\d+)/.exec(process.env.ELECTRON_EXTRA_LAUNCH_ARGS || '');
let { basedir = process.cwd(), outdir = 'build', port = ELECTRON_DEBUG_PORT ? parseInt(ELECTRON_DEBUG_PORT[1]) : 8500, include, exclude, reports = [], merge = false } = options;
const { readFile, writeFile } = fs_1.default.promises;
debug(`basedir: ${basedir}`);
const OUTDIR = path_1.default.resolve(basedir, outdir);
const NYC_OUTPUT = path_1.default.resolve(basedir, '.nyc_output');
const NYC_OUTFILE = path_1.default.join(NYC_OUTPUT, `out.json`);
try {
fs_1.default.mkdirSync(NYC_OUTPUT, { recursive: true });
}
catch (e) {
}
let cdp = null;
let attempts = 0;
let coverageMap;
let merged;
async function startCoverage() {
try {
cdp = await (0, chrome_remote_interface_1.default)({ port });
cdp.on('disconnect', () => {
debug('disconnected from chrome debugging protocol');
cdp = null;
});
debug(`connected to chrome debugging protocol on port ${port}`);
}
catch (ignored) {
if (++attempts < 10) {
setTimeout(startCoverage, 50);
}
else {
console.error(`unable to connect to chrome debugging protocol on port ${port}`);
}
return;
}
coverageMap = istanbul_lib_coverage_1.default.createCoverageMap();
if (merge)
merged = readFile(NYC_OUTFILE, 'utf8')
.then(text => {
const data = JSON.parse(text);
coverageMap.merge(data);
debug('merged existing coverage map');
})
.catch(() => {
console.error('unable to merge', NYC_OUTFILE);
});
await cdp.Profiler.enable();
await cdp.Profiler.startPreciseCoverage({
detailed: true,
callCount: true,
allowTriggeredUpdates: true
});
debug('started precise coverage');
}
const includes = include && picomatch(include);
const excludes = exclude && picomatch(exclude);
const isMatch = include
? exclude
? (path) => includes(path) && !excludes(path) : includes
: exclude
? excludes : () => false;
async function takeCoverage() {
const coverage = await cdp.Profiler.takePreciseCoverage();
debug('taken precise coverage');
for (const { url, functions } of coverage.result) {
if (url)
try {
const { pathname } = new URL(url);
if (isMatch(pathname)) {
debug('matched', pathname);
const converter = (0, v8_to_istanbul_1.default)(path_1.default.join(OUTDIR, pathname), 0, undefined, pathname => {
const relative = path_1.default.relative(basedir, pathname).replace(/\\/g, '/');
const accept = relative.startsWith('src') || relative.startsWith('../../src');
return !accept;
});
await converter.load();
converter.applyCoverage(functions);
coverageMap.merge(converter.toIstanbul());
}
}
catch (ignored) {
debug(`failed to collect coverage result for: ${url}`);
}
}
}
async function stopCoverage() {
await cdp.Profiler.stopPreciseCoverage();
debug('stopped precise coverage');
}
async function writeCoverageMap() {
await writeFile(NYC_OUTFILE, JSON.stringify(coverageMap.toJSON()));
}
async function writeReports() {
const context = istanbul_lib_report_1.default.createContext({
dir: path_1.default.resolve(basedir, 'coverage'),
coverageMap
});
for (const reporter of reports) {
istanbul_reports_1.default.create(reporter, {
skipEmpty: false
}).execute(context);
}
}
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome' || browser.name === 'edge' || browser.name === 'electron') {
let rdpArg = launchOptions.args.find(arg => arg.startsWith('--remote-debugging-port'));
if (!rdpArg) {
// Too bad this code doesn't work with electron, you have to rely to the env property:
// ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=????
rdpArg = `--remote-debugging-port=${port}`;
launchOptions.args.push(rdpArg);
}
if (!options.port) {
port = parseInt(rdpArg.split('=')[1]);
}
setTimeout(startCoverage, 50);
}
else {
console.error(`native coverage is not available in '${browser.name}'`);
}
return launchOptions;
});
on('after:spec', async (spec) => {
if (cdp) {
await merged;
await takeCoverage();
await stopCoverage();
}
});
on('after:run', () => Promise.all([
writeCoverageMap(),
writeReports()
]));
}
exports.nativeCoveragePlugin = nativeCoveragePlugin;
exports.default = nativeCoveragePlugin;
//# sourceMappingURL=index.js.map