test(street-level): fail only unexplained panel-press misses in CI

CI on fb024d5 failed step 24 with QA_FAIL_ON_RETRY=1: the press after a
double-click dock landed on the globe because the panel, showing a photo
that was still settling, moved 116 px up and lost 46 px of height between
the pre-press hit check and the press (box 1058,382 330x471 -> 1058,266
330x425, from the new diagnostics and the uploaded screenshot). That is
the panel's own content changing its layout, not the rail race fixed in
9dde7b4.

- Fail-on-retry now fails a miss only when the panel's box was the same
  at the hit check and at the press (the pointer missed a header that
  stood still); a miss the panel moving explains is retried with a
  ::warning:: naming the move. New harness test with a moving panel.
- Step 24 waits for the open photo and its sequence to finish loading
  before pressing the header again.

Verified: strict fixture gate on a production build 28 ok + 2 "no Google
3D" skips and the panel gate 6/6, both with QA_FAIL_ON_RETRY=1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-06 17:07:45 +01:00
1 parent fb024d582b
commit db79027998
4 files changed
+49 -6

No files matched your search

+14 -2
View File
@@ -12,7 +12,11 @@
* Every retry is logged with what led to it (the pre-press hit check, the
* stillness wait, the panel box at the hit check and at the press) and as a
* GitHub `::warning::`. With QA_FAIL_ON_RETRY=1 (or `--fail-on-retry`) a
* retry fails the run instead.
* miss the panel's own movement cannot explain fails the run instead: if its
* box was the same at the hit check and at the press, the pointer missed a
* header that stood still. A panel whose content changed its layout between
* the two (a photo finishing loading, a live count wrapping a line) is
* retried with a warning that names the move.
*/
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -41,6 +45,14 @@ export function pressMissed(attempt) {
}
/** The panel's box, rounded, as `left,top widthxheight`. */
/** Whether the panel's box changed (by more than 2 px) between two reads. */
export function panelMoved(before, after) {
if (!before || !after) return false;
return ['left', 'top', 'width', 'height'].some(
(key) => Math.abs((after[key] ?? NaN) - (before[key] ?? NaN)) > 2,
);
}
export function formatBox(box) {
if (!box) return 'unknown';
const r = (value) => Math.round(value ?? NaN);
@@ -268,7 +280,7 @@ async function pressUntilDecided(
retries,
extra: note ? await note() : '',
});
if (failOnRetry)
if (failOnRetry && !panelMoved(box, attempt.pressed))
throw new Error(
`QA_FAIL_ON_RETRY=1 turns this retry into a failure: ${why}`,
);
+3
View File
@@ -1660,6 +1660,9 @@ async function main() {
await step(
'collapsing a floating window docks it as the rail strip',
async () => {
// The double-click just docked a panel showing a photo: let the photo
// and its sequence finish loading before pressing the header again.
await uiUntil((u) => !u.street.loading && !u.sequence.loading);
await floatPanel();
await page.click(
'.panel-collapse-btn[data-collapse-target="street-level-panel"]',