End-to-End Testing with Playwright

TestingPlaywrightE2EFrontend
Share on LinkedIn Share on X Share on Reddit Share on HN Share on Bluesky

Our Cypress suite took 45 minutes and failed on unrelated tests weekly. We rewrote the critical paths in Playwright — twelve tests covering signup, checkout, and account management. Runtime dropped to four minutes. Failures dropped to near zero because Playwright's auto-waiting eliminated the timing issues that caused 80% of our Cypress flakes.

Playwright is a browser automation framework for end-to-end testing. It controls real browsers programmatically, simulating user interactions and asserting on page state. Unlike unit tests that mock the browser, E2E tests verify the entire stack works together.

Setup and first test

npm init playwright@latest
import { test, expect } from "@playwright/test";

test("complete purchase flow", async ({ page }) => {
  await page.goto("/products/widget");
  await page.getByRole("button", { name: "Add to Cart" }).click();
  await page.getByRole("link", { name: "Cart" }).click();
  await page.getByRole("button", { name: "Checkout" }).click();
  await page.getByLabel("Email").fill("[email protected]");
  await page.getByRole("button", { name: "Pay" }).click();
  await expect(page.getByText("Order confirmed")).toBeVisible();
});

Playwright auto-waits for elements to be visible, enabled, and stable before interacting.

Locator strategies

Prefer user-facing locators: getByRole, getByLabel, getByText, getByTestId. Avoid CSS classes that change with styling.

Network interception

await page.route("**/api/payments", (route) => {
  route.fulfill({ status: 402, body: JSON.stringify({ error: "Card declined" }) });
});

Intercept for error scenarios. Use real APIs for happy-path tests when a test environment is available.

Page Object Model

Encapsulate page interactions to reduce duplication. When the checkout form redesigns, update one file instead of twenty tests.

Auth setup project

Log in once in a setup project, save storage state, reuse across tests — don't click through login in every spec.

Parallel execution

export default defineConfig({
  fullyParallel: true,
  retries: process.env.CI ? 1 : 0,
  use: { baseURL: "http://localhost:3000", trace: "on-first-retry" },
  webServer: { command: "npm run dev", port: 3000 },
});

Test data management

Seed data via API in beforeEach, not through the UI. A test seed endpoint creates known state in milliseconds.

Operational checklist for teams

Assign an owner for each recurring failure mode. Measure baseline before changes and compare after two sprints. Document the fix in the team wiki or runbook so the next engineer does not rediscover it. Schedule a quarterly review of metrics, tooling, and process — what reduced flakes or improved quality last quarter should be doubled down on; what did not work should be retired explicitly rather than left on the books.

Operational checklist for teams

Assign an owner for each recurring failure mode. Measure baseline before changes and compare after two sprints. Document the fix in the team wiki or runbook so the next engineer does not rediscover it. Schedule a quarterly review of metrics, tooling, and process — what reduced flakes or improved quality last quarter should be doubled down on; what did not work should be retired explicitly rather than left on the books.

Operational checklist for teams

Assign an owner for each recurring failure mode. Measure baseline before changes and compare after two sprints. Document the fix in the team wiki or runbook so the next engineer does not rediscover it. Schedule a quarterly review of metrics, tooling, and process — what reduced flakes or improved quality last quarter should be doubled down on; what did not work should be retired explicitly rather than left on the books.

Operational checklist for teams

Assign an owner for each recurring failure mode. Measure baseline before changes and compare after two sprints. Document the fix in the team wiki or runbook so the next engineer does not rediscover it. Schedule a quarterly review of metrics, tooling, and process — what reduced flakes or improved quality last quarter should be doubled down on; what did not work should be retired explicitly rather than left on the books.

Operational checklist for teams

Assign an owner for each recurring failure mode. Measure baseline before changes and compare after two sprints. Document the fix in the team wiki or runbook so the next engineer does not rediscover it. Schedule a quarterly review of metrics, tooling, and process — what reduced flakes or improved quality last quarter should be doubled down on; what did not work should be retired explicitly rather than left on the books.

Flake resistance patterns

Playwright's auto-wait reduces flakes but does not eliminate them. Use locator API over raw selectors; chain getByRole for accessibility-aligned queries. Isolate tests with fresh browser context per test — shared state causes order-dependent failures. Run E2E against staging with production-like data seed, not empty databases. Parallelize by spec file, not by test within file, when tests share database state.

