All Cheatsheets
Playwright Cheatsheet
Cheatsheet

Playwright Cheatsheet

Complete reference guide for Playwright testing framework with selectors, actions, assertions, and best practices

DevBuild StudioUpdated February 2024

Quick Reference: Master Playwright end-to-end testing with this comprehensive cheatsheet. Includes installation, selectors, actions, assertions, and advanced patterns!

Getting Started

Playwright is a powerful end-to-end testing framework that enables reliable testing across Chromium, Firefox, and WebKit with a single API.

Installation

bash
1# Install Playwright with npm
2npm init playwright@latest
3
4# Or add to existing project
5npm install -D @playwright/test
6
7# Install browsers
8npx playwright install
9
10# Install specific browser
11npx playwright install chromium

Quick Start

typescript
1// tests/example.spec.ts
2import { test, expect } from '@playwright/test';
3
4test('basic test', async ({ page }) => {
5  await page.goto('https://playwright.dev/');
6  await expect(page).toHaveTitle(/Playwright/);
7});

Prerequisites

  • Node.js 18+ (required)
  • Modern browser (Chrome, Firefox, or Safari)
  • Basic TypeScript/JavaScript knowledge

Essential Commands

Running Tests

CommandDescriptionExample
npx playwright testRun all testsnpx playwright test
npx playwright test --headedRun with browser UInpx playwright test --headed
npx playwright test --debugDebug modenpx playwright test --debug
npx playwright test --uiRun in UI modenpx playwright test --ui
npx playwright test file.spec.tsRun specific filenpx playwright test login.spec.ts
npx playwright test --grep @smokeRun tests with tagnpx playwright test --grep @smoke

Code Generation

bash
1# Record new test
2npx playwright codegen https://example.com
3
4# Record with specific browser
5npx playwright codegen --browser=firefox https://example.com
6
7# Record with device emulation
8npx playwright codegen --device="iPhone 13" https://example.com

Selectors

Basic Selectors

Selector TypeSyntaxExample
Texttext=page.locator('text=Sign in')
CSSCSS selectorpage.locator('.submit-button')
ID#idpage.locator('#username')
Class.classpage.locator('.form-input')
Attribute[attr=value]page.locator('[data-testid="submit"]')
XPathxpath=page.locator('xpath=//button')
typescript
1// 1. data-testid (Best)
2await page.locator('[data-testid="login-button"]').click();
3
4// 2. Role-based (Accessible)
5await page.getByRole('button', { name: 'Sign in' }).click();
6
7// 3. Label text
8await page.getByLabel('Email address').fill('user@example.com');
9
10// 4. Placeholder
11await page.getByPlaceholder('Enter your email').fill('test@test.com');
12
13// 5. Text content
14await page.getByText('Welcome back').click();

Advanced Selectors

typescript
1// Has text
2await page.locator('button:has-text("Submit")').click();
3
4// Nth element
5await page.locator('li >> nth=2').click();
6
7// Within parent
8await page.locator('.parent >> .child').click();
9
10// Filter by text
11await page.locator('button').filter({ hasText: 'Submit' }).click();
12
13// First/Last
14await page.locator('li').first().click();
15await page.locator('li').last().click();

Actions

typescript
1// Navigate to URL
2await page.goto('https://example.com');
3
4// Navigate with options
5await page.goto('https://example.com', {
6  waitUntil: 'networkidle',
7  timeout: 30000
8});
9
10// Go back/forward
11await page.goBack();
12await page.goForward();
13
14// Reload
15await page.reload();

Clicks and Interactions

typescript
1// Basic click
2await page.locator('button').click();
3
4// Double click
5await page.locator('button').dblclick();
6
7// Right click
8await page.locator('button').click({ button: 'right' });
9
10// Click with modifiers
11await page.locator('a').click({ modifiers: ['Control'] });
12
13// Hover
14await page.locator('.menu-item').hover();
15
16// Focus
17await page.locator('input').focus();

Form Input

typescript
1// Type text
2await page.locator('#username').fill('john@example.com');
3
4// Type slowly (for autocomplete)
5await page.locator('input').pressSequentially('playwright', { delay: 100 });
6
7// Clear input
8await page.locator('input').clear();
9
10// Press key
11await page.locator('input').press('Enter');
12
13// Keyboard shortcuts
14await page.keyboard.press('Control+A');
15await page.keyboard.press('Meta+C'); // Cmd on Mac
16
17// Check/uncheck
18await page.locator('#agree').check();
19await page.locator('#agree').uncheck();
20
21// Select option
22await page.locator('select').selectOption('value');
23await page.locator('select').selectOption({ label: 'Blue' });

File Upload

typescript
1// Upload single file
2await page.locator('input[type="file"]').setInputFiles('path/to/file.pdf');
3
4// Upload multiple files
5await page.locator('input[type="file"]').setInputFiles([
6  'file1.pdf',
7  'file2.pdf'
8]);
9
10// Remove files
11await page.locator('input[type="file"]').setInputFiles([]);

Assertions

Page Assertions

typescript
1// Title
2await expect(page).toHaveTitle('My Page');
3await expect(page).toHaveTitle(/Page/);
4
5// URL
6await expect(page).toHaveURL('https://example.com/dashboard');
7await expect(page).toHaveURL(/dashboard/);
8
9// Screenshot comparison
10await expect(page).toHaveScreenshot();

Element Assertions

typescript
1// Visibility
2await expect(page.locator('.message')).toBeVisible();
3await expect(page.locator('.loader')).toBeHidden();
4
5// Text content
6await expect(page.locator('h1')).toHaveText('Welcome');
7await expect(page.locator('h1')).toContainText('Welcome');
8
9// Count
10await expect(page.locator('li')).toHaveCount(3);
11
12// Attribute
13await expect(page.locator('a')).toHaveAttribute('href', '/home');
14
15// Class
16await expect(page.locator('button')).toHaveClass(/active/);
17
18// Value (for inputs)
19await expect(page.locator('input')).toHaveValue('test@example.com');
20
21// Enabled/Disabled
22await expect(page.locator('button')).toBeEnabled();
23await expect(page.locator('button')).toBeDisabled();
24
25// Checked
26await expect(page.locator('#checkbox')).toBeChecked();
27await expect(page.locator('#checkbox')).not.toBeChecked();

Soft Assertions

typescript
1// Continue test even if assertion fails
2await expect.soft(page.locator('h1')).toHaveText('Expected Title');
3await expect.soft(page.locator('p')).toBeVisible();
4
5// Test continues and reports all failures at the end

Waiting Strategies

Auto-Waiting

typescript
1// Playwright auto-waits for elements to be:
2// - Attached to DOM
3// - Visible
4// - Stable (not animating)
5// - Enabled
6// - Not covered by other elements
7
8await page.locator('button').click(); // Auto-waits before clicking

Explicit Waits

typescript
1// Wait for selector
2await page.waitForSelector('.content');
3
4// Wait for URL
5await page.waitForURL('**/dashboard');
6
7// Wait for load state
8await page.waitForLoadState('networkidle');
9await page.waitForLoadState('domcontentloaded');
10
11// Wait for timeout
12await page.waitForTimeout(1000); // Avoid if possible!
13
14// Wait for function
15await page.waitForFunction(() => {
16  return document.querySelector('.loaded') !== null;
17});
18
19// Wait for response
20await page.waitForResponse('**/api/users');
21await page.waitForResponse(resp =>
22  resp.url().includes('/api/') && resp.status() === 200
23);

Screenshots and Videos

Screenshots

typescript
1// Full page screenshot
2await page.screenshot({ path: 'screenshot.png' });
3
4// Full page (including scrollable area)
5await page.screenshot({ path: 'screenshot.png', fullPage: true });
6
7// Element screenshot
8await page.locator('.card').screenshot({ path: 'card.png' });
9
10// Screenshot as buffer
11const buffer = await page.screenshot();

Videos

typescript
1// Configure in playwright.config.ts
2export default {
3  use: {
4    video: 'on', // 'on', 'off', 'retain-on-failure', 'on-first-retry'
5    videoSize: { width: 1280, height: 720 }
6  }
7};
8
9// Access video in test
10test('with video', async ({ page }) => {
11  // Test code...
12  const path = await page.video()?.path();
13  console.log('Video saved at:', path);
14});

Configuration

playwright.config.ts

typescript
1import { defineConfig, devices } from '@playwright/test';
2
3export default defineConfig({
4  // Test directory
5  testDir: './tests',
6
7  // Global timeout
8  timeout: 30000,
9
10  // Expect timeout
11  expect: {
12    timeout: 5000
13  },
14
15  // Retries
16  retries: process.env.CI ? 2 : 0,
17
18  // Workers (parallel execution)
19  workers: process.env.CI ? 1 : undefined,
20
21  // Reporter
22  reporter: [
23    ['html'],
24    ['json', { outputFile: 'test-results.json' }],
25    ['junit', { outputFile: 'junit-results.xml' }]
26  ],
27
28  // Shared settings
29  use: {
30    baseURL: 'http://localhost:3000',
31    trace: 'on-first-retry',
32    screenshot: 'only-on-failure',
33    video: 'retain-on-failure',
34
35    // Browser context options
36    viewport: { width: 1280, height: 720 },
37    ignoreHTTPSErrors: true,
38
39    // Locale & timezone
40    locale: 'en-US',
41    timezoneId: 'America/New_York',
42  },
43
44  // Projects (browsers)
45  projects: [
46    {
47      name: 'chromium',
48      use: { ...devices['Desktop Chrome'] },
49    },
50    {
51      name: 'firefox',
52      use: { ...devices['Desktop Firefox'] },
53    },
54    {
55      name: 'webkit',
56      use: { ...devices['Desktop Safari'] },
57    },
58    {
59      name: 'Mobile Chrome',
60      use: { ...devices['Pixel 5'] },
61    },
62    {
63      name: 'Mobile Safari',
64      use: { ...devices['iPhone 13'] },
65    },
66  ],
67
68  // Web server
69  webServer: {
70    command: 'npm run start',
71    url: 'http://localhost:3000',
72    reuseExistingServer: !process.env.CI,
73    timeout: 120000,
74  },
75});

Fixtures and Hooks

Test Hooks

typescript
1import { test } from '@playwright/test';
2
3// Before each test
4test.beforeEach(async ({ page }) => {
5  await page.goto('https://example.com');
6  await page.locator('#login').click();
7});
8
9// After each test
10test.afterEach(async ({ page }) => {
11  await page.close();
12});
13
14// Before all tests in file
15test.beforeAll(async () => {
16  // Setup database, etc.
17});
18
19// After all tests in file
20test.afterAll(async () => {
21  // Cleanup
22});

Custom Fixtures

typescript
1// fixtures.ts
2import { test as base } from '@playwright/test';
3
4type MyFixtures = {
5  authenticatedPage: Page;
6};
7
8export const test = base.extend<MyFixtures>({
9  authenticatedPage: async ({ page }, use) => {
10    // Setup
11    await page.goto('/login');
12    await page.fill('#username', 'user');
13    await page.fill('#password', 'pass');
14    await page.click('#submit');
15    await page.waitForURL('**/dashboard');
16
17    // Use fixture
18    await use(page);
19
20    // Teardown
21    await page.close();
22  },
23});
24
25// Use in test
26test('use authenticated page', async ({ authenticatedPage }) => {
27  await authenticatedPage.goto('/profile');
28  // Already logged in!
29});

Common Patterns

Page Object Model

typescript
1// 📁 pages/LoginPage.ts
2import { Page, Locator } from '@playwright/test';
3
4export class LoginPage {
5  readonly page: Page;
6  readonly usernameInput: Locator;
7  readonly passwordInput: Locator;
8  readonly submitButton: Locator;
9
10  constructor(page: Page) {
11    this.page = page;
12    this.usernameInput = page.locator('#username');
13    this.passwordInput = page.locator('#password');
14    this.submitButton = page.locator('button[type="submit"]');
15  }
16
17  async goto() {
18    await this.page.goto('/login');
19  }
20
21  async login(username: string, password: string) {
22    await this.usernameInput.fill(username);
23    await this.passwordInput.fill(password);
24    await this.submitButton.click();
25    await this.page.waitForURL('**/dashboard');
26  }
27}
28
29// 📁 tests/login.spec.ts
30import { test, expect } from '@playwright/test';
31import { LoginPage } from './pages/LoginPage';
32
33test('successful login', async ({ page }) => {
34  const loginPage = new LoginPage(page);
35  await loginPage.goto();
36  await loginPage.login('user@example.com', 'password123');
37
38  await expect(page).toHaveURL(/dashboard/);
39});

API Testing

typescript
1import { test, expect } from '@playwright/test';
2
3test('API testing', async ({ request }) => {
4  // GET request
5  const response = await request.get('https://api.example.com/users');
6  expect(response.ok()).toBeTruthy();
7  const users = await response.json();
8  expect(users).toHaveLength(5);
9
10  // POST request
11  const newUser = await request.post('https://api.example.com/users', {
12    data: {
13      name: 'John Doe',
14      email: 'john@example.com'
15    }
16  });
17  expect(newUser.ok()).toBeTruthy();
18
19  // With headers
20  const authResponse = await request.get('https://api.example.com/profile', {
21    headers: {
22      'Authorization': 'Bearer token123'
23    }
24  });
25});

Handling Dialogs

typescript
1// Alert, confirm, prompt
2test('handle dialogs', async ({ page }) => {
3  // Accept dialog
4  page.on('dialog', dialog => dialog.accept());
5  await page.locator('button').click();
6
7  // Dismiss dialog
8  page.on('dialog', dialog => dialog.dismiss());
9
10  // Get dialog message
11  page.on('dialog', async dialog => {
12    console.log(dialog.message());
13    await dialog.accept();
14  });
15
16  // Prompt with input
17  page.on('dialog', dialog => dialog.accept('My input'));
18});

Multiple Tabs/Windows

typescript
1test('handle new tab', async ({ context, page }) => {
2  // Wait for new page
3  const [newPage] = await Promise.all([
4    context.waitForEvent('page'),
5    page.locator('a[target="_blank"]').click()
6  ]);
7
8  await newPage.waitForLoadState();
9  console.log(await newPage.title());
10  await newPage.close();
11});

Network Interception

typescript
1test('mock API response', async ({ page }) => {
2  // Mock API endpoint
3  await page.route('**/api/users', route => {
4    route.fulfill({
5      status: 200,
6      contentType: 'application/json',
7      body: JSON.stringify([
8        { id: 1, name: 'John' },
9        { id: 2, name: 'Jane' }
10      ])
11    });
12  });
13
14  await page.goto('/users');
15});
16
17test('block images', async ({ page }) => {
18  // Block image requests
19  await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
20  await page.goto('/');
21});

Best Practices

Pro Tip: Use data-testid attributes for reliable selectors that won't break when UI changes

Selector Best Practices

  1. Prefer user-facing attributes

    typescript
    1// ✅ Good
    2await page.getByRole('button', { name: 'Submit' });
    3await page.getByLabel('Email');
    4await page.getByText('Welcome');
    5
    6// ❌ Avoid
    7await page.locator('.btn-primary-submit-form');
    8await page.locator('div > div > button:nth-child(2)');
  2. Use data-testid for dynamic content

    typescript
    1// ✅ Good
    2await page.locator('[data-testid="user-profile"]');
    3
    4// ❌ Avoid
    5await page.locator('#user-123456');
  3. Keep selectors simple

    typescript
    1// ✅ Good
    2await page.locator('[data-testid="submit"]');
    3
    4// ❌ Avoid
    5await page.locator('div.container > form > div.row > button.submit');

Test Organization

typescript
1// Group related tests
2test.describe('Login functionality', () => {
3  test('successful login', async ({ page }) => {
4    // Test code
5  });
6
7  test('failed login', async ({ page }) => {
8    // Test code
9  });
10
11  test('password reset', async ({ page }) => {
12    // Test code
13  });
14});
15
16// Add tags
17test('smoke test @smoke', async ({ page }) => {
18  // Critical path test
19});
20
21test('integration test @integration', async ({ page }) => {
22  // Full flow test
23});

Performance Tips

  1. Reuse authentication state

    typescript
    1// Save auth state once
    2test('save auth state', async ({ page }) => {
    3  await page.goto('/login');
    4  await page.fill('#username', 'user');
    5  await page.fill('#password', 'pass');
    6  await page.click('#submit');
    7
    8  await page.context().storageState({ path: 'auth.json' });
    9});
    10
    11// Reuse in other tests
    12test.use({ storageState: 'auth.json' });
  2. Run tests in parallel

    typescript
    1// playwright.config.ts
    2export default {
    3  workers: 4, // Run 4 tests in parallel
    4};
  3. Use test.describe.parallel

    typescript
    test.describe.parallel('parallel tests', () => {
      // All tests in this block run in parallel
    });

Debugging

Debug Mode

bash
1# Run in debug mode
2npx playwright test --debug
3
4# Debug specific test
5npx playwright test login.spec.ts --debug
6
7# Debug from specific line
8PWDEBUG=console npx playwright test

Playwright Inspector

typescript
1// Add breakpoint in code
2await page.pause();
3
4// Inspect element
5await page.locator('button').click();

Trace Viewer

bash
1# Enable trace in config
2use: {
3  trace: 'on'
4}
5
6# View trace
7npx playwright show-trace trace.zip

VS Code Extension

  • Install "Playwright Test for VSCode"
  • Run/Debug tests from editor
  • Set breakpoints
  • View test results inline

Troubleshooting

Common Issues

Issue: Element not visible

typescript
// Solution: Wait for element or check visibility
await page.locator('button').waitFor({ state: 'visible' });
await expect(page.locator('button')).toBeVisible();

Issue: Timeout waiting for element

typescript
1// Solution: Increase timeout or fix selector
2await page.locator('button').click({ timeout: 10000 });
3
4// Or update globally
5test.setTimeout(60000);

Issue: Flaky tests

typescript
1// Solution: Use proper waits instead of timeouts
2// ❌ Bad
3await page.waitForTimeout(1000);
4
5// ✅ Good
6await page.waitForLoadState('networkidle');
7await expect(page.locator('.content')).toBeVisible();

Issue: Tests fail in CI

typescript
1// Solution: Disable parallelization or increase retries
2export default {
3  workers: process.env.CI ? 1 : 4,
4  retries: process.env.CI ? 2 : 0,
5};

Quick Command Reference

bash
1# Running Tests
2npx playwright test                    # Run all tests
3npx playwright test --headed          # Show browser
4npx playwright test --debug           # Debug mode
5npx playwright test --ui              # UI mode
6npx playwright test file.spec.ts     # Run specific file
7npx playwright test --grep @smoke    # Run tagged tests
8npx playwright test --project=webkit # Run specific browser
9
10# Code Generation
11npx playwright codegen [url]         # Record tests
12npx playwright codegen --target=typescript # Generate TypeScript
13
14# Reports
15npx playwright show-report           # View HTML report
16npx playwright show-trace trace.zip  # View trace
17
18# Installation
19npm init playwright@latest           # Create new project
20npx playwright install               # Install browsers
21npx playwright install chromium      # Install specific browser
22
23# Configuration
24npx playwright test --config=custom.config.ts  # Custom config

Additional Resources


Bookmark This Page! Keep this Playwright cheatsheet handy for quick reference while writing your tests.

Last updated: February 2024 | Playwright v1.41

Was this cheatsheet helpful? Let us know!