File size: 7,586 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
import { relative } from 'pathe';
import { m as mm } from './vendor/index.xL8XjTLv.js';
import './vendor/_commonjsHelpers.jjO7Zipk.js';
import 'util';
import 'path';
const THRESHOLD_KEYS = ["lines", "functions", "statements", "branches"];
const GLOBAL_THRESHOLDS_KEY = "global";
class BaseCoverageProvider {
/**
* Check if current coverage is above configured thresholds and bump the thresholds if needed
*/
updateThresholds({ thresholds: allThresholds, perFile, configurationFile, onUpdate }) {
let updatedThresholds = false;
const config = resolveConfig(configurationFile);
assertConfigurationModule(config);
for (const { coverageMap, thresholds, name } of allThresholds) {
const summaries = perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()];
const thresholdsToUpdate = [];
for (const key of THRESHOLD_KEYS) {
const threshold = thresholds[key] ?? 100;
const actual = Math.min(...summaries.map((summary) => summary[key].pct));
if (actual > threshold)
thresholdsToUpdate.push([key, actual]);
}
if (thresholdsToUpdate.length === 0)
continue;
updatedThresholds = true;
for (const [threshold, newValue] of thresholdsToUpdate) {
if (name === GLOBAL_THRESHOLDS_KEY) {
config.test.coverage.thresholds[threshold] = newValue;
} else {
const glob = config.test.coverage.thresholds[name];
glob[threshold] = newValue;
}
}
}
if (updatedThresholds) {
console.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
onUpdate();
}
}
/**
* Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
*/
checkThresholds({ thresholds: allThresholds, perFile }) {
for (const { coverageMap, thresholds, name } of allThresholds) {
if (thresholds.branches === void 0 && thresholds.functions === void 0 && thresholds.lines === void 0 && thresholds.statements === void 0)
continue;
const summaries = perFile ? coverageMap.files().map((file) => ({
file,
summary: coverageMap.fileCoverageFor(file).toSummary()
})) : [{
file: null,
summary: coverageMap.getCoverageSummary()
}];
for (const { summary, file } of summaries) {
for (const thresholdKey of ["lines", "functions", "statements", "branches"]) {
const threshold = thresholds[thresholdKey];
if (threshold !== void 0) {
const coverage = summary.data[thresholdKey].pct;
if (coverage < threshold) {
process.exitCode = 1;
let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${threshold}%)`;
if (perFile && file)
errorMessage += ` for ${relative("./", file).replace(/\\/g, "/")}`;
console.error(errorMessage);
}
}
}
}
}
}
/**
* Constructs collected coverage and users' threshold options into separate sets
* where each threshold set holds their own coverage maps. Threshold set is either
* for specific files defined by glob pattern or global for all other files.
*/
resolveThresholds({ coverageMap, thresholds, createCoverageMap, root }) {
const resolvedThresholds = [];
const files = coverageMap.files();
const filesMatchedByGlobs = [];
const globalCoverageMap = createCoverageMap();
for (const key of Object.keys(thresholds)) {
if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key))
continue;
const glob = key;
const globThresholds = resolveGlobThresholds(thresholds[glob]);
const globCoverageMap = createCoverageMap();
const matchingFiles = files.filter((file) => mm.isMatch(relative(root, file), glob));
filesMatchedByGlobs.push(...matchingFiles);
for (const file of matchingFiles) {
const fileCoverage = coverageMap.fileCoverageFor(file);
globCoverageMap.addFileCoverage(fileCoverage);
}
resolvedThresholds.push({
name: glob,
coverageMap: globCoverageMap,
thresholds: globThresholds
});
}
for (const file of files.filter((file2) => !filesMatchedByGlobs.includes(file2))) {
const fileCoverage = coverageMap.fileCoverageFor(file);
globalCoverageMap.addFileCoverage(fileCoverage);
}
resolvedThresholds.unshift({
name: GLOBAL_THRESHOLDS_KEY,
coverageMap: globalCoverageMap,
thresholds: {
branches: thresholds.branches,
functions: thresholds.functions,
lines: thresholds.lines,
statements: thresholds.statements
}
});
return resolvedThresholds;
}
/**
* Resolve reporters from various configuration options
*/
resolveReporters(configReporters) {
if (!Array.isArray(configReporters))
return [[configReporters, {}]];
const resolvedReporters = [];
for (const reporter of configReporters) {
if (Array.isArray(reporter)) {
resolvedReporters.push([reporter[0], reporter[1] || {}]);
} else {
resolvedReporters.push([reporter, {}]);
}
}
return resolvedReporters;
}
hasTerminalReporter(reporters) {
return reporters.some(([reporter]) => reporter === "text" || reporter === "text-summary" || reporter === "text-lcov" || reporter === "teamcity");
}
toSlices(array, size) {
return array.reduce((chunks, item) => {
const index = Math.max(0, chunks.length - 1);
const lastChunk = chunks[index] || [];
chunks[index] = lastChunk;
if (lastChunk.length >= size)
chunks.push([item]);
else
lastChunk.push(item);
return chunks;
}, []);
}
}
function resolveGlobThresholds(thresholds) {
if (!thresholds || typeof thresholds !== "object")
return {};
return {
lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
statements: "statements" in thresholds && typeof thresholds.statements === "number" ? thresholds.statements : void 0
};
}
function assertConfigurationModule(config) {
try {
if (typeof config.test.coverage.thresholds !== "object")
throw new Error("Expected config.test.coverage.thresholds to be an object");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Unable to parse thresholds from configuration file: ${message}`);
}
}
function resolveConfig(configModule) {
const mod = configModule.exports.default;
try {
if (mod.$type === "object")
return mod;
if (mod.$type === "function-call") {
if (mod.$args[0].$type === "object")
return mod.$args[0];
if (mod.$args[0].$type === "arrow-function-expression" && mod.$args[0].$body.$type === "object")
return mod.$args[0].$body;
}
} catch (error) {
throw new Error(error instanceof Error ? error.message : String(error));
}
throw new Error("Failed to update coverage thresholds. Configuration file is too complex.");
}
export { BaseCoverageProvider };
|