|
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 { |
|
|
|
|
|
|
|
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(); |
|
} |
|
} |
|
|
|
|
|
|
|
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); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
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 }; |
|
|