test(machine-details): fix flaky "disarms once a save settles cleanly" (PP-78tm) - #2092
Conversation
…" (PP-78tm) The test waited only for the "Saved" note to appear, then checked the beforeunload guard had disarmed in a separate synchronous assertion. The note text and the listener teardown settle in different effect passes, so on a loaded CI runner the check could sample the window where the note had settled but the guard had not yet unsubscribed. The flake reddened the required CI Gate, including on a main commit (run 33130774346). Assert the disarm inside the same waitFor as the "Saved" note so the poll retries until both hold together. The later synchronous "Discard unsaved changes?" query is deliberately left synchronous — it is load-bearing and unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DeLnU9YW1LtvSN9fpE5Y41
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
What
Fixes the flaky unit test
MachineDetailsForm > unsaved-changes navigation guard > disarms once a save settles cleanly(bead PP-78tm).Why it flaked
The test waited only for the
"Saved"note to render, then checked that thebeforeunloadguard had disarmed in a separate synchronous assertion right after thewaitFor:The
"Saved"note (machine-details-form.tsx:443-447) and thebeforeunloadlistener teardown (:159-168) both derive from the sameisDirtyfalse-flip, but the note text lands in the commit phase while the listener teardown lands in the passive-effect phase. On a loaded CI runner the synchronous check could sample the window where the note had settled but the guard had not yet unsubscribed — a falsetrue. It reddened the required CI Gate, including on amaincommit (run33130774346, SHAd139b903, per the bead's 2026-08-29 analysis).The fix
Assert the disarm inside the same
waitForas the"Saved"note, so the poll retries until both conditions hold together:If the guard ever genuinely fails to disarm,
beforeUnloadWasBlocked()staystrue, the callback keeps throwing, andwaitFortimes out and fails — so the test still catches a real regression; it is not weakened. The later, deliberately synchronousqueryByText("Discard unsaved changes?")assertion is load-bearing and left untouched.beforeUnloadWasBlocked()dispatches a fresh cancelablebeforeunloadeach poll; the handler ispreventDefault-only, so repeated dispatch is harmless (noted inline).Scope
One test file,
+8/-2. No component change. Comment-only rationale added; it does not over-claim the exact CI mechanism (the bead records that the split-effect cause was never reproduced locally — this is defensive hardening around the correct RTL pattern, robust regardless of the true trigger).Validation
pnpm exec vitest run <file>: 22/22 pass, across four reruns and once with--coverage(the CI differentiator).Opened unattended by the nightly bead session; carries
ownerless.🤖 Generated with Claude Code
https://claude.ai/code/session_01DeLnU9YW1LtvSN9fpE5Y41
Generated by Claude Code