npm init playwright@latest # scaffold a new project
npx playwright test # run all tests headlessly
npx playwright test tests/auth.spec.ts # run one file
npx playwright test -g "checkout" # run tests matching a title
npx playwright test --ui # interactive UI mode
npx playwright test --headed # show the browser window
npx playwright test --debug # step through with the Inspector
npx playwright test --project=chromium # run a single project
npx playwright codegen example.com # record actions into test code
npx playwright show-report # open the last HTML report
npx playwright install # download browser binaries
npx playwright install --with-deps # also install OS dependencies (CI)Setup details are in how to install Playwright, and the recorder is covered in the codegen guide.
page.getByRole('button', { name: 'Sign in' }) // by ARIA role and accessible name
page.getByText('Welcome back') // by visible text
page.getByLabel('Email') // form control by its label
page.getByPlaceholder('Search') // input by placeholder
page.getByAltText('Company logo') // image by alt text
page.getByTitle('Close') // by title attribute
page.getByTestId('checkout-button') // by data-testid
page.locator('.card') // CSS (or XPath) fallback
page.locator('.card').filter({ hasText: 'Pro' }) // narrow a match
page.getByRole('listitem').first() // also .last() and .nth(2)
page.locator('.row').getByRole('checkbox') // chain to search insidePrefer the getBy methods over raw CSS. The locators guide explains the priority order.
await page.getByRole('button', { name: 'Save' }).click();
await page.getByText('Item').dblclick();
await page.getByLabel('Email').fill('ada@example.com'); // clears, then types
await page.getByLabel('Search').press('Enter'); // single key or chord
await page.getByLabel('Accept terms').check(); // also .uncheck()
await page.getByLabel('Country').selectOption('France'); // by value or label
await page.getByRole('menuitem', { name: 'File' }).hover();
await page.locator('#task').dragTo(page.locator('#done'));
await page.getByLabel('Avatar').setInputFiles('photo.jpg'); // pass [] to clearEvery action auto-waits for the element to be actionable. Details in waits and timeouts.
All of these retry until they pass or the 5 second expect timeout expires. Prefix any of them with .not to invert.
| Assertion | Passes when |
|---|---|
await expect(locator).toBeVisible() | Element is visible |
await expect(locator).toBeHidden() | Element is hidden or absent |
await expect(locator).toHaveText('Done') | Full text matches (string or regex) |
await expect(locator).toContainText('Do') | Text contains the substring |
await expect(locator).toHaveValue('42') | Input has the value |
await expect(locator).toBeChecked() | Checkbox or radio is checked |
await expect(locator).toBeEnabled() | Element is enabled (also toBeDisabled) |
await expect(locator).toHaveCount(3) | Locator resolves to n elements |
await expect(locator).toHaveAttribute('href', '/home') | Attribute has the value |
await expect(locator).toHaveClass(/active/) | Class matches |
await expect(page).toHaveURL(/.*dashboard/) | Page URL matches |
await expect(page).toHaveTitle('Home') | Page title matches |
More patterns, including soft assertions and expect.poll, in the assertions guide.
await page.goto('https://example.com/login'); // waits for the load event
await page.reload();
await page.goBack(); // also goForward()
await page.waitForURL('**/dashboard'); // glob, regex, or predicate
page.url(); // current URL, synchronous
await page.title();import { test, expect } from '@playwright/test';
test.describe('checkout', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/cart');
});
test('pays with a saved card', async ({ page }) => {
await test.step('open payment form', async () => {
await page.getByRole('button', { name: 'Pay now' }).click();
});
await expect(page.getByText('Payment accepted')).toBeVisible();
});
test.skip('applies gift cards', async ({ page }) => {}); // never runs
test.fixme('splits payment', async ({ page }) => {}); // known broken
// test.only(...) runs just that test while debugging
});// Mock an API response
await page.route('**/api/users', (route) =>
route.fulfill({ json: [{ id: 1, name: 'Ada' }] })
);
// Block requests (images, trackers)
await page.route('**/*.png', (route) => route.abort());
// Let a request through unchanged
await page.route('**/api/**', (route) => route.continue());
// Wait for a specific response
const response = await page.waitForResponse(
(r) => r.url().includes('/api/orders') && r.status() === 200
);Register routes before the navigation or click that triggers the request.
import { test, devices } from '@playwright/test';
test.use({ viewport: { width: 1280, height: 720 } });
test.use({ ...devices['iPhone 15'] }); // full device profile
test.use({ locale: 'de-DE' });
test.use({ timezoneId: 'Europe/Berlin' });
test.use({ geolocation: { latitude: 48.8566, longitude: 2.3522 }, permissions: ['geolocation'] });
test.use({ colorScheme: 'dark' });Place test.use at the top level of a file or inside a describe block. The same options work in use in the config for a whole project.
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 2 : 0, // retry only in CI
use: {
baseURL: 'https://staging.example.com',
trace: 'on-first-retry', // record a trace when a retry happens
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});With baseURL set, page.goto('/login') resolves against it.
npx playwright test --debug # Playwright Inspector, step by step
npx playwright test --ui # UI mode with watch and time travel
PWDEBUG=1 npx playwright test # same Inspector via env var
npx playwright show-trace trace.zip # inspect a recorded traceInside a test, await page.pause() stops execution and opens the Inspector at that exact point when running headed. The trace viewer shows a DOM snapshot, console, and network for every action, which makes it the fastest way to understand a CI-only failure.
Any script built from these snippets can do double duty as a production monitor. A Hyperping Browser Check runs your Playwright code on a schedule from multiple regions and alerts you when a step fails. Your test suite catches regressions before deploy, and the same script catches outages after.