From 003054460bac73568743ca7337196d854eeaf5a9 Mon Sep 17 00:00:00 2001 From: gnezim Date: Wed, 15 Apr 2026 16:33:01 +0300 Subject: [PATCH] Add coverage delta check script --- package.json | 3 +- scripts/ci/check-coverage-delta.mjs | 58 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 scripts/ci/check-coverage-delta.mjs diff --git a/package.json b/package.json index 9666130a..ac5d5a69 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test:coverage": "vitest run --coverage", "lint": "eslint \"src/**/*.{ts,tsx}\" --max-warnings 0", "typecheck": "tsc --noEmit", - "bundle-size": "node scripts/ci/bundle-size-gate.mjs" + "bundle-size": "node scripts/ci/bundle-size-gate.mjs", + "check-coverage": "node scripts/ci/check-coverage-delta.mjs" }, "dependencies": { "@microsoft/signalr": "^10.0.0", diff --git a/scripts/ci/check-coverage-delta.mjs b/scripts/ci/check-coverage-delta.mjs new file mode 100644 index 00000000..e0d904ae --- /dev/null +++ b/scripts/ci/check-coverage-delta.mjs @@ -0,0 +1,58 @@ +/** + * Coverage gate for CI. + * + * Reads coverage-summary.json (Vitest --coverage output) and fails + * if overall line coverage drops below the configured threshold. + * + * Usage: + * pnpm test:coverage + * node scripts/ci/check-coverage-delta.mjs + * + * Environment variable overrides: + * COVERAGE_THRESHOLD – minimum line coverage % (default: 65) + * COVERAGE_FILE – path to coverage-summary.json + */ + +import { readFileSync } from "node:fs"; + +const THRESHOLD = Number(process.env.COVERAGE_THRESHOLD ?? 65); +const COVERAGE_FILE = + process.env.COVERAGE_FILE ?? "coverage/coverage-summary.json"; + +let summary; +try { + summary = JSON.parse(readFileSync(COVERAGE_FILE, "utf8")); +} catch (err) { + console.error(`Could not read ${COVERAGE_FILE}: ${err.message}`); + console.error("Run `pnpm test:coverage` first."); + process.exit(1); +} + +const total = summary.total; +if (!total) { + console.error(`No "total" key in ${COVERAGE_FILE}`); + process.exit(1); +} + +const linePct = total.lines?.pct ?? 0; +const branchPct = total.branches?.pct ?? 0; +const fnPct = total.functions?.pct ?? 0; +const stmtPct = total.statements?.pct ?? 0; + +console.log("\n--- Coverage Report ---\n"); +console.log(` Lines: ${linePct.toFixed(2)}%`); +console.log(` Branches: ${branchPct.toFixed(2)}%`); +console.log(` Functions: ${fnPct.toFixed(2)}%`); +console.log(` Statements: ${stmtPct.toFixed(2)}%`); +console.log(`\n Threshold: ${THRESHOLD}% (lines)\n`); + +if (linePct < THRESHOLD) { + console.error( + `FAILED: Line coverage ${linePct.toFixed(2)}% is below the ${THRESHOLD}% threshold` + ); + process.exit(1); +} else { + console.log( + `PASSED: Line coverage ${linePct.toFixed(2)}% meets the ${THRESHOLD}% threshold` + ); +}