Skip to main content

Playwright

End-to-end testing framework by Microsoft that supports Chromium, Firefox, and WebKit. Auto-waiting, parallel execution, the Trace Viewer for debugging, a codegen recorder, and built-in API testing — all in one modern testing toolkit.

✓ Chromium, Firefox, WebKit ✓ Auto-waiting ✓ Trace Viewer ✓ Codegen recorder ✓ API testing ✓ Parallel execution

What is Playwright?

Playwright is an open-source end-to-end testing framework developed by Microsoft. It automates real browser instances — Chromium (Chrome/Edge), Firefox, and WebKit (Safari) — via a single, consistent API. Unlike older automation tools, Playwright was designed from the ground up for modern web applications: single-page apps, server-rendered apps, multi-tab flows, iframes, and web components.

Tests run in parallel by default, use smart auto-waiting to eliminate flakiness, and can target mobile device emulation. The Trace Viewer records a full timeline of every test action for deterministic debugging. Codegen records user interactions and produces test code automatically. Playwright supports TypeScript, JavaScript, Python, Java, and C#.

Key Features

Installation

# Create a new project with Playwright pre-configured
npm init playwright@latest

# Add Playwright to an existing project
npm install --save-dev @playwright/test

# Install browser binaries (Chromium, Firefox, WebKit)
npx playwright install

# Run tests
npx playwright test

# Run tests with UI mode (interactive, time-travel debugging)
npx playwright test --ui

# Generate tests using the codegen tool
npx playwright codegen https://example.com

Writing Tests

Tests are organized with test.describe blocks and individual test() cases. Use accessible locators (getByRole, getByLabel) rather than CSS selectors for resilient tests.

// tests/auth.spec.ts
import { test, expect } from '@playwright/test';

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

    // Fill in the login form
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('s3cret');
    await page.getByRole('button', { name: 'Sign in' }).click();

    // Playwright auto-waits for navigation to complete
    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  });

  test('shows error for wrong password', async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('wrong');
    await page.getByRole('button', { name: 'Sign in' }).click();

    await expect(page.getByText('Invalid credentials')).toBeVisible();
  });
});

Fixtures for Reusable Setup

Fixtures replace beforeEach hooks with composable, type-safe setup. An auth fixture logs in once and passes the authenticated page to each test that needs it — without repeating the login steps.

// fixtures/auth.ts — a custom fixture that pre-authenticates
import { test as base, expect } from '@playwright/test';

type AuthFixtures = {
  authenticatedPage: Page;
};

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    // Log in once, share the authenticated browser state
    await page.goto('/login');
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('adminpass');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await page.waitForURL('/dashboard');
    await use(page);
  },
});

// Use in tests
test('admin can delete a user', async ({ authenticatedPage }) => {
  await authenticatedPage.goto('/users');
  // ...
});

API Testing

The request fixture gives you a full HTTP client for testing APIs without launching a browser. Useful for testing REST endpoints or verifying back-end state after a UI action.

// API testing with Playwright — no browser needed
import { test, expect } from '@playwright/test';

test('GET /api/users returns a list', async ({ request }) => {
  const response = await request.get('/api/users');
  expect(response.status()).toBe(200);

  const users = await response.json();
  expect(Array.isArray(users)).toBe(true);
  expect(users.length).toBeGreaterThan(0);
  expect(users[0]).toHaveProperty('id');
});

test('POST /api/users creates a new user', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: { name: 'Alice', email: '[email protected]' }
  });
  expect(response.status()).toBe(201);
  const user = await response.json();
  expect(user.email).toBe('[email protected]');
});

Configuration

playwright.config.ts controls browsers, parallelism, retries, base URL, tracing, and screenshot policies. Define multiple projects to test across browsers and viewports in one run.

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,          // run tests in parallel
  forbidOnly: !!process.env.CI, // fail CI if test.only is left in
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',

  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry', // capture traces on retry
    screenshot: 'only-on-failure',
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
  ],
});

Playwright vs Cypress

Feature Playwright Cypress
Browser supportChrome, Firefox, WebKitChrome-based (FF/WebKit experimental)
Multi-tab / multi-originYesLimited
Test languagesTS, JS, Python, Java, C#JS / TS only
Interactive debuggerUI mode / Trace ViewerBuilt-in Test Runner GUI
Component testingExperimentalStable
Parallelism (free)Yes (built-in)Requires Cypress Cloud

Pros and Cons

Pros

  • ✓ True cross-browser: Chrome, Firefox, and WebKit in one suite
  • ✓ Auto-waiting eliminates most flaky test issues
  • ✓ Trace Viewer makes post-mortem debugging straightforward
  • ✓ Multi-language support (Python, Java, C# in addition to TS/JS)
  • ✓ Free parallelism without a cloud service

Cons

  • ✗ Component testing is still experimental
  • ✗ Interactive test runner less polished than Cypress GUI
  • ✗ Steeper initial learning curve compared to Cypress
  • ✗ No built-in cloud recording dashboard (third-party integrations exist)
  • ✗ Browser binaries (~300 MB) add to CI download time

Frequently Asked Questions

What is Playwright?▼

Playwright is an end-to-end testing framework developed by Microsoft. It automates Chromium, Firefox, and WebKit browsers using a single API, supports auto-waiting, parallel execution, the Trace Viewer for debugging, a codegen tool for recording tests, and built-in API testing. Tests are written in TypeScript, JavaScript, Python, Java, or C#.

How does Playwright's auto-waiting work?▼

Playwright automatically waits for elements to be actionable before interacting with them. When you call page.click() or page.fill(), Playwright waits for the element to be visible, enabled, and stable (not animating) before acting. This eliminates most explicit waits and sleeps that make tests fragile. You can configure a global timeout and per-action timeouts in the config.

What is the Playwright Trace Viewer?▼

The Trace Viewer is a debugging tool that records a full trace of a test run — every action, network request, console log, and DOM snapshot — and lets you replay it in a browser-based timeline. Enable it with trace: 'on-first-retry' in the config (so it only runs on failing retries, keeping storage low). View traces with npx playwright show-report or by uploading to the Playwright CI portal.

How does Playwright codegen work?▼

Running npx playwright codegen <URL> opens a browser with an inspector overlay. As you interact with the page — clicking, typing, navigating — Playwright records each action and generates test code in real time. The generated code uses recommended locator strategies (getByRole, getByLabel, getByText) rather than fragile CSS selectors. It is a useful starting point but generated tests typically need refinement.

Can Playwright test APIs without a browser?▼

Yes. Playwright includes a built-in APIRequestContext (accessed via the request fixture in tests) that lets you make HTTP requests and assert on responses — no browser required. This is useful for testing REST API endpoints, verifying backend state after a UI action, or running mixed browser + API test scenarios in the same test file.

How does Playwright compare to Cypress?▼

Playwright runs tests out-of-process (a separate Node process controlling the browser) while Cypress runs inside the browser. Playwright natively supports Chromium, Firefox, and WebKit; Cypress primarily targets Chrome-based browsers (Firefox and WebKit support is experimental). Playwright has better multi-tab, iframe, and multi-origin support. Cypress has a more polished interactive dashboard and an established cloud recording service. Both are excellent choices for modern E2E testing.

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

Related Articles

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 →