Skip to main content

Playwright E2E Testing Guide

The complete reference for Playwright — Microsoft's modern end-to-end testing framework. Covers locators, auto-wait, fixtures, trace viewer, codegen, multi-browser support, and API testing.

✓ Chromium, Firefox, WebKit ✓ Auto-wait built-in ✓ Trace viewer ✓ Codegen recorder ✓ API testing support

What is Playwright?

Playwright is a modern end-to-end browser automation framework developed by Microsoft. Released in 2020, it quickly became the preferred choice for teams that needed reliable cross-browser E2E tests without Selenium's verbosity or Cypress's single-tab limitation. Playwright controls real browser instances through low-level protocols — the Chrome DevTools Protocol for Chromium, Firefox's Remote Protocol, and WebKit's inspector — giving it deep access to browser internals.

The framework's killer features are auto-waiting (no more flaky explicit waits), resilient locators (CSS selectors, ARIA roles, text), and first-class tooling: Trace Viewer for post-mortem debugging, Codegen for recording interactions as test code, and a VS Code extension for running and debugging tests with full IDE integration.

Installation

# Install Playwright with browser binaries
npm init playwright@latest

# Or manually
npm install --save-dev @playwright/test
npx playwright install

# playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests/e2e',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
  ],
});

Core Test Structure: page.goto & Locators

import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
  await page.goto('/login');

  // Prefer ARIA-role locators for resilience
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('secret');
  await page.getByRole('button', { name: 'Sign in' }).click();

  // Assert navigation happened
  await expect(page).toHaveURL('/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

test('shows validation error for empty email', async ({ page }) => {
  await page.goto('/login');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page.getByText('Email is required')).toBeVisible();
});

Fixtures: Sharing Setup Across Tests

// fixtures.ts
import { test as base, expect } from '@playwright/test';

type Fixtures = { authenticatedPage: Page };

export const test = base.extend<Fixtures>({
  authenticatedPage: async ({ page }, use) => {
    // Reusable login before each test
    await page.goto('/login');
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('password');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await page.waitForURL('/dashboard');

    await use(page); // test runs here

    // Teardown (optional)
    await page.goto('/logout');
  },
});

export { expect };

// In your test file
import { test, expect } from './fixtures';

test('dashboard loads user data', async ({ authenticatedPage: page }) => {
  await expect(page.getByText('Welcome back')).toBeVisible();
});

Network Interception & API Testing

// Intercept network requests
test('handles API error gracefully', async ({ page }) => {
  await page.route('/api/users', route =>
    route.fulfill({ status: 500, body: 'Internal Server Error' })
  );

  await page.goto('/users');
  await expect(page.getByText('Something went wrong')).toBeVisible();
});

// Pure API testing (no browser)
test('POST /api/products returns 201', async ({ request }) => {
  const response = await request.post('/api/products', {
    data: { name: 'Widget', price: 9.99 },
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
  });

  expect(response.status()).toBe(201);
  const body = await response.json();
  expect(body).toMatchObject({ id: expect.any(String), name: 'Widget' });
});

Key Features

Use Cases

Comparison: Playwright vs Alternatives

Feature Playwright Cypress Selenium Puppeteer
Browser support Chromium+FF+WebKit Chrome+Firefox+Edge All browsers Chromium only
Auto-wait Built-in Built-in Manual Manual
Multi-tab support Yes No Yes Yes
Trace/debug tooling Trace Viewer Time-travel debug Basic None
API testing Built-in cy.request() No No
Language support JS/TS/Python/Java/C# JS/TS only Many languages JS/TS only

Pros & Cons

Pros

  • True cross-browser including WebKit/Safari
  • Auto-wait eliminates most flakiness
  • Trace Viewer makes CI failures debuggable
  • Multi-tab and iframe support out of the box
  • Supports multiple programming languages
  • Strong Microsoft backing and rapid development

Cons

  • Large binary downloads for browser engines
  • Steeper learning curve than Cypress for beginners
  • No component testing (Cypress has this)
  • Test isolation is per-file, not per-test by default
  • Less visual feedback during test execution than Cypress

Frequently Asked Questions

What is Playwright and how does it differ from Cypress?▼

Playwright is an end-to-end browser automation library developed by Microsoft. Unlike Cypress, which runs inside the browser using JavaScript injection, Playwright controls browsers through the Chrome DevTools Protocol (Chromium), Firefox's remote protocol, and WebKit's inspector protocol — giving it true cross-browser support including Safari (WebKit). Playwright also supports multiple tabs, iframes, file downloads, and network interception natively.

Which browsers does Playwright support?▼

Playwright bundles three browser engines: Chromium (covering Chrome and Edge), Firefox, and WebKit (covering Safari). You can run tests against all three with a single test file. Playwright also supports headed mode (visible browser window), headless mode (CI), and a persistent context that preserves browser state between tests. Mobile device emulation is built in via device descriptors.

What is Playwright's auto-wait feature?▼

Playwright automatically waits for elements to be visible, enabled, and stable before interacting with them. When you call page.click() on a locator, Playwright polls until the element exists in the DOM, is visible, is not obscured, is enabled, and has stable position — then clicks it. This eliminates most explicit waitForSelector and sleep calls that plague Selenium test suites.

What is Playwright Codegen?▼

Playwright Codegen is a code recorder that generates Playwright test code by watching your manual interactions in a browser. Run npx playwright codegen https://example.com to open a browser where every click, type, and navigation generates the corresponding Playwright code in a panel. It's an excellent starting point for new test files, though generated code usually needs cleanup and assertions added.

How does Playwright's Trace Viewer work?▼

The Trace Viewer is a post-mortem debugging tool. When a test fails, Playwright can save a trace file containing a timeline of every action, screenshots before and after each step, DOM snapshots, network logs, and console output. Open it with npx playwright show-trace trace.zip or in the Playwright VS Code extension. Traces make CI failures debuggable without re-running the test.

Can Playwright test APIs without a browser?▼

Yes. Playwright's APIRequestContext lets you make HTTP requests directly, bypassing the browser. Use playwright.request.get(url) or request.post(url, { data: {} }) within your test fixtures. This is useful for seeding test data via API before UI tests, or for pure API contract testing as part of an E2E suite without the overhead of a full browser page.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →