Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,45 @@ jobs:
path: .ci-artifacts/frontend-next-unit-junit.xml
if-no-files-found: warn
retention-days: 7
# #3318: the tier's first coverage measurement, and the ratchet that
# holds it. It runs here rather than in a new job because everything it
# needs is already in this one -- the node runtime is the image's own
# (#3331), the install is the `npm ci` above, and the test files are the
# same 24 the `npm test` step just ran. A new job would mean a second
# toolchain install to compute a number the first one is already
# positioned to produce.
#
# The measured baseline is 10.95% lines / 4.77% branches over src/, and
# it is low because src/routes/ is 23,450 lines of route module with
# almost no unit tests behind it. That is the honest number, recorded as
# measured, and the ratchet below is what stops it moving without
# somebody writing the new figure down in a commit.
- name: Measure coverage (#3318)
working-directory: arcane/home/honeypot-dashboard/frontend-next
run: npm run test:coverage
# The gate reads the summary the step above just wrote and compares it
# to frontend-next/coverage-baseline.json. No flags: the baseline path
# and the tolerance both live in committed files, so there is no
# command-line knob here that could widen the gate without a diff. It
# also fails if a *.test.ts exists that vitest's own `include:` globs
# would never run -- a test file nothing collects is a file, not a
# check, and it would otherwise sit inside the number below.
- name: Coverage ratchet (#3318)
run: python3 scripts/check-frontend-next-coverage.py
# `error`, not `ignore`/`warn`: "the report was published" should be a
# fact this step can fail on, and a measurement run that produced no
# coverage/ directory is exactly the case where a reader would otherwise
# find an absent report and assume the gate had nothing to say. The
# per-(run, attempt) name and the reused pinned upload-artifact SHA are
# the same reasoning as the JUnit upload above.
- name: Publish the coverage report (#3318)
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: frontend-next-coverage-${{ github.run_id }}-${{ github.run_attempt }}
path: arcane/home/honeypot-dashboard/frontend-next/coverage/
if-no-files-found: error
retention-days: 7
# No live-ES smoke suite here on purpose: port-tests/ needs the real
# cluster over an SSH tunnel to the homeserver (see its README) --
# not reachable from a GitHub-hosted runner, and not appropriate to
Expand Down Expand Up @@ -699,6 +738,21 @@ jobs:
path: .ci-artifacts/frontend-next-unit-junit.xml
if-no-files-found: warn
retention-days: 7
# #3318: twin of the homeserver copy's three steps above, byte-identical
# for the same reason every step in this pair is. Full rationale there.
- name: Measure coverage (#3318)
working-directory: arcane/home/honeypot-dashboard/frontend-next
run: npm run test:coverage
- name: Coverage ratchet (#3318)
run: python3 scripts/check-frontend-next-coverage.py
- name: Publish the coverage report (#3318)
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: frontend-next-coverage-${{ github.run_id }}-${{ github.run_attempt }}
path: arcane/home/honeypot-dashboard/frontend-next/coverage/
if-no-files-found: error
retention-days: 7
# No live-ES smoke suite here on purpose -- see the homeserver twin.
- run: npm run build
working-directory: arcane/home/honeypot-dashboard/frontend-next
Expand Down
8 changes: 8 additions & 0 deletions arcane/home/honeypot-dashboard/frontend-next/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ playwright-report/
# there, never committed -- a committed mutation report is a stale report.
reports/mutation/
.stryker-tmp/

# Coverage output (#3318): the html report, coverage-summary.json and the v8
# raw data. Same reasoning as the two entries above -- regenerated on every
# run, published from CI as an artifact, and a committed copy would be a
# coverage claim about nobody's tree. The ratchet reads the measured
# coverage-summary.json; the *baseline* it compares against is the separate,
# deliberately committed coverage-baseline.json.
coverage/
16 changes: 16 additions & 0 deletions arcane/home/honeypot-dashboard/frontend-next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ for how a commit actually reaches the live host.
`backend-api.sh`, `bff-load.sh`) — see `../port-tests/README.md`. Run
against a real build, not `npm run dev`'s HMR server.

Unit tests are `npm test` (vitest) and `npm run test:browser` (Playwright);
`npm run test:coverage` adds the v8 line/branch measurement over `src/`.
CI runs the measurement in the `frontend-next` pair in
`.github/workflows/quality.yml` and then holds the number with
`scripts/check-frontend-next-coverage.py`, which compares the summary that
run produced against the committed `coverage-baseline.json` and also fails if
a `*.test.ts` exists that the `include:` globs in `vitest.config.ts` would
never collect. The baseline moves only by editing it in a commit, with a
fresh measurement in the same diff; there is no flag that widens the gate.
Measured 2026-09-27 on node 22 (the image's own runtime): 10.95% lines,
4.77% branches, low because `src/routes/` is 23,450 lines of route module
with almost no unit tests behind it. Line coverage also says nothing about
whether an assertion would notice a change to the code — see
`stryker.conf.mjs` and `docs/frontend-mutation-pilot.md` for the pilot that
asks that question instead.

## New-page review checklist (capped-truth discipline, #2179)

When authoring or reviewing a page, check the ways a number can quietly lie.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"issue": 3318,
"recordedOn": "2026-09-27",
"recordedBy": "npm run test:coverage in arcane/home/honeypot-dashboard/frontend-next",
"runtime": "node 22.23.2 -- node:22-alpine@sha256:c610fcdf, the frontend-next image's own FROM line (#3331), so this is the number the shipped runtime measures",
"vitest": "4.1.11",
"coverageProvider": "v8",
"suite": "24 test files, 215 cases, all passing",
"definition": "vitest.config.ts's own coverage block: include src/**, all: true, minus the *.test.ts/*.test.tsx files themselves and the generated src/routeTree.gen.ts. Excluding the route tree lowers this figure (11.63% -> 10.95% lines) rather than raising it, so it is not an exclusion that flatters the number.",
"tolerancePoints": 0.5,
"toleranceCounts": 0,
"toleranceRationale": "tolerancePoints is 0.5 of a percentage point, about 37 of the 7359 measured lines, so ordinary work that adds untested code does not fail the run for it. toleranceCounts is 0: the covered line and branch counts must not fall at all, because a percentage over a 7,359-line denominator cannot see a seven-line regression (measured -- one new seven-line untested module moved lines 10.95% -> 10.94% and the percentage gate stayed green) while the covered count does. A v8 run of an unchanged tree is bit-identical between runs (measured twice, same summary), so neither allowance is slack for measurement noise: both are slack for a change that moves the denominator, and taking it is a decision someone has to write down here.",
"updatePolicy": "The only way to move this number is an explicit commit editing this file, with the new value copied out of a real `npm run test:coverage` run. The gate has no --update flag and no environment override for the tolerance. Lowering coverage must be argued for in that diff, not slipped in beside it.",
"total": {
"lines": { "total": 7359, "covered": 806, "skipped": 0, "pct": 10.95 },
"statements": { "total": 8452, "covered": 859, "skipped": 0, "pct": 10.16 },
"functions": { "total": 2666, "covered": 107, "skipped": 0, "pct": 4.01 },
"branches": { "total": 7250, "covered": 346, "skipped": 0, "pct": 4.77 }
}
}
171 changes: 171 additions & 0 deletions arcane/home/honeypot-dashboard/frontend-next/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions arcane/home/honeypot-dashboard/frontend-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:watch": "vitest",
"test:browser": "playwright test",
"test:property": "vitest run src/lib/appearanceCookie.property.test.ts",
Expand Down Expand Up @@ -46,6 +47,7 @@
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.5",
"@vitejs/plugin-react": "^6.1.1",
"@vitest/coverage-v8": "^4.1.11",
"fast-check": "^4.10.2",
"jsdom": "^30.0.1",
"lru-cache": "^11.2.6",
Expand Down
Loading
Loading