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.
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
- Auto-wait — all actions wait for element readiness automatically, eliminating flaky sleeps
- Trace Viewer — replay failing tests with full DOM snapshots and network logs
- Codegen — record manual interactions as test code with
npx playwright codegen - Multi-browser — Chromium, Firefox, and WebKit in a single test file
- Mobile emulation — device descriptors for responsive testing
- Parallel execution — tests run in parallel workers by default
- API testing — HTTP requests without a browser via request fixture
- VS Code extension — run, debug, and record tests from the editor
Use Cases
- Authentication flows — login, OAuth, session management, MFA
- Form submissions — validation, file uploads, multi-step wizards
- Cross-browser regression — ensure features work in Chrome, Firefox, and Safari
- API contract testing — verify backend responses as part of E2E suite
- Visual regression — screenshot comparison with
toHaveScreenshot() - Accessibility testing — ARIA role locators catch missing accessibility attributes
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.
Related Products
on GumroadDevToolkit Starter Kit
A ready-to-deploy developer tools website with 12 built-in tools. Astro + Preact + Tailwind. Deploy to Cloudflare Pages for free. Built-in SEO, dark mode, and ad-ready layout.
Buy on Gumroad →GitHub Actions CI/CD Templates Pack
12 production-ready GitHub Actions workflow templates covering Node.js, Python, Docker, multi-environment deployments, security scanning, and auto-changelog. Copy, paste, ship.
Buy on Gumroad →TypeScript Quick Reference Toolkit
5 practical reference files for TypeScript developers: cheatsheet, TS vs JS comparison, 20 utility types with real examples, tsconfig best practices template, and 15 common mistakes guide. Print it. Keep it on your desk.
Buy on Gumroad →You Might Also Need
Playwright Config Generator
Build a playwright.config.ts file visually. Configure browsers, timeouts, retries, reporters, screenshots, video, trace, and web server settings. Copy-ready TypeScript or JavaScript output.
Open free tool →
Playwright E2E Test Generator
Generate Playwright end-to-end test code visually. Add click, fill, expect, and navigate steps, toggle TypeScript/JavaScript, and copy the test code.
Open free tool →
Playwright Test Generator
Generate Playwright end-to-end test code in TypeScript instantly. Configure browser target, test type, and locators to get ready-to-run Playwright tests with page.goto, Locator API, and expect assertions.
Open free tool →
More Free Tools
Related Articles
Playwright vs Selenium 2026: Which Should You Choose?
Complete comparison of Playwright and Selenium for browser automation and end-to-end testing in 2026. Performance benchmarks, API differences, migration guide, and when to use each.
Playwright E2E Testing Guide 2026: Complete Setup & Best Practices
A complete guide to Playwright end-to-end testing in 2026. Covers installation, Page Object Model, visual regression, parallel execution, GitHub Actions CI, and debugging techniques with TypeScript examples.
Playwright vs Cypress vs Puppeteer 2026: Which Testing Tool Should You Choose?
Playwright vs Cypress vs Puppeteer 2026 — compare feature sets, performance, developer experience, parallel testing, CI costs, and browser support. Find the right E2E testing tool for your stack.
DevOps Automation Tools in 2026: Orchestrating the Software Delivery Pipeline
Master DevOps automation with this guide to the tools that orchestrate modern software delivery. Covers CI/CD pipelines, infrastructure provisioning, configuration management, testing automation, and the emerging AI-assisted automation patterns that define 2026.
End-to-End Testing in 2026: Writing Tests That Don't Lie
E2E testing guide 2026: choose Playwright or Cypress, eliminate flakiness, structure test suites for CI/CD, and understand real application quality limits.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$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.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →