Skip to content

Playwright cheat sheet 2026

Updated 2026-09-28
134
copyable snippets
26
sections
TS + Py
languages

Everything you type daily in Playwright on one searchable page: install, write a test, find elements with getByRole and friends, act, assert with auto-retrying expect(), wait for URLs and responses, handle frames, dialogs, popups, uploads and downloads, manage cookies and storage, test APIs, emulate devices, record traces, configure projects and annotate tests. Switch between TypeScript and Python and copy any snippet in one click.

A quick reference for Playwright 1.63 (September 2026), organised the way you work — from installation to test annotations, plus the CLI and network mocking. Type to filter, pick a section, or switch the whole sheet to Python.

Key takeaways

  • Playwright auto-waits: actions and expect() assertions retry on their own, so tests need no sleeps.
  • Locate by role, label and text first; keep CSS and XPath for elements with nothing user-facing.
  • Start waiting for popups, downloads and responses before the click that triggers them.
  • Record a trace on the first retry and open it with npx playwright show-trace to debug flaky tests.
  • Keep tests independent, and move to page objects once a suite covers more than a handful of screens.

The cheat sheet

Type to filter across all sections, pick a section, or switch to Python. Every snippet has a copy button.

134 entries

1. Installation

  • New project (TypeScript or JavaScript)
    npm init playwright@latest
  • Add to an existing project
    npm install -D @playwright/test
    npx playwright install
  • Install one browser
    npx playwright install chromium
  • Browsers + OS libraries (CI images)
    npx playwright install --with-deps
  • Check the version
    npx playwright --version

2. Basic Test Structure

  • A test file
    import { test, expect } from '@playwright/test';
    
    test('has title', async ({ page }) => {
      await page.goto('https://example.com');
      await expect(page).toHaveTitle('Example Domain');
    });
  • Group tests + run code before each
    test.describe('checkout', () => {
      test.beforeEach(async ({ page }) => {
        await page.goto('/cart');
      });
    
      test('pays by card', async ({ page }) => {
        await page.getByRole('button', { name: 'Pay' }).click();
      });
    });
  • Named steps in the report
    await test.step('add item to cart', async () => {
      await page.getByRole('button', { name: 'Add' }).click();
    });
  • Hooks at a glance
    test.beforeAll(async () => { /* once per worker */ });
    test.beforeEach(async ({ page }) => { /* before every test */ });
    test.afterEach(async ({ page }) => { /* after every test */ });
    test.afterAll(async () => { /* once per worker */ });

    beforeAll / afterAll run once per worker process.

3. Scripts Without the Test Runner

  • Launch → page → close
    import { chromium } from 'playwright';
    
    (async () => {
      const browser = await chromium.launch();
      const page = await browser.newPage();
      await page.goto('https://example.com');
      console.log(await page.title());
      await browser.close();
    })();

    The `playwright` package on its own — for crawlers, scrapers and one-off scripts.

4. Browser & Context

  • Launch a browser (visible, slowed down)
    const browser = await chromium.launch({ headless: false, slowMo: 250 });
  • New isolated context (like incognito)
    const context = await browser.newContext({ viewport: { width: 1280, height: 720 }, locale: 'en-GB' });
  • New page (tab) in the context
    const page = await context.newPage();
  • Two users side by side
    const admin = await browser.newContext({ storageState: 'auth/admin.json' });
    const user = await browser.newContext({ storageState: 'auth/user.json' });
  • Close
    await context.close();
    await browser.close();

5. Page Navigation

  • Open a URL
    await page.goto('https://example.com');
    await page.goto('/login');   // relative to baseURL
  • With options
    await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
  • Back / forward / reload
    await page.goBack();
    await page.goForward();
    await page.reload();

6. Locators

  • By role (first choice)
    page.getByRole('button', { name: 'Submit' })
  • By text (substring, case-insensitive)
    page.getByText('Welcome')
    page.getByText('Welcome back', { exact: true })
  • By label
    page.getByLabel('Email')
  • By placeholder
    page.getByPlaceholder('Search')
  • By test id (data-testid)
    page.getByTestId('login-btn')
  • By alt text / title
    page.getByAltText('Logo')
    page.getByTitle('Close')
  • CSS / XPath
    page.locator('.btn-primary')
    page.locator('#login')
    page.locator('input[name="email"]')
    page.locator('//button[@type="submit"]')

    For elements with nothing user-facing to target.

  • Filter by text or by a child
    page.getByRole('listitem').filter({ hasText: 'Product 2' })
    page.getByRole('row').filter({ has: page.getByRole('cell', { name: 'Ada' }) })
  • Chain inside a container
    page.getByRole('dialog').getByRole('button', { name: 'OK' })
  • Pick one of many
    page.getByRole('listitem').first()
    page.getByRole('listitem').nth(2)
    page.getByRole('listitem').last()
  • Only visible matches
    page.locator('.toast').visible()

    Playwright 1.63+. Same result: .filter({ visible: true }).

7. Actions

  • Click / double / right click
    await locator.click();
    await locator.dblclick();
    await locator.click({ button: 'right' });
  • Type into a field (replaces the value)
    await page.getByLabel('Username').fill('testuser');
  • Clear a field
    await page.getByLabel('Search').clear();
  • Type key by key (autocomplete, masks)
    await locator.pressSequentially('Kyiv', { delay: 50 });
  • Press a key / shortcut
    await page.getByLabel('Search').press('Enter');
    await page.keyboard.press('Control+A');
  • Checkbox / radio
    await page.getByLabel('I agree').check();
    await page.getByLabel('I agree').uncheck();
    await page.getByLabel('Express delivery').setChecked(true);
  • Select from a <select>
    await page.getByLabel('Country').selectOption('UA');
    await page.getByLabel('Tags').selectOption(['qa', 'e2e']);
  • Hover
    await page.getByRole('menuitem', { name: 'Settings' }).hover();
  • Drag and drop
    await page.locator('#card').dragTo(page.locator('#done'));
  • Scroll into view / focus
    await locator.scrollIntoViewIfNeeded();
    await locator.focus();

8. Assertions

  • Visible / hidden
    await expect(locator).toBeVisible();
    await expect(locator).toBeHidden();
  • Exact / partial text
    await expect(locator).toHaveText('Welcome, Ada');
    await expect(locator).toContainText('Welcome');
  • Input value
    await expect(page.getByLabel('Email')).toHaveValue('ada@example.com');
  • Attribute / class
    await expect(link).toHaveAttribute('href', '/home');
    await expect(tab).toHaveClass(/active/);
  • Enabled / disabled / checked
    await expect(button).toBeEnabled();
    await expect(button).toBeDisabled();
    await expect(checkbox).toBeChecked();
  • Count of matches
    await expect(page.getByRole('listitem')).toHaveCount(3);
  • Page URL and title
    await expect(page).toHaveURL(/\/dashboard/);
    await expect(page).toHaveTitle('Dashboard');
  • Accessibility tree snapshot
    await expect(page.getByRole('navigation')).toMatchAriaSnapshot(`
      - link "Home"
      - link "Pricing"
    `);
  • Visual comparison
    await expect(page).toHaveScreenshot('home.png');
  • Soft assertion (test keeps going)
    await expect.soft(locator).toHaveText('Saved');
  • Negate / longer timeout
    await expect(spinner).not.toBeVisible({ timeout: 10_000 });

9. Waits

  • Wait for an element
    await expect(page.getByText('Saved')).toBeVisible();

    Actions and expect() wait automatically — no sleeps needed.

  • Wait until it disappears
    await page.getByTestId('spinner').waitFor({ state: 'hidden' });
  • Wait for a URL after a click
    await page.getByRole('button', { name: 'Next' }).click();
    await page.waitForURL('**/step-2');
  • Wait for a network response
    const responsePromise = page.waitForResponse('**/api/data');
    await page.getByRole('button', { name: 'Load' }).click();
    const response = await responsePromise;
    expect(response.status()).toBe(200);

    Start waiting before the click — no await on the first line.

  • Wait for a load state
    await page.waitForLoadState('domcontentloaded');
  • Retry a block until it passes
    await expect(async () => {
      const res = await page.request.get('/api/status');
      expect(res.status()).toBe(200);
    }).toPass({ timeout: 15_000 });
  • Default timeouts
    page.setDefaultTimeout(10_000);            // actions
    page.setDefaultNavigationTimeout(30_000); // goto, waitForURL

10. Frames

  • Work inside an iframe
    const frame = page.frameLocator('#payment');
    await frame.getByLabel('Card number').fill('4242 4242 4242 4242');
  • Iframe from an existing locator
    await page.locator('iframe[name="editor"]').contentFrame().getByRole('textbox').fill('Hi');
  • Search every frame at once
    await page.frameLocator().getByRole('button', { name: 'Pay' }).click();

    Playwright 1.63+: without a selector it searches all frames in the page.

11. Dialogs

  • Accept an alert / confirm
    page.once('dialog', (dialog) => dialog.accept());
    await page.getByRole('button', { name: 'Delete' }).click();

    Register the handler before the click. Without a handler, dialogs are dismissed automatically.

  • Dismiss a confirm
    page.once('dialog', (dialog) => dialog.dismiss());
  • Answer a prompt
    page.once('dialog', (dialog) => dialog.accept('Ada'));
  • Check the message
    page.once('dialog', async (dialog) => {
      expect(dialog.message()).toContain('Are you sure?');
      await dialog.accept();
    });

12. Handling Popups

  • Link that opens a new tab
    const popupPromise = page.waitForEvent('popup');
    await page.getByRole('link', { name: 'Open docs' }).click();
    const popup = await popupPromise;
    await expect(popup).toHaveTitle(/Docs/);

    Start waiting before the click.

  • Any new tab in the context
    const pagePromise = context.waitForEvent('page');
    await page.getByRole('button', { name: 'Open report' }).click();
    const newPage = await pagePromise;
  • All open tabs / switch
    const pages = context.pages();
    await pages[1].bringToFront();

13. File Uploads

  • Upload one file
    await page.getByLabel('Upload CV').setInputFiles('fixtures/cv.pdf');
  • Upload several / clear
    await page.getByLabel('Photos').setInputFiles(['a.png', 'b.png']);
    await page.getByLabel('Photos').setInputFiles([]);
  • File from memory
    await page.getByLabel('Upload').setInputFiles({
      name: 'data.csv',
      mimeType: 'text/csv',
      buffer: Buffer.from('id,name\n1,Ada'),
    });
  • Upload button without an <input>
    const chooserPromise = page.waitForEvent('filechooser');
    await page.getByRole('button', { name: 'Upload' }).click();
    const chooser = await chooserPromise;
    await chooser.setFiles('fixtures/cv.pdf');

14. Downloads

  • Wait for and save a download
    const downloadPromise = page.waitForEvent('download');
    await page.getByRole('button', { name: 'Export' }).click();
    const download = await downloadPromise;
    await download.saveAs('downloads/' + download.suggestedFilename());
  • Temporary path of the file
    const filePath = await download.path();

15. Cookies

  • Get cookies
    const cookies = await context.cookies();
  • Set a cookie
    await context.addCookies([
      { name: 'token', value: 'abc123', domain: 'example.com', path: '/' },
    ]);
  • Clear all / one cookie
    await context.clearCookies();
    await context.clearCookies({ name: 'token' });

16. Local Storage / Session Storage

  • Set / read localStorage
    await page.localStorage.setItem('theme', 'dark');
    const theme = await page.localStorage.getItem('theme');

    Playwright 1.61+, for the page's current origin.

  • Set / read sessionStorage
    await page.sessionStorage.setItem('step', '2');
    const step = await page.sessionStorage.getItem('step');
  • Clear localStorage
    await page.localStorage.clear();
  • Save the logged-in state
    await context.storageState({ path: 'playwright/.auth/user.json' });

    Saves cookies and localStorage (IndexedDB with indexedDB: true). sessionStorage is not included.

  • Start a context logged in
    const context = await browser.newContext({ storageState: 'playwright/.auth/user.json' });

17. API Testing (Request)

  • GET and assert
    const response = await request.get('/api/users');
    await expect(response).toBeOK();
    const json = await response.json();
    expect(json.data).toHaveLength(1);
  • POST JSON
    const created = await request.post('/api/users', { data: { name: 'Ada', role: 'qa' } });
    expect(created.status()).toBe(201);
  • Client with a token
    const api = await playwright.request.newContext({
      baseURL: 'https://api.example.com',
      extraHTTPHeaders: { Authorization: `Bearer ${process.env.API_TOKEN}` },
    });
  • Same cookies as the page
    const res = await page.request.get('/api/profile');

    page.request shares cookies with the browser context.

18. Page Information

  • Title, URL, HTML
    const title = await page.title();
    const url = page.url();
    const html = await page.content();
  • Viewport size
    const size = page.viewportSize();
  • Browser console messages
    page.on('console', (msg) => console.log(msg.type(), msg.text()));

19. Viewport & Emulation

  • Set the viewport
    await page.setViewportSize({ width: 1280, height: 720 });
  • Emulate a phone (config)
    // playwright.config.ts → projects
    { name: 'mobile', use: { ...devices['iPhone 15'] } }
  • Dark mode / reduced motion
    await page.emulateMedia({ colorScheme: 'dark', reducedMotion: 'reduce' });
  • Locale, time zone, user agent
    test.use({ locale: 'uk-UA', timezoneId: 'Europe/Kyiv', userAgent: 'MyTestBot/1.0' });

    At file or describe level, or in the config's use block.

  • Geolocation
    await context.grantPermissions(['geolocation']);
    await context.setGeolocation({ latitude: 50.45, longitude: 30.52 });
  • Freeze the clock
    await page.clock.setFixedTime(new Date('2026-12-31T23:59:00'));

20. Screenshots & Video

  • Screenshot (viewport / full page)
    await page.screenshot({ path: 'page.png' });
    await page.screenshot({ path: 'full.png', fullPage: true });
  • Screenshot one element
    await page.getByRole('dialog').screenshot({ path: 'dialog.png' });
  • Hide changing parts
    await page.screenshot({ path: 'stable.png', mask: [page.getByTestId('clock')] });
  • Keep artifacts of failed tests (config)
    use: { video: 'retain-on-failure', screenshot: 'only-on-failure' }
  • Record video in a script
    const context = await browser.newContext({ recordVideo: { dir: 'videos/' } });

21. Tracing

  • Trace on the first retry (config)
    use: { trace: 'on-first-retry' }
  • Force a trace for one run
    npx playwright test --trace on
  • Record a trace in a script
    await context.tracing.start({ screenshots: true, snapshots: true, sources: true });
    // … steps …
    await context.tracing.stop({ path: 'trace.zip' });
  • Open a trace
    npx playwright show-trace trace.zip

22. Test Configuration

  • playwright.config.ts
    import { defineConfig, devices } from '@playwright/test';
    
    export default defineConfig({
      testDir: './tests',
      timeout: 30_000,
      retries: process.env.CI ? 2 : 0,
      workers: process.env.CI ? 4 : undefined,
      reporter: 'html',
      use: {
        baseURL: 'https://example.com',
        headless: true,
        viewport: { width: 1280, height: 720 },
        trace: 'on-first-retry',
      },
      projects: [
        { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
        { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
        { name: 'webkit', use: { ...devices['Desktop Safari'] } },
      ],
    });
  • Start the app before tests
    webServer: {
      command: 'npm run start',
      url: 'http://localhost:3000',
      reuseExistingServer: !process.env.CI,
    }
  • Assertion timeout
    expect: { timeout: 5_000 }

23. Test Annotations

  • Skip (always or on a condition)
    test.skip(browserName === 'webkit', 'Not supported on WebKit');

    Inside the test body; browserName comes from the fixtures.

  • Known bug / expected failure
    test.fixme();   // not run, marked as fixme
    test.fail();    // runs, must fail
  • Slow test (triple the timeout)
    test.slow();
  • Tag and link an issue
    test('checkout', {
      tag: '@smoke',
      annotation: { type: 'issue', description: 'https://jira.example.com/QA-42' },
    }, async ({ page }) => { /* … */ });
  • Log in once for a file
    test.use({ storageState: 'playwright/.auth/user.json' });

    test.use goes at file or describe level.

  • Run tests in a file in order
    test.describe.configure({ mode: 'serial' });

24. Keyboard Shortcuts (UI Mode)

  • Open UI mode
    npx playwright test --ui

    UI mode is part of the Node.js test runner.

  • Run tests
    F5
  • Stop running tests
    Shift + F5
  • Toggle test output
    Ctrl + `

25. Run & Debug (CLI)

  • Run all tests
    npx playwright test
  • One file
    npx playwright test tests/login.spec.ts
  • By tag or title
    npx playwright test --grep @smoke
  • One browser project
    npx playwright test --project=firefox
  • Watch the browser
    npx playwright test --headed
  • Step through with the Inspector
    npx playwright test --debug
  • Pause inside a test
    await page.pause();
  • Re-run only the failures
    npx playwright test --last-failed
  • Only tests changed since main
    npx playwright test --only-changed=main
  • Parallel workers
    npx playwright test --workers=4
  • Split across CI machines
    npx playwright test --shard=1/4
  • HTML report
    npx playwright show-report
  • Record a test (codegen)
    npx playwright codegen https://example.com

26. Network Mocking

  • Mock a response
    await page.route('**/api/users', (route) =>
      route.fulfill({ status: 200, json: [{ id: 1, name: 'Mock' }] }),
    );
  • Patch a real response
    await page.route('**/api/users', async (route) => {
      const response = await route.fetch();
      const json = await response.json();
      json.push({ id: 99, name: 'Injected' });
      await route.fulfill({ response, json });
    });
  • Add a request header
    await page.route('**/api/**', (route) =>
      route.continue({ headers: { ...route.request().headers(), 'x-test': '1' } }),
    );
  • Simulate a server error
    await page.route('**/api/orders', (route) => route.fulfill({ status: 500 }));
  • Block images
    await page.route(/\.(png|jpg|webp)$/, (route) => route.abort());

FAQ

getByRole with an accessible name, e.g. page.getByRole('button', { name: 'Submit' }). Then getByLabel, getByPlaceholder, getByText and getByTestId. Use CSS or XPath for elements with nothing user-facing to target.

Sources

Related tools

Related guides