diff --git a/TODO.md b/TODO.md index b494400..bf82a66 100644 --- a/TODO.md +++ b/TODO.md @@ -23,6 +23,11 @@ latest run passes. # Completed Steps +- 2026-10-03: the tap-target check in `make frontend-viewport-test` expects one + visible pin button per WAN host row (issue #46), where it expected at least 10 + of the 26, so pin buttons missing from only some rows now fail it. The host + row count the harness gathers, which the `app-rendered` check also reads, now + counts only the WAN host rows: the local host rows have no pin button - 2026-10-03: `backend/script/lint` says what went wrong with its `.golangci.yml` check (issue #34). On a hash mismatch it says to compare the file with the org standard: if they differ, restore the org standard; if they @@ -211,9 +216,11 @@ latest run passes. - 2026-08-09: automated responsive-layout harness (`make frontend-viewport-test`): digest-pinned headless Chrome driven over CDP against the built `dist/`, viewport widths derived from the breakpoints in - `src/styles.css` ([#13](https://git.eeqj.de/sneak/netwatch/issues/13)). Every - check carries a presence guard so none of them can pass against a page it is - not actually measuring. Found two real layout defects, filed as + `src/styles.css` ([#13](https://git.eeqj.de/sneak/netwatch/issues/13)). The + tap-target and host-row checks each fail when they measured nothing; the + overflow, viewport-edge and clipped-text checks have no such guard of their + own and rely on the `app-rendered` check, which fails the run when the app did + not render. Found two real layout defects, filed as [#42](https://git.eeqj.de/sneak/netwatch/issues/42) and [#43](https://git.eeqj.de/sneak/netwatch/issues/43) - 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile diff --git a/test/viewport/README.md b/test/viewport/README.md index d37a688..9ee28a5 100644 --- a/test/viewport/README.md +++ b/test/viewport/README.md @@ -49,10 +49,11 @@ sizes straddling the breakpoint for the rotation case. excluded: it is a design choice, not breakage. - **tap-targets-44px** — every interactive control is at least 44x44 CSS px on touch viewports, _and_ each selector in the control list matched at least the - number of visible elements it declares. The second half is what stops the - check passing vacuously: with size alone, a renamed class would take its - controls out of the measured set and the check would report "all 0 controls - are at least 44x44" and pass. See below. + number of visible elements it declares: one of each single control, and one + pin button per WAN host row. The second half is what stops the check passing + vacuously: with size alone, a renamed class would take its controls out of the + measured set and the check would report "all 0 controls are at least 44x44" + and pass. See below. - **host-rows-stacked / host-rows-side-by-side** — the rows genuinely reflow. Computed `flex-direction` _and_ the actual geometry are checked, and in the narrow layout the info block and the sparkline must each occupy essentially diff --git a/test/viewport/checks.js b/test/viewport/checks.js index 14c7ebf..d010037 100644 --- a/test/viewport/checks.js +++ b/test/viewport/checks.js @@ -15,7 +15,8 @@ export const MIN_TAP_TARGET_PX = 44; // The controls named in the definition of done, plus the pause button. // Each carries the smallest number of *visible* instances the page has to -// contain for the tap-target oracle to be measuring anything at all. +// contain, worked out from the facts gathered from that page, for the +// tap-target oracle to be measuring every control it should. // // Without those floors the check is inert: `undersized` is empty both when // every control is large enough and when the selectors have gone stale and @@ -24,13 +25,12 @@ export const MIN_TAP_TARGET_PX = 44; // for all three singleton controls vanishing at once — so the floor is per // selector, and one stale selector out of four fails the check. export const INTERACTIVE_CONTROLS = [ - { selector: "#pause-btn", minCount: 1 }, - { selector: "#interval-select", minCount: 1 }, - // One per pinnable host row. `app-rendered` already requires at least - // 10 host rows, so a count below that means the pin buttons stopped - // being rendered per row rather than that there were fewer hosts. - { selector: ".pin-btn", minCount: 10 }, - { selector: "#debug-toggle", minCount: 1 }, + { selector: "#pause-btn", minCount: () => 1 }, + { selector: "#interval-select", minCount: () => 1 }, + // One per WAN host row, so pin buttons missing from even one row fail + // the check rather than only a drop below some fixed number. + { selector: ".pin-btn", minCount: (facts) => facts.rowCount }, + { selector: "#debug-toggle", minCount: () => 1 }, ]; export const INTERACTIVE_SELECTORS = INTERACTIVE_CONTROLS.map( @@ -106,7 +106,7 @@ export function evaluateChecks(facts, viewport, probes) { check( "app-rendered", facts.rowCount >= 10 && facts.numericLatencies >= 5, - `${facts.rowCount} host rows, ${facts.numericLatencies} showing a numeric latency`, + `${facts.rowCount} WAN host rows, ${facts.numericLatencies} showing a numeric latency`, ); const viewportWidth = Math.min(facts.innerWidth, facts.documentClientWidth); @@ -162,7 +162,8 @@ export function evaluateChecks(facts, viewport, probes) { seen.set(target.selector, (seen.get(target.selector) ?? 0) + 1); } const missing = INTERACTIVE_CONTROLS.filter( - (control) => (seen.get(control.selector) ?? 0) < control.minCount, + (control) => + (seen.get(control.selector) ?? 0) < control.minCount(facts), ); const undersized = facts.tapTargets.filter( @@ -184,11 +185,11 @@ export function evaluateChecks(facts, viewport, probes) { const detail = []; if (missing.length > 0) { detail.push( - "oracle is not measuring the page: " + + "oracle is not measuring every control: " + summarise( missing, (c) => - `${c.selector} matched ${seen.get(c.selector) ?? 0} visible element(s), expected at least ${c.minCount}`, + `${c.selector} matched ${seen.get(c.selector) ?? 0} visible element(s), expected at least ${c.minCount(facts)}`, 4, ), ); diff --git a/test/viewport/facts.js b/test/viewport/facts.js index f266e6c..8aaa3a7 100644 --- a/test/viewport/facts.js +++ b/test/viewport/facts.js @@ -173,7 +173,9 @@ export function collectLayoutFacts(options) { clipped, tapTargets, rows, - rowCount: document.querySelectorAll(".host-row").length, + // WAN host rows only: each has a pin button, and the tap-target + // check expects one per row. The local host rows have none. + rowCount: document.querySelectorAll("#wan-hosts .host-row").length, numericLatencies: Array.from( document.querySelectorAll(".latency-value"), ).filter((el) => /\d/.test(el.textContent)).length,