Skip to main content

Playwright Test Recorder Guide

Master Playwright's codegen command, write resilient selectors, handle authentication state, and ship reliable end-to-end tests to CI.

✓ codegen recording ✓ Resilient selectors ✓ Auth state reuse ✓ CI/CD integration

Recording Your First Test

Playwright's codegen command opens a browser and records every interaction as runnable test code in real time.

# Install Playwright (first time)
npm init playwright@latest

# Record against your local dev server
npx playwright codegen http://localhost:3000

# Record with Firefox
npx playwright codegen --browser firefox https://example.com

# Record and save authentication state (cookies, localStorage)
npx playwright codegen --save-storage=auth.json https://example.com/login

Selector Priority — Most to Least Resilient

Selector Example Resilience
getByRolegetByRole('button', { name: 'Submit' })Excellent
getByLabelgetByLabel('Email address')Excellent
getByPlaceholdergetByPlaceholder('Search...')Good
getByTextgetByText('Sign in')Good
getByTestIdgetByTestId('submit-btn')Good (needs markup)
CSS selectorlocator('.btn-primary')Fragile
XPathlocator('//button[1]')Very fragile

Complete Test with Auto-Waiting

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

test.describe('Login flow', () => {
  test('successful login redirects to dashboard', async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('secret123');
    await page.getByRole('button', { name: 'Sign in' }).click();

    // Auto-wait for navigation — no sleeps needed
    await page.waitForURL('/dashboard');
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  });

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

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

Authentication State Reuse

// global-setup.ts — logs in once, saves session
import { chromium } from '@playwright/test';
export default async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('/login');
  await page.getByLabel('Email').fill(process.env.TEST_USER!);
  await page.getByLabel('Password').fill(process.env.TEST_PASS!);
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('/dashboard');
  await page.context().storageState({ path: 'playwright/.auth/user.json' });
  await browser.close();
}

// playwright.config.ts
export default defineConfig({
  globalSetup: './global-setup.ts',
  projects: [
    { name: 'authenticated', use: { storageState: 'playwright/.auth/user.json' } },
  ],
});

GitHub Actions CI Setup

# .github/workflows/playwright.yml
name: Playwright E2E Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --workers=4
        env:
          TEST_USER: ${{ secrets.TEST_USER }}
          TEST_PASS: ${{ secrets.TEST_PASS }}
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-html-report
          path: playwright-report/
          retention-days: 14

Common Pitfalls

Frequently Asked Questions

How do I record a Playwright test using the codegen command?▼

Run npx playwright codegen <url> in your terminal. A Chromium browser opens alongside a code inspector window. Every action you take is recorded as Playwright code in real time. Use --browser firefox or --browser webkit to record in different browsers. The --save-storage flag persists authentication cookies between recording sessions.

What is the difference between getByRole, getByText, and getByTestId?▼

Playwright recommended priority: getByRole (ARIA role + accessible name) > getByLabel > getByPlaceholder > getByText > getByTestId. getByRole is the most resilient because it reflects how users and assistive technology experience the page. Avoid CSS selectors and XPath — they break when styling or DOM structure changes.

How do I handle authentication in Playwright tests?▼

Use storageState to save and reuse sessions. Run a global setup file that logs in once and saves the browser state (cookies + localStorage) with page.context().storageState(). In playwright.config.ts, set storageState for authenticated projects. This avoids logging in before every test and speeds up the suite dramatically.

How do I wait for elements without using fixed sleeps?▼

Playwright has auto-waiting built in — most actions automatically wait for the element to be visible, stable, and enabled. Use await expect(locator).toBeVisible() for explicit waits. Use page.waitForResponse() for network calls and page.waitForURL() after navigation. Never use page.waitForTimeout() — it creates brittle tests that fail under slow CI conditions.

How do I run Playwright tests in CI/CD?▼

Install browsers in CI with npx playwright install --with-deps. Run tests headlessly (the default) with npx playwright test. Use --workers=4 to parallelize. The --reporter=html flag generates an HTML report — upload it as a CI artifact. Use shards (--shard=1/4) to split the suite across multiple runners for faster feedback.

What is Playwright page object model and should I use it?▼

The Page Object Model wraps each page into a class with methods for actions and assertions. Instead of repeating selectors, you call loginPage.fillCredentials(user, pass). For large suites POM reduces duplication. For small suites it adds unnecessary boilerplate. Playwright fixtures are the modern alternative — create fixtures that instantiate page objects, making them injectable into any test.

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 →