Playwright E2E Testing
Playwright is a modern browser-automation framework for end-to-end (E2E) testing. It drives Chromium, Firefox, and WebKit with a single API, runs headless in CI, auto-waits for elements (eliminating most flaky sleeps), and ships outstanding debugging via traces — a recorded timeline of DOM snapshots, network, and console for every step.
E2E tests give the highest confidence but are the slowest and most fragile, so they belong at the top of the testing pyramid: a small, stable set covering critical user journeys (auth, checkout, publishing), with most logic verified by faster unit and integration tests.
TL;DR
- Use Playwright for high-signal E2E: critical user flows, auth, payments.
- Keep E2E suites small and stable; push logic down to unit/integration tests.
- Make tests resilient: auto-waiting + assertions, never arbitrary sleeps.
- Use traces and screenshots to debug flakes.
Quick Example
Role-based locators plus web-first assertions that auto-wait:
Setup
This scaffolds a playwright.config.*, a tests/ folder, and installs browser binaries for Chromium, Firefox, and WebKit.
Locators: Prefer Roles
Stable, user-facing selectors are the single biggest lever against flakiness:
getByRole— best; matches how users (and assistive tech) perceive the page.getByLabel— form fields.getByText— fine, but watch for ambiguity.
⚠️ Avoid deep CSS chains and nth-child selectors — they break on any markup change. Prefer roles, labels, and
data-testid.
Test Data, State & Auth
- Seed known data in a dedicated test environment.
- Set up state via API calls, not by clicking through the UI — far faster and less flaky.
- Clean up after tests, or run against a disposable database.
Persist auth so tests don't log in every time:
The pattern: run login once in a setup step, save cookies/localStorage (storageState), and reuse it across tests.
Page Object Model
When many tests touch the same screens, the Page Object Model (POM) improves maintainability:
- Keep page objects thin.
- Expose actions and assertions, not raw selectors everywhere.
- Don't over-abstract — E2E tests should stay readable.
Cross-Browser & Visual Testing
Cross-browser runs catch real bugs but are expensive. A common balance: run the full suite on Chromium per PR, and a smaller smoke suite on Firefox/WebKit (or nightly).
Visual regression (screenshot comparison) is valuable for UI-heavy apps — but use deterministic data, disabled animations, and a fixed viewport, and budget for maintenance.
CI/CD & Flake Management
- Run headless, record traces on failure, upload artifacts (screenshots, videos, traces).
- Retry failures with trace capture to diagnose flakes — but treat flaky tests as bugs to fix, not noise to retry forever.
Best Practices
- Auto-wait, never sleep — assert on visibility/navigation/responses instead of fixed delays.
- Set state via API, reserve the UI for the behavior under test.
- Persist auth with storage state to keep tests fast.
- Keep the suite small — a few critical journeys, not a UI re-test of everything.
- Capture traces on failure — they make flake debugging tractable.
Common Mistakes
Arbitrary sleeps
Driving setup through the UI
FAQ
Playwright or Cypress?
Both are excellent. Playwright supports all three browser engines (Chromium/Firefox/WebKit) with true parallelism, runs tests in Node out-of-process, and has first-class tracing — strong for cross-browser and large suites. Cypress runs in-browser with a polished interactive runner and time-travel debugging that many find friendlier. Choose Playwright for breadth and CI scale, Cypress for developer-experience and in-browser debugging.
How do I stop E2E tests from being flaky?
Eliminate non-determinism and timing guesses: use auto-waiting locators and web-first assertions (never waitForTimeout), prefer role/label/data-testid selectors over CSS chains, set up state via API, reset data between tests, and persist auth. When a flake appears, capture a trace and fix the root cause rather than blanket-retrying.
How many E2E tests should I have?
Few — they're the slow, expensive top of the pyramid. Cover the critical revenue/safety journeys (login, checkout, core create/publish flows) and let unit and integration tests handle the rest. A bloated E2E suite is slow and flaky and tends to duplicate coverage you can get faster at lower levels.
How do I set up authentication efficiently?
Log in once in a setup project, save the browser's storageState (cookies + localStorage) to a file, and configure tests to reuse it. This skips the login flow on every test, cutting runtime and a common source of flakiness. Use separate storage states for different roles (admin, user) when you need them.
Related Topics
- Testing — The hub
- Cypress — The main alternative
- Integration Testing — The layer below E2E
- Unit Testing — Where most logic should be tested
- CI/CD — Running E2E in pipelines