might-cli
Version:
A no-code solution for performing frontend tests
130 lines (129 loc) • 5.17 kB
JavaScript
import v8toIstanbul from 'v8-to-istanbul';
import { createContext } from 'istanbul-lib-report';
import libCoverage from 'istanbul-lib-coverage';
import reports from 'istanbul-reports';
import { join } from 'path';
import fs from 'fs-extra';
import { SourceMapConsumer } from 'source-map';
import convert from 'convert-source-map';
import nanomatch from 'nanomatch';
export async function coverage(coverageCollection, meta, dir, exclude) {
const mainMap = libCoverage.createCoverageMap();
const processed = {};
for (const entry of coverageCollection) {
const entryPath = new URL(entry.url).pathname;
if (!entryPath.length || nanomatch.any(entryPath.replace('\\', ''), exclude, undefined))
continue;
if (!processed[entryPath]) {
const sourcemap = await getSourcemap(entry);
if (sourcemap) {
const consumer = await SourceMapConsumer.with(sourcemap, undefined, c => c);
await Promise.all(consumer.sources
.map(async (path, id) => {
var _a;
let relative = path.replace(/\\/g, '');
if (path.includes('://'))
relative = path.substring(path.indexOf('://') + 3);
relative = relative.replace(new RegExp(process.env.PWD, 'g'), '');
if (relative.startsWith(`/${meta.name}/`))
relative = relative.replace(`/${meta.name}`, '');
else if (relative.startsWith(`${meta.name}/`))
relative = relative.replace(meta.name, '');
if (!relative.startsWith('/'))
relative = '/' + relative;
if (!nanomatch.any(relative, exclude, undefined)) {
try {
await fs.outputFile(join(dir, relative), (_a = sourcemap === null || sourcemap === void 0 ? void 0 : sourcemap.sourcesContent) === null || _a === void 0 ? void 0 : _a[id]);
}
catch (err) {
}
}
}));
}
processed[entryPath] = v8toIstanbul('', 0, {
sourceMap: sourcemap ? { sourcemap } : undefined,
source: entry.source
});
await processed[entryPath].load();
}
const converter = processed[entryPath];
converter.applyCoverage(entry.functions);
const data = converter.toIstanbul();
Object.entries(data).forEach(([key, file]) => {
key = key
.replace(/\\/g, '')
.replace(new RegExp(process.env.PWD, 'g'), '');
if (key.startsWith(`/${meta.name}/`))
key = key.replace(`/${meta.name}`, '');
else if (key.startsWith(`${meta.name}/`))
key = key.replace(meta.name, '');
if (!key.startsWith('/'))
key = '/' + key;
if (nanomatch.any(key, exclude, undefined))
return;
file.path = join(dir, key);
try {
const exists = mainMap.fileCoverageFor(file.path);
mergeMappings(file, exists);
exists.merge(file);
}
catch {
mainMap.addFileCoverage(file);
}
});
}
const context = createContext({
dir,
defaultSummarizer: 'nested',
coverageMap: mainMap
});
try {
reports.create('json').execute(context);
reports.create('clover').execute(context);
reports.create('lcov').execute(context);
}
catch (e) {
}
return [Math.floor(mainMap.getCoverageSummary().lines.pct), mainMap.files().map(file => {
const coverage = Math.floor(mainMap.fileCoverageFor(file).toSummary().lines.pct);
const uncoveredLines = mainMap.fileCoverageFor(file).getUncoveredLines();
return {
name: file.replace(dir, ''),
coverage,
uncoveredLines
};
})];
}
function mergeMappings(file, target) {
Object.entries(file.fnMap).forEach(([key, mapping]) => {
if (target.fnMap[key] === undefined)
target.fnMap[key] = mapping;
});
Object.entries(file.branchMap).forEach(([key, mapping]) => {
if (target.branchMap[key] === undefined)
target.branchMap[key] = mapping;
});
}
async function getSourcemap(entry) {
var _a;
let sourcemap;
try {
sourcemap = (_a = convert.fromSource(entry.source)) === null || _a === void 0 ? void 0 : _a.sourcemap;
if (sourcemap)
return sourcemap;
}
catch {
}
try {
const fetch = (await import('node-fetch')).default;
const response = await fetch(`${entry.url}.map`);
if (response.status !== 200)
throw new Error();
const buffer = Buffer.from(await response.arrayBuffer());
sourcemap = JSON.parse(buffer.toString('utf-8'));
if (sourcemap)
return sourcemap;
}
catch {
}
}