Stability Checklist
When to Use
Use this checklist before any baseline is captured. Skip a step here, get a flake later.
Decision
| Symptom | Most likely missing checklist item |
|---|---|
| First baseline capture differs slightly each time | Fonts (3) or network (5) |
| Hover state shows briefly | Mouse position not normalized — move it to (0,0) before capture |
| Image regions vary | Lazy-loading (4) |
| Time/counter regions vary | Masking (6) |
| Cross-OS diffs | Pinning (10) |
| A batch of captures fails at once, with no site change | Not a stability problem — worker concurrency. Playwright defaults to 50% of your cores against a single-container backend; cap workers first |
Pattern
The 10-point checklist:
- Disable animations —
animations: 'disabled'(Playwright default fortoHaveScreenshot) - Hide carets / blinking cursors —
caret: 'hide'(Playwright default) - Wait for fonts loaded —
await page.evaluate(() => document.fonts.ready) - Wait for images loaded (including lazy) — scroll-to-bottom-then-top, or eager-load
- Wait for network idle —
await page.waitForLoadState('networkidle') - Mask dynamic regions —
mask: [page.locator('[data-vrt-mask]')] - Normalize scroll position —
await page.evaluate(() => window.scrollTo(0, 0)) - Hide cookie banners / GDPR overlays — set the consent cookie before navigation
- Clear focus indicators that shift across OSes —
(document.activeElement as HTMLElement)?.blur() - Pin the Chromium version — use the official Playwright Docker image (
mcr.microsoft.com/playwright:v1.X.Y-noble) for both local capture and CI
Global stability stylesheet via expect.toHaveScreenshot.stylePath:
/* tests/playwright/screenshot.css */
*, *::before, *::after {
animation: none !important;
transition: none !important;
caret-color: transparent !important;
}
iframe { visibility: hidden; }
[data-vrt-mask] { visibility: hidden; }
// playwright.config.ts
expect: {
toHaveScreenshot: { stylePath: './screenshot.css' },
}
Common Mistakes
- Wrong: adding VR tests without disabling animations → Right: universal first-month flakiness cause
- Wrong:
waitForLoadState('networkidle')on a page with long-poll/SSE → Right: hangs forever; switch to'domcontentloaded'plus explicit waits - Wrong: dismissing cookie banner via UI click each test → Right: slow and flaky; set the cookie before navigation instead