Playwright Cheatsheet

Configuration

Use this Playwright reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

playwright.config.ts skeleton

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

export default defineConfig({
  // --- Test discovery ---
  testDir: './tests',
  testMatch: '**/*.spec.ts',          // default
  testIgnore: '**/fixtures/**',

  // --- Parallelism ---
  fullyParallel: true,                // all tests in parallel (default: false)
  workers: process.env.CI ? 2 : undefined,  // undefined = auto (# CPUs)

  // --- Retries ---
  retries: process.env.CI ? 2 : 0,

  // --- Timeouts ---
  timeout: 30_000,                    // per test
  expect: { timeout: 5_000 },         // per assertion

  // --- Global hooks ---
  globalSetup: './global-setup.ts',
  globalTeardown: './global-teardown.ts',

  // --- Reporting ---
  reporter: [
    ['list'],
    ['html', { open: 'on-failure' }],
  ],

  // --- Default options for all tests ---
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    headless: true,
    viewport: { width: 1280, height: 720 },
    locale: 'en-US',
    timezoneId: 'America/New_York',
    actionTimeout: 10_000,
    navigationTimeout: 30_000,
  },

  // --- Projects (cross-browser / device matrix) ---
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
    { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
  ],

  // --- Dev server ---
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
  },
});

Top-level options

OptionDefaultDescription
testDir'tests'Test root directory
testMatch'**/*.{spec,test}.{ts,js}'Glob/regex for test files
testIgnoreExclude patterns
fullyParallelfalseParallelize within files too
workersCPU countWorker process count
retries0Retry count on failure
timeout30000Per-test timeout ms
globalTimeoutTotal run timeout ms
maxFailures0Stop after N failures (0 = never)
forbidOnlyfalseFail if test.only exists (CI safety)
preserveOutput'always''always' | 'never' | 'failures-only'
outputDir'test-results'Artifacts directory
snapshotDirtestDirSnapshot storage
snapshotPathTemplateCustom snapshot path pattern
updateSnapshots'missing''all' | 'missing' | 'none'
reporter'list'Reporter(s)
globalSetupPath to global setup module
globalTeardownPath to global teardown module
metadataArbitrary metadata passed to reporters

use options (BrowserContextOptions + more)

Browser & display

OptionDefaultDescription
headlesstrueHeadless mode
channel'chrome', 'msedge', 'chrome-beta' etc
viewport{w:1280, h:720}Viewport size (null for no viewport)
deviceScaleFactor1DPR
isMobilefalseMobile emulation
hasTouchfalseTouch emulation
colorScheme'light''light' | 'dark' | 'no-preference'
reducedMotion'no-preference''reduce' | 'no-preference'
forcedColors'none''active' | 'none'
localesystemBCP 47 locale
timezoneIdsystemIANA timezone
geolocation{ latitude, longitude, accuracy? }
permissions[]Permissions to grant

Timeouts

OptionDefaultDescription
actionTimeout0 (test timeout)Per-action timeout
navigationTimeout0 (test timeout)Per-navigation timeout

Network

OptionDefaultDescription
baseURLBase for relative goto() calls
extraHTTPHeaders{}Headers added to every request
httpCredentials{ username, password } for HTTP auth
ignoreHTTPSErrorsfalseIgnore TLS errors
offlinefalseSimulate offline
proxy{ server, bypass?, username?, password? }
storageStatePath or object with cookies/localStorage

Artifacts

OptionDefaultDescription
screenshot'off''off' | 'on' | 'only-on-failure'
video'off''off' | 'on' | 'retain-on-failure' | 'on-first-retry'
trace'off''off' | 'on' | 'retain-on-failure' | 'on-first-retry' | 'on-all-retries'

Misc

OptionDefaultDescription
testIdAttribute'data-testid'Attribute used by getByTestId
launchOptions{}Options passed to browser.launch()
contextOptions{}Options passed to browser.newContext()
bypassCSPfalseBypass Content-Security-Policy
javaScriptEnabledtrueEnable JS
serviceWorkers'allow''allow' | 'block'
userAgentbrowser defaultOverride User-Agent
acceptDownloadstrueAllow downloads
downloadsPathtemp dirDownload destination

Projects — multi-browser & device matrix

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

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

    // Mobile
    { name: 'pixel5',    use: { ...devices['Pixel 5'] } },
    { name: 'iphone13',  use: { ...devices['iPhone 13'] } },

    // Branded channels
    { name: 'chrome', use: { channel: 'chrome' } },
    { name: 'edge',   use: { channel: 'msedge' } },

    // Tagged subsets
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: 'auth-tests',
      use: { storageState: 'auth.json' },
      dependencies: ['setup'],        // run setup project first
    },
  ],
});

webServer

webServer: {
  command: 'npm run start',
  url: 'http://localhost:3000',
  reuseExistingServer: !process.env.CI,
  timeout: 120_000,       // ms to wait for server to be ready
  stdout: 'pipe',         // 'pipe' | 'ignore'
  stderr: 'pipe',
  env: { NODE_ENV: 'test' },
},

// Multiple servers
webServer: [
  { command: 'npm run api', url: 'http://localhost:4000' },
  { command: 'npm run web', url: 'http://localhost:3000' },
],

Reporters

reporter: 'list'                               // simple shorthand
reporter: [['list'], ['html']]                 // multiple

// Built-in reporters
['list']                                       // stdout (default non-CI)
['dot']                                        // compact dots
['line']                                       // one line per test
['html', { open: 'on-failure', outputFolder: 'report' }]
['json', { outputFile: 'results.json' }]
['junit', { outputFile: 'results.xml' }]
['github']                                     // GitHub Actions annotations
['blob', { outputDir: 'blob-report' }]         // for merge-reports
['null']                                       // suppress all output

CLI flag overrides

Many config options can be overridden at the CLI without editing the config:

npx playwright test --headed
npx playwright test --workers=4
npx playwright test --retries=1
npx playwright test --timeout=60000
npx playwright test --reporter=dot
npx playwright test --project=chromium
npx playwright test --grep @smoke
npx playwright test --grep-invert @slow
npx playwright test --update-snapshots
npx playwright test --forbid-only

Global setup / teardown

// global-setup.ts
import { chromium, FullConfig } from '@playwright/test';

export default async function globalSetup(config: FullConfig) {
  const { baseURL } = config.projects[0].use;
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto(baseURL + '/login');
  // ... login steps
  await page.context().storageState({ path: 'auth.json' });
  await browser.close();
}

// global-teardown.ts
export default async function globalTeardown() {
  // clean up DBs, temp files, etc.
}

For auth, prefer a setup project with dependencies (above) over globalSetup — setup projects run inside the test runner, so they get fixtures, tracing, and HTML-report visibility.