A Playwright test clicks a button that is visibly on screen, and roughly one run in twenty nothing happens. The button is server-rendered but the app hydrates after load. Why does the click get lost, and how do you fix the flake properly?
Playwright actionability checks pass on server-rendered markup because the element is attached, visible, stable and enabled - none of which imply React has attached its listener. The click lands on inert HTML, so this is a race with hydration, not a timing problem. Wait on a readiness signal the app exposes deliberately.
Answer
Short answer
Playwright waits for the element to be attached, visible, stable, enabled and able to receive events. Server-rendered HTML satisfies every one of those before any JavaScript has run. The button is genuinely there and genuinely clickable — it simply has no onClick listener yet, so the event dispatches into markup that does nothing with it. Nothing throws, nothing retries, and the test proceeds to fail on the assertion after it.
Was this answer helpful?
Likely follow-ups
- Why is waitForTimeout the wrong fix even if you set it to five seconds?
- What is wrong with waiting for networkidle here?
- How would you expose a hydration-complete signal without shipping test-only code to production?
- A real user clicks that fast too. What does the product do about it?
- How do you prove the flake is gone rather than rarer?
Related questions
- What does auto-waiting do in Playwright and Cypress, how do the two differ architecturally, and what can neither of them test?mediumAlso on playwright and e2e-testing6 min
- How do you get a browser test into the state it needs to start, without clicking through the UI to get there?mediumAlso on playwright6 min
- A modal passed design and QA review, but keyboard users report they can tab out of it into the page behind, and once they do they cannot get back or close it. Diagnose it and tell me what a correct dialog does.hardSame kind of round: scenario4 min
- How do you tell whether a value escapes to the heap, and how would you find the allocations that are costing you?hardSame kind of round: concept6 min