Playwright cheat sheet 2026
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.
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 --uiUI 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
Playwright Complete Notes: Beginner to Advanced (TypeScript, 2026)
A 14-chapter Playwright course in TypeScript — architecture, setup, locators, auto-waiting, actions, frames and popups, waits, API mocking, fixtures, debugging, 2026 features and interview questions — with runnable code, self-checks and progress tracking.
How to Build a Scalable Playwright Framework in TypeScript (2026)
A step-by-step blueprint for a maintainable Playwright + TypeScript framework: folder structure you can click through, config, page objects, fixtures, test data, API setup, storageState auth and a sharded GitHub Actions pipeline.
Playwright storageState: Log In Once, Reuse It in Every Test (2026)
Stop logging in before every Playwright test. Save the authenticated browser state once with storageState, reuse it across the suite, and see exactly how much CI time it saves with an interactive calculator.