Playwright E2e Complete
Comprehensive skill designed for plan, build, comprehensive, playwright. Includes structured workflows, validation checks, and reusable patterns for development.
Playwright E2E Test Builder Skill
A Claude Code skill for building comprehensive end-to-end test suites with Playwright — covering project setup, test architecture, Page Object Model, visual testing, API testing, CI integration, and cross-browser coverage.
When to Use This Skill
Choose this skill when:
- Setting up Playwright from scratch in an existing project
- Building E2E tests for critical user flows (auth, checkout, forms)
- Creating a test architecture with Page Object Model
- Adding visual regression testing to your pipeline
- Configuring parallel test execution in CI/CD
- Testing across Chromium, Firefox, and WebKit simultaneously
Consider alternatives when:
- You need unit tests (use Vitest or Jest)
- You need API-only testing (use Supertest or Hurl)
- You need mobile native app testing (use Detox or Appium)
Quick Start
# Initialize Playwright in your project npm init playwright@latest # Installs browsers, creates config, adds example tests # Run all tests npx playwright test # Run with interactive UI npx playwright test --ui # Record a test npx playwright codegen http://localhost:3000
// tests/e2e/checkout.spec.ts import { test, expect } from '@playwright/test'; test.describe('Checkout Flow', () => { test.beforeEach(async ({ page }) => { await page.goto('/products'); }); test('user can complete purchase', async ({ page }) => { await page.getByRole('button', { name: 'Add to Cart' }).first().click(); await page.getByRole('link', { name: 'Cart' }).click(); await expect(page.getByText('1 item')).toBeVisible(); await page.getByRole('button', { name: 'Checkout' }).click(); await page.getByLabel('Card number').fill('4242424242424242'); await page.getByLabel('Expiry').fill('12/28'); await page.getByLabel('CVC').fill('123'); await page.getByRole('button', { name: 'Pay' }).click(); await expect(page.getByText('Order confirmed')).toBeVisible(); }); });
Core Concepts
Test Architecture
| Layer | Purpose | Example |
|---|---|---|
| Spec Files | Test scenarios by feature | auth.spec.ts, checkout.spec.ts |
| Page Objects | Page interaction abstraction | LoginPage, DashboardPage |
| Fixtures | Shared test setup/teardown | Auth state, test data |
| Helpers | Reusable test utilities | createTestUser(), seedDatabase() |
| Config | Browser and environment setup | playwright.config.ts |
Page Object Model
// pages/LoginPage.ts import { Page, Locator } from '@playwright/test'; export class LoginPage { readonly emailInput: Locator; readonly passwordInput: Locator; readonly submitButton: Locator; constructor(private page: Page) { this.emailInput = page.getByLabel('Email'); this.passwordInput = page.getByLabel('Password'); this.submitButton = page.getByRole('button', { name: 'Sign in' }); } async goto() { await this.page.goto('/login'); } async login(email: string, password: string) { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } } // Usage in tests test('login with valid credentials', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.goto(); await loginPage.login('[email protected]', 'password'); await expect(page).toHaveURL('/dashboard'); });
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
testDir | string | "./tests" | Test file directory |
timeout | number | 30000 | Per-test timeout (ms) |
retries | number | 0 | Retry count (use 2 in CI) |
workers | string | "50%" | Parallel workers |
use.baseURL | string | — | Application base URL |
use.trace | string | "on-first-retry" | Trace recording mode |
use.screenshot | string | "only-on-failure" | Screenshot capture mode |
reporter | array | [["html"]] | Test reporter(s) |
Best Practices
-
Use the Page Object Model for all but trivial tests — POM encapsulates page interactions behind meaningful methods, making tests readable and resilient to UI changes.
-
Prefer role-based locators —
getByRole('button', { name: 'Submit' })is the most resilient locator strategy; it survives CSS refactors and validates accessibility. -
Set up authentication fixtures — authenticate once in a setup project and save
storageState; all subsequent tests reuse the session, eliminating login overhead per test. -
Enable traces on first retry — traces capture screenshots, DOM snapshots, network requests, and console logs; they're invaluable for debugging failures, especially in CI.
-
Run tests in parallel with isolated browser contexts — Playwright creates a fresh context per test by default; leverage this for safe parallelization to reduce total test run time.
Common Issues
Tests fail intermittently due to timing — Replace page.waitForTimeout() with proper assertions like expect(locator).toBeVisible() which auto-retries. Flaky waits are the #1 cause of unreliable E2E tests.
Selectors break after UI redesign — CSS class-based selectors are fragile. Switch to getByRole, getByLabel, or getByTestId which are resilient to visual changes.
Tests pass locally, fail in CI — CI runs on slower hardware. Increase timeouts, add retries: 2, and ensure the dev server is fully started before tests begin. Use webServer config to auto-start the server.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.