Network conditioning in CI

Run one smoke spec per PR on default network and nightly suite with slow3G emulation for checkout flow. Service workers and HTTP/3 behave differently under throttle — catches timeouts unit tests miss. Record trace on first retry only to balance artifact storage.

Test data isolation

Each Playwright worker gets unique email prefix {workerIndex}[email protected] — parallel tests never collide on unique email constraint. Global setup seeds reference data once; tests only create deltas.

Resources

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

testing playwright e2e rollout

Field RUM on Android 4G. RDS Proxy where relevant. Rollback in PR.

An operator's checklist for testing playwright e2e

Test strategy for testing playwright e2e should buy confidence per minute of CI. Pyramid vs trophy debates matter less than owning flaky tests and testing the contracts that break in prod.

For testing playwright e2e:

Track flake rate as a first-class metric; quarantine with an expiry, do not delete coverage silently.

Signal Target Alarm
Crawl / index ratio Team-defined SLO Page on burn rate
Rich result valid % Baseline − noise Ticket if sustained
Organic landing LCP Budget cap Weekly review

Metrics and alarms for testing playwright e2e

Reviewers should challenge assumptions encoded in testing playwright e2e: defaults copied from tutorials, timeouts that exceed upstream SLAs, and authz checks applied only on the primary UI path. Require a short threat or failure note in the PR when the change touches a trust boundary.

Concrete probes:

  1. Scenario A for testing playwright e2e: partial dependency outage — prove clients degrade gracefully and retries do not amplify load.
  2. Scenario B for testing playwright e2e: bad config shipped — prove rollback within the declared RTO without data corruption.
  3. Scenario C for testing playwright e2e: traffic 3× baseline — prove autoscaling or shedding keeps the golden journey healthy.

Cross-team contracts for testing playwright e2e

Roll out testing playwright e2e behind a flag or weighted route when possible. Start with internal users or a low-risk geography. Watch the signals in the table for at least one full business cycle before calling the migration done. Keep the previous path warm until error budgets stabilize.

Document the owner, the dashboard, and the single command that reverts the change. If that sentence is hard to write, the design is not ready for production traffic.

Developer experience when changing testing playwright e2e

Detail 1 (433): for testing playwright e2e, define the contract between producers and consumers explicitly — payload shape, timeout, and idempotency key. When developer experience when changing testing playwright e2e becomes painful, it is usually because that contract was implicit.

I keep a short matrix: who can break testing playwright e2e, how we detect it within five minutes, and who is paged. Update the matrix when ownership moves. Add one synthetic check that exercises the failure path, not only the happy path. Prefer checks that run continuously over quarterly manual reviews that everyone skips under deadline pressure.

If you only remember one thing about testing playwright e2e: optimize for reversible decisions. Reversibility beats cleverness when the incident channel is busy and the blast radius is unclear.

Observability cardinality around testing playwright e2e

Detail 2 (761): for testing playwright e2e, define the contract between producers and consumers explicitly — payload shape, timeout, and idempotency key. When observability cardinality around testing playwright e2e becomes painful, it is usually because that contract was implicit.

I keep a short matrix: who can break testing playwright e2e, how we detect it within five minutes, and who is paged. Update the matrix when ownership moves. Add one synthetic check that exercises the failure path, not only the happy path. Prefer checks that run continuously over quarterly manual reviews that everyone skips under deadline pressure.

If you only remember one thing about testing playwright e2e: optimize for reversible decisions. Reversibility beats cleverness when the incident channel is busy and the blast radius is unclear.

Frequently asked questions

How is Playwright different from Cypress?

Playwright runs tests out-of-process, supports multiple browser contexts and tabs in one test, and has built-in cross-browser support for Chromium, Firefox, and WebKit. Cypress runs inside the browser, which limits multi-tab and multi-origin testing. Playwright's auto-waiting waits for elements to be actionable before interacting. Choose Playwright for cross-browser CI and complex multi-page flows.

How many E2E tests should I write?

Fewer than you think. Cover critical user journeys only: signup, login, core purchase flow, and one happy path per major feature. Aim for 20-50 E2E tests for a medium app, not 500. Everything else should be unit and integration tests.

How do I make Playwright tests less flaky?

Use Playwright's built-in auto-waiting — never add manual sleep(). Mock external APIs with page.route(). Use data-testid attributes for selectors. Run each test with isolated browser context. Seed test data via API before the test, not through the UI.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →