Files
LocalAI/scripts/ui-coverage-check.sh
Richard Palethorpe 8d70855ea6 test: add Go + React UI coverage gates and fill test gaps (#9989)
- Strict monotonic Go coverage gate (make test-coverage-check, 45% baseline)
  run in CI; fixes ginkgo dropping all-but-one coverprofile across multiple
  recursive roots, builds with -tags auth, and folds in the in-process
  tests/e2e suite via --coverpkg.
- React UI e2e coverage (make test-ui-coverage: vite-plugin-istanbul + nyc,
  nix-provided Chromium) plus e2e specs for 6 previously-untested pages, and a
  UI coverage gate (make test-ui-coverage-check) with a small tolerance since
  e2e line coverage jitters ~0.5pp run-to-run.
- pre-commit hook: lint + coverage on Go changes, Playwright e2e + UI coverage
  gate on react-ui changes; install with make install-hooks.
- New Go handler tests (settings, branding), hermetic base64 download test.
- fix(ui): model editor reads vram_display (snake_case), so the VRAM estimate
  renders again; covered by a regression test.

Assisted-by: Claude:claude-opus-4-7

Signed-off-by: Richard Palethorpe <io@richiejp.com>
2026-05-26 22:06:10 +02:00

62 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env sh
#
# ui-coverage-check.sh SUMMARY_JSON BASELINE_FILE
#
# Compares the total line coverage in an nyc coverage-summary.json against a
# committed baseline and fails (exit 1) if it dropped by more than
# UI_COVERAGE_TOLERANCE percentage points (default 1.0). The React UI e2e suite
# drives the real app, so a removed feature or deleted spec shows up as a
# coverage drop here.
#
# UI e2e line coverage is NOT deterministic: async/debounced paths (e.g. the
# VRAM estimate's 500ms debounce) mean identical specs vary ~0.5pp run-to-run.
# The tolerance absorbs that jitter; keep it just above the observed wobble.
# (The Go gate carries a smaller tolerance for the same reason — its e2e slice.)
#
# When coverage rises meaningfully, regenerate and commit the baseline with:
# make test-ui-coverage-baseline
set -eu
summary="${1:?usage: ui-coverage-check.sh SUMMARY_JSON BASELINE_FILE}"
baseline_file="${2:?usage: ui-coverage-check.sh SUMMARY_JSON BASELINE_FILE}"
tolerance="${UI_COVERAGE_TOLERANCE:-1.0}"
if [ ! -f "$summary" ]; then
echo "ui-coverage-check: coverage summary not found: $summary" >&2
echo "ui-coverage-check: run 'make test-ui-coverage' first." >&2
exit 2
fi
if [ ! -f "$baseline_file" ]; then
echo "ui-coverage-check: baseline not found: $baseline_file" >&2
echo "ui-coverage-check: create it with 'make test-ui-coverage-baseline'." >&2
exit 2
fi
current="$(node -e 'const fs=require("fs");process.stdout.write(String(JSON.parse(fs.readFileSync(process.argv[1])).total.lines.pct))' "$summary")"
baseline="$(tr -d '[:space:]%' < "$baseline_file")"
if [ -z "$current" ]; then
echo "ui-coverage-check: could not parse total.lines.pct from $summary" >&2
exit 2
fi
# Fail closed on a missing/garbage baseline rather than letting awk coerce an
# empty or non-numeric value to 0 (which would pass any coverage silently).
case "$baseline" in
'' | *[!0-9.]* )
echo "ui-coverage-check: baseline is empty or non-numeric ('$baseline') in $baseline_file" >&2
echo "ui-coverage-check: regenerate it with 'make test-ui-coverage-baseline'" >&2
exit 2 ;;
esac
if awk -v c="$current" -v b="$baseline" -v t="$tolerance" 'BEGIN { exit !(c < b - t) }'; then
echo "ui-coverage-check: FAIL — UI line coverage ${current}% is below baseline ${baseline}% by more than ${tolerance}pp." >&2
echo "ui-coverage-check: add or restore e2e specs; coverage regressed beyond the jitter tolerance." >&2
exit 1
fi
if awk -v c="$current" -v b="$baseline" 'BEGIN { exit !(c > b) }'; then
echo "ui-coverage-check: OK — UI line coverage rose to ${current}% (baseline ${baseline}%); consider 'make test-ui-coverage-baseline'."
else
echo "ui-coverage-check: OK — UI line coverage ${current}% within ${tolerance}pp of baseline ${baseline}%."
fi