Playwright cheat sheet

A scannable Playwright reference: 30 short snippets across 12 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
Install and first testSetting up Playwright, running the generated example test, and the trace viewer that makes failures debuggablelesson
Locators and assertionsA locator is lazy: it describes how to find an element and is resolved again on each use. That is exactly what letslesson
Runner, fixtures and CIProjects and workers, typed fixtures for shared setup, and a CI plan that keeps traces when a test failslesson
Actions and user interactionsEverything on the page object - keyboard, mouse, touchscreen - acts on the page as a whole and bypasses locator checkslesson
Auto-waiting, timeouts and flakinessBefore an action, Playwright waits for a specific set of conditions and retries until they hold or the timeout expireslesson
Network interception and mockingFulfil, modify and abort requests, record a HAR for replay, and assert on what the page actually sentlesson
API testing with request contextsUse the request fixture for direct API tests, and API calls for setup, so a UI test starts from a known server statelesson
Test organisation and page objectsCompose page objects with fixtures, build test data explicitly, and use tags and sharding to keep a growing suite fastlesson
Debugging with UI mode, codegen and inspectorCodegen produces working code, not good code. Its selectors are whatever was stable at the moment you clicked, and theylesson
Visual comparisons, screenshots and videoSet up screenshot baselines that do not fail on every font update, mask the parts that legitimately change, and capturelesson
Multiple browsers, devices and emulationRun the same tests on Chromium, Firefox and WebKit projects, emulate a phone properly, and control locale, timezonelesson
Component testing and advanced configurationMount components in isolation with Playwright CT, write custom reporters, use global setup, and shard a large suitelesson

Quick snippets

Install and first test

Install and run

npm init playwright@latest
# asks for TypeScript or JavaScript, installs browsers, writes playwright.config.ts

npx playwright test              # run everything headless
npx playwright test --headed     # watch the browser
npx playwright test --ui         # interactive time-travel interface
npx playwright show-report       # open the HTML report

What a generated test looks like

import { test, expect } from '@playwright/test';

test('home page has the right title', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await expect(page).toHaveTitle(/Playwright/);
});

test('get started link reaches the docs', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await page.getByRole('link', { name: 'Get started' }).click();
  await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});

Full lesson: Install and first test →

Locators and assertions

Locators that survive a redesign

// preferred: semantics a user would recognise
page.getByRole('button', { name: 'Sign in' });
page.getByLabel('Email address');
page.getByPlaceholder('Search');

// scope to a container, then filter
const row = page.getByRole('row').filter({ hasText: 'Ada Lovelace' });
await row.getByRole('button', { name: 'Edit' }).click();

// escape hatch when nothing semantic exists in the markup
page.getByTestId('checkout-total');

Web-first assertions

await expect(page.getByRole('alert')).toHaveText('Saved');
await expect(page.getByRole('listitem')).toHaveCount(3);
await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled();
await expect(page).toHaveURL(/\/dashboard$/);
await expect(page.getByTestId('spinner')).toBeHidden();

// negate with .not
await expect(page.getByText('Error')).not.toBeVisible();

Full lesson: Locators and assertions →

Runner, fixtures and CI

Running in CI

# the ordered CI steps
npm ci
npx playwright install --with-deps chromium
npx playwright test --shard=1/3
npx playwright merge-reports --reporter html ./blob-report

Full lesson: Runner, fixtures and CI →

Actions and user interactions

Clicks that are not a plain click

const row = page.getByRole("row", { name: "Invoice 1042" });

await row.click();                                   // left, centre, once
await row.dblclick();
await row.click({ button: "right" });                // context menu
await row.click({ modifiers: ["Shift"] });           // range select
await row.click({ position: { x: 10, y: 10 } });     // a specific spot
await row.hover();
await row.click({ trial: true });                    // check actionability, do nothing

Clicks that are not a plain click

// select a range the way a user does
await page.getByRole("row", { name: "Invoice 1042" }).click();
await page.getByRole("row", { name: "Invoice 1047" }).click({ modifiers: ["Shift"] });

// hover to reveal a menu, then click the item
await page.getByRole("button", { name: "Actions" }).hover();
await page.getByRole("menuitem", { name: "Duplicate" }).click();

// drag and drop
await page.getByTestId("card-1").dragTo(page.getByTestId("column-done"));

Files, keyboard and scrolling

// keyboard focus and chords
await page.getByRole("textbox").focus();
await page.keyboard.press("Control+A");
await page.keyboard.press("Backspace");
await page.keyboard.type("replaced");

// global shortcut
await page.keyboard.press("Control+Shift+P");

// programmatic scrolling, when you need a specific element in view
await page.getByText("Terms and conditions").scrollIntoViewIfNeeded();

Full lesson: Actions and user interactions →

Auto-waiting, timeouts and flakiness

What auto-waiting actually checks

// wait for the state you actually depend on, not for time to pass
const banner = page.getByRole("alert");
await banner.waitFor({ state: "visible" });
await expect(banner).toHaveText("Saved");

// states: attached, detached, visible, hidden
await page.getByTestId("spinner").waitFor({ state: "hidden" });

// page-level conditions
await page.waitForLoadState("domcontentloaded");
await page.waitForURL("**/dashboard");
await page.waitForResponse((r) => r.url().includes("/api/orders") && r.ok());

Web-first assertions

// retried until it passes or the expect timeout expires
await expect(page.getByRole("heading")).toHaveText("Invoices");
await expect(page.getByRole("listitem")).toHaveCount(3);
await expect(page.getByRole("button", { name: "Pay" })).toBeEnabled();
await expect(page.getByLabel("Email")).toHaveValue("[email protected]");
await expect(page.getByTestId("total")).toContainText("42.00");
await expect(page).toHaveURL(/\/invoices\/\d+/);
await expect(page).toHaveTitle(/Invoices/);

// negation also retries, which is usually what you want
await expect(page.getByText("Loading")).toBeHidden();

Web-first assertions

// polling something that is not a locator
await expect.poll(async () => {
  const res = await page.request.get("/api/orders");
  return (await res.json()).length;
}, { timeout: 10_000 }).toBe(3);

// retrying a whole block
await expect(async () => {
  await page.getByRole("button", { name: "Refresh" }).click();
  await expect(page.getByRole("row")).toHaveCount(4);
}).toPass({ timeout: 15_000 });

Full lesson: Auto-waiting, timeouts and flakiness →

Network interception and mocking

Record once, replay always

// record: run with the real network, write the archive
await page.routeFromHAR("hars/orders.har", {
  url: "**/api/**",
  update: true,
});

// replay: no network needed for the recorded endpoints
await page.routeFromHAR("hars/orders.har", {
  url: "**/api/**",
  notFound: "abort",        // or "fallback" to hit the real network
});

Asserting on what was sent

// a fixture that fails the test on any unexpected 5xx
export const test = base.extend({
  page: async ({ page }, use) => {
    const failures = [];
    page.on("response", (r) => {
      if (r.status() >= 500) failures.push(r.status() + " " + r.url());
    });
    await use(page);
    expect(failures, "unexpected server errors").toEqual([]);
  },
});

Full lesson: Network interception and mocking →

API testing with request contexts

The request fixture

// a separate context, with its own base URL and headers
const api = await request.newContext({
  baseURL: "https://api.staging.example.com",
  extraHTTPHeaders: { Accept: "application/json" },
  storageState: "playwright/.auth/api.json",
});

Schema checks and contract drift

// a compact contract suite
for (const path of ["/api/orders/1042", "/api/users/me"]) {
  test("contract: " + path, async ({ request }) => {
    const res = await request.get(path);
    await expect(res).toBeOK();
    expect(res.headers()["content-type"]).toContain("application/json");
  });
}

Full lesson: API testing with request contexts →

Test organisation and page objects

Tags, groups and sharding

test.describe("checkout", { tag: ["@checkout", "@slow"] }, () => {
  test("pays with a saved card", { tag: "@smoke" }, async ({ page }) => {
    // ...
  });
});

test.skip(({ browserName }) => browserName === "firefox", "known Firefox bug");

Tags, groups and sharding

npx playwright test --grep @smoke                 # the fast subset
npx playwright test --grep-invert @slow           # everything else
npx playwright test --shard=1/3                   # one third of the suite
npx playwright test --shard=1/3 --reporter=blob   # merge reports afterwards
npx playwright merge-reports --reporter=html ./blob-report

