Playwright Cheat sheet
Everything you use daily in Playwright Test on one page — in TypeScript, using the modern locator-based API Playwright recommends. Bookmark it for coding and for the night before an interview. New to Playwright? Start with The Complete Playwright Tutorial.
Locators (Recommended Order)
Prefer user-facing locators — they survive refactors better than CSS or XPath.
page.getByRole('button', { name: 'Submit' }) // ⭐ best — how users and screen readers see it
page.getByLabel('Email') // form fields with a label
page.getByPlaceholder('Enter email')
page.getByText('Welcome back') // non-interactive text
page.getByAltText('Company logo') // images
page.getByTitle('Close')
page.getByTestId('login-btn') // needs data-testid
page.locator('#username') // CSS — fallback
page.locator('//input[@id="user"]') // XPath — last resort
Filtering and chaining:
page.getByRole('listitem').filter({ hasText: 'Product 2' })
page.getByRole('row').filter({ has: page.getByText('Ravi') })
.getByRole('button', { name: 'Edit' }) // "Edit" in Ravi's row
page.locator('.card').first() // .last() / .nth(2)
page.locator('#form').getByRole('button') // scoped inside a container
⚠️ Strictness: if a locator matches several elements, actions throw instead of guessing. Make it unambiguous with filter(), scoping, or first() when the order genuinely doesn't matter.
Actions
await page.goto('/login'); // uses baseURL
await page.getByRole('button', { name: 'Log in' }).click();
await page.getByLabel('Email').fill('qa@example.com'); // clears, then types
await page.getByLabel('Email').clear();
await page.getByLabel('Search').pressSequentially('playwright', { delay: 50 }); // key by key
await page.getByLabel('Search').press('Enter');
await page.getByLabel('I agree').check(); // checkbox / radio
await page.getByLabel('Country').selectOption('India'); // dropdown
await page.getByLabel('Upload').setInputFiles('files/resume.pdf');
await page.getByText('Products').hover();
await page.getByText('Item').dblclick();
await page.getByText('Item').click({ button: 'right' }); // right-click
await page.locator('#source').dragTo(page.locator('#target'));
Older tutorials use page-level shortcuts like page.click('#btn') and page.type(). They still exist, but Playwright's docs recommend the locator versions above (page.type() is deprecated in favor of pressSequentially()).
Reading values
await page.getByTestId('message').textContent();
await page.getByTestId('message').innerText(); // rendered text only
await page.getByLabel('Email').inputValue();
await page.getByRole('link', { name: 'Docs' }).getAttribute('href');
await page.getByRole('listitem').count();
await page.getByRole('listitem').allTextContents(); // string[]
await page.title();
page.url();
For checks, prefer assertions (below) over reading values and comparing — assertions retry automatically.
Auto-Wait and Waiting ⭐
Before every action Playwright waits for the element to be attached, visible, stable, enabled and able to receive events. You rarely need explicit waits — when you do, wait for a condition, never a fixed time:
// ❌ Avoid — hard-coded pause
await page.waitForTimeout(3000);
// ✅ Prefer — condition-based
await expect(page.getByTestId('result')).toBeVisible();
await page.waitForURL('**/dashboard');
// Wait for a specific API call triggered by a click
const responsePromise = page.waitForResponse(
r => r.url().includes('/api/users') && r.status() === 200
);
await page.getByRole('button', { name: 'Load users' }).click();
await responsePromise;
Playwright's docs discourage waitForLoadState('networkidle') and waitForSelector() for tests — web-first assertions do the same job more reliably. More in Playwright Auto-Wait & Synchronization.
Assertions (Web-First)
These auto-retry until they pass or time out (5 seconds by default):
const msg = page.getByTestId('message');
await expect(msg).toBeVisible();
await expect(msg).toBeHidden();
await expect(msg).toHaveText('Saved');
await expect(msg).toContainText('Sav');
await expect(msg).not.toBeVisible(); // negate any assertion
await expect(
page.getByRole('button', { name: 'Save' })
).toBeEnabled(); // or toBeDisabled()
await expect(page.getByLabel('I agree')).toBeChecked();
await expect(page.getByLabel('Email')).toHaveValue('qa@example.com');
await expect(
page.getByRole('link', { name: 'Docs' })
).toHaveAttribute('href', /docs/);
await expect(page.getByRole('listitem')).toHaveCount(5);
await expect(page).toHaveTitle(/Dashboard/);
await expect(page).toHaveURL(/dashboard/);
await expect.soft(msg).toHaveText('Saved'); // soft: records failure, test continues
await expect(page).toHaveScreenshot('dashboard.png'); // visual comparison
Details: Playwright Assertions & Screenshots.
Frames, Dialogs, New Tabs and Downloads
// iframe
await page
.frameLocator('#payment-frame')
.getByLabel('Card number')
.fill('4111111111111111');
// JavaScript alert / confirm — register the handler BEFORE the click
page.once('dialog', dialog => dialog.accept());
await page.getByRole('button', { name: 'Delete' }).click();
// New tab
const pagePromise = context.waitForEvent('page');
await page.getByRole('link', { name: 'Open in new tab' }).click();
const newTab = await pagePromise;
await newTab.waitForLoadState();
await expect(newTab).toHaveTitle(/Help/);
// Download
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Export CSV' }).click();
const download = await downloadPromise;
await download.saveAs('downloads/' + download.suggestedFilename());
More in Playwright UI Handling: Alerts, Tabs & Keyboard.
Screenshots
await page.screenshot({ path: 'page.png', fullPage: true });
await page
.getByTestId('chart')
.screenshot({ path: 'chart.png' }); // one element
Test Runner
import { test, expect } from '@playwright/test';
test.describe('Login', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
});
test('valid login @smoke', async ({ page }) => {
await page.getByLabel('Username').fill('admin');
await page.getByRole('button', { name: 'Log in' }).click();
await expect(page).toHaveURL(/dashboard/);
});
test('heavy report', async ({ page }) => {
test.slow(); // triples the timeout for this test
await page.goto('/reports');
});
});
| Annotation | Effect |
|---|---|
test.only() |
Run only this test (never commit it) |
test.skip() |
Skip it |
test.fixme() |
Mark as known-broken and skip |
test.slow() |
Triple the timeout |
test.describe.serial() |
Run in order, stop on first failure |
@smoke in the title |
A tag — run with --grep @smoke |
Hooks, workers and retries: Playwright Test Runner.
Custom fixture (e.g. a page object)
import { test as base, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
const test = base.extend<{ loginPage: LoginPage }>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await use(loginPage); // the test runs here
},
});
test('uses a fixture', async ({ loginPage }) => {
await expect(loginPage.page).toHaveURL(/login/);
});
Storage State (Log In Once) ⭐
The biggest speed win in a Playwright suite — every test skips the login flow:
// tests/auth.setup.ts — after logging in once:
await page.context().storageState({
path: 'playwright/.auth/user.json'
});
// playwright.config.ts — reuse it (see the config below)
use: {
storageState: 'playwright/.auth/user.json'
}
Full walkthrough: Playwright Storage State.
API Testing and Network Mocking
// API request (the request fixture)
const res = await request.post('/api/login', {
data: { user: 'admin', pass: 'secret' }
});
expect(res.status()).toBe(200);
const body = await res.json();
// Mock a response
await page.route('**/api/users', route =>
route.fulfill({
status: 200,
json: { users: [] }
})
);
// Simulate a server error
await page.route('**/api/orders', route =>
route.fulfill({ status: 500 })
);
// Block images to speed up runs
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
More: Playwright API Testing & Network Mocking.
Config (playwright.config.ts)
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
workers: process.env.CI ? 2 : undefined,
retries: process.env.CI ? 2 : 0,
timeout: 30_000, // per test
expect: { timeout: 5_000 }, // per assertion
reporter: [['html'], ['list']],
use: {
baseURL: 'https://example.com',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry', // ⭐ enables the trace viewer
},
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json'
},
dependencies: ['setup'],
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
storageState: 'playwright/.auth/user.json'
},
dependencies: ['setup'],
},
],
});
CLI Commands
npx playwright test # run all tests
npx playwright test tests/login.spec.ts # one file
npx playwright test -g "valid login" # by title
npx playwright test --grep @smoke # by tag
npx playwright test --project=firefox # one browser
npx playwright test --headed # see the browser
npx playwright test --ui # ⭐ UI mode — time-travel debugging
npx playwright test --debug # step through with the Inspector
npx playwright test --workers=1 # run serially
npx playwright test --last-failed # re-run only failures
npx playwright show-report # HTML report
npx playwright show-trace trace.zip # ⭐ trace viewer
npx playwright codegen https://example.com # record a test
npx playwright install --with-deps # browsers + OS dependencies (CI)
Pause a running test at any line with await page.pause();. See Playwright Reports & Debugging.
Playwright vs Selenium (Quick)
| Aspect | Playwright | Selenium |
|---|---|---|
| Waits | Automatic | Explicit waits you write |
| Test runner | Built in | TestNG / JUnit |
| API testing & mocking | Built in | Separate tools |
| Debugging | Trace viewer, UI mode | Screenshots and logs |
| Ecosystem | Growing fast | Largest |
→ Full comparison · Convert code with the Selenium ⇄ Playwright Translator
Go Deeper
- The Complete Playwright Tutorial — the full learning path
- Playwright Locators & Actions
- Top 20 Playwright Interview Questions and the Playwright Interview Simulator
- JavaScript for Testers — the language underneath
- Playwright Visualizer — see actions and auto-wait step by step
📚 Official documentation: Selenium docs: Locator strategies · Selenium documentation