Tags, groups and sharding

strategy:
  matrix:
    shard: [1, 2, 3, 4]
steps:
  - run: npx playwright test --shard=${{ matrix.shard }}/4 --reporter=blob
  - uses: actions/upload-artifact@v4
    with:
      name: blob-${{ matrix.shard }}
      path: blob-report

Full lesson: Test organisation and page objects →

Debugging with UI mode, codegen and inspector

UI mode

npx playwright test --ui

# in UI mode:
# - pick a test and watch it run with a timeline
# - hover a step to see the DOM snapshot at that moment
# - open the source, the console and the network for that step
# - toggle "watch" and re-run on every file save
# - click the locator picker to test a selector against the live page

Codegen and the inspector

# scaffold a test by using the app
npx playwright codegen https://staging.example.com

# start from a saved session so you are already logged in
npx playwright codegen --load-storage=playwright/.auth/user.json https://staging.example.com

# emulate a device while recording
npx playwright codegen --device="iPhone 15" https://staging.example.com

# record against a local file
npx playwright codegen ./fixtures/page.html

Codegen and the inspector

# step through an existing test
npx playwright test invoices.spec.ts --debug

# or pause at a specific point from inside the test
# await page.pause();

Full lesson: Debugging with UI mode, codegen and inspector →

Visual comparisons, screenshots and video

Baselines and expected diffs

npx playwright test visuals.spec.ts                       # compare
npx playwright test visuals.spec.ts --update-snapshots     # accept the new baseline

# inspect the difference report
npx playwright show-report

Screenshots and video on failure

use: {
  screenshot: "only-on-failure",     // or "on" / "off"
  video: "retain-on-failure",        // or "on-first-retry" / "off"
  trace: "on-first-retry",
},

Screenshots and video on failure

// an ad hoc capture from inside a test
await page.screenshot({ path: "artifacts/step-1.png", fullPage: true });
await page.locator("#summary").screenshot({ path: "artifacts/summary.png" });
await page.video()?.saveAs("artifacts/run.webm");

Full lesson: Visual comparisons, screenshots and video →

Multiple browsers, devices and emulation

One suite, several browsers

import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  projects: [
    { name: "chromium", use: { ...devices["Desktop Chrome"] } },
    { name: "firefox", use: { ...devices["Desktop Firefox"] } },
    { name: "webkit", use: { ...devices["Desktop Safari"] } },

    { name: "mobile-safari", use: { ...devices["iPhone 15"] } },
    { name: "mobile-chrome", use: { ...devices["Pixel 8"] } },
  ],
});

One suite, several browsers

npx playwright test --project=chromium
npx playwright test --project=webkit --project=firefox
npx playwright install --with-deps        # fetch browser binaries and OS deps

Locale, timezone and colour scheme

// per-test overrides when you only need one variation
test.describe("light theme", () => {
  test.use({ colorScheme: "light" });
  test("renders a readable contrast ratio", async ({ page }) => { /* ... */ });
});

Full lesson: Multiple browsers, devices and emulation →

Component testing and advanced configuration

Component testing

npm init playwright@latest -- --ct
# a separate config and a separate test directory are created

npx playwright test -c playwright-ct.config.ts

Global setup and teardown

// playwright.config.ts
export default defineConfig({
  globalSetup: "./global-setup.ts",
  globalTeardown: "./global-teardown.ts",
  webServer: {
    command: "npm run start:test",
    url: "http://localhost:3000/health",
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
  },
});

Reporters and sharding

reporter: [
  ["list"],
  ["html", { open: "never", outputFolder: "playwright-report" }],
  ["junit", { outputFile: "reports/junit.xml" }],
  ["./my-reporter.ts"],
],
workers: process.env.CI ? 2 : undefined,

Full lesson: Component testing and advanced configuration →

FAQ

Is this Playwright cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 12 lessons of the Playwright course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full Playwright course — it carries the worked explanations, the edge cases and the exercises behind every line here.

AI Basics AI Agents Math for AI Machine Learning scikit-learn TensorFlow

Last refreshed 2026-09-27.