Playwright Cheatsheet

Debugging

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 Inspector

npx playwright test --debug                    # pause before every test
npx playwright test --debug tests/foo.spec.ts  # specific file
npx playwright test --debug -g "my test"       # filter by name
PWDEBUG=1 npx playwright test                  # env var equivalent

In the Inspector you can: - Step through actions one at a time - Hover to highlight elements in the browser - Use the Pick locator tool to generate selectors - See action log with timing

page.pause()

test('debug this', async ({ page }) => {
  await page.goto('/');
  await page.pause();      // <-- execution stops here; Inspector opens
  await page.locator('button').click();
});

UI mode (interactive test runner)

npx playwright test --ui
  • Watch mode — re-run tests on file save
  • Timeline view of each action
  • Live DOM snapshot viewer
  • Network waterfall
  • Filter tests by file, title, tag, status
  • Pick locator tool built-in

Headed mode (visible browser)

npx playwright test --headed
npx playwright test --headed --slowMo=500    # slow down actions by 500 ms
// In config / fixture
const browser = await chromium.launch({ headless: false, slowMo: 100 });

Verbose logging

DEBUG=pw:api npx playwright test              # Playwright API calls
DEBUG=pw:browser npx playwright test          # browser protocol messages
DEBUG=pw:* npx playwright test                # everything

console & page errors

// Capture browser console messages
page.on('console', msg => {
  console.log(`[${msg.type()}] ${msg.text()}`);
});

// Capture uncaught errors
page.on('pageerror', err => {
  console.error('Page error:', err.message);
});

// Fail test on console errors
page.on('console', msg => {
  if (msg.type() === 'error') throw new Error(`Console error: ${msg.text()}`);
});

Custom error messages in assertions

await expect(locator, 'Submit button should be visible after form loads')
  .toBeVisible({ timeout: 10_000 });

await expect(page, 'Should redirect to dashboard after login')
  .toHaveURL('/dashboard');

Taking screenshots on failure (manual)

test.afterEach(async ({ page }, testInfo) => {
  if (testInfo.status !== testInfo.expectedStatus) {
    const screenshotPath = testInfo.outputPath('screenshot.png');
    await page.screenshot({ path: screenshotPath, fullPage: true });
    await testInfo.attach('failure-screenshot', {
      path: screenshotPath,
      contentType: 'image/png',
    });
  }
});

Codegen — record tests from browser

npx playwright codegen https://example.com          # opens browser + inspector
npx playwright codegen --target=playwright-test     # TypeScript output (default)
npx playwright codegen --viewport-size=390,844      # mobile viewport
npx playwright codegen --device="iPhone 13"
npx playwright codegen --save-storage=auth.json     # save cookies after recording
npx playwright codegen --load-storage=auth.json     # start authenticated

Codegen outputs to stdout; copy-paste or redirect:

npx playwright codegen https://example.com > tests/recorded.spec.ts

locator.highlight() — visual debug

// Highlight a locator in the browser for visual inspection
await page.locator('.card').first().highlight();

page.locator.evaluate — inspect DOM state

const classes = await page.locator('#btn').evaluate(el => el.className);
console.log(classes);

const computed = await page.locator('h1').evaluate(el =>
  getComputedStyle(el).color
);

Tracing for post-mortem debugging

// playwright.config.ts — keep trace on failure
use: { trace: 'retain-on-failure' }

// View trace after a failed run:
npx playwright show-trace test-results/my-test/trace.zip

The trace viewer shows: - Timeline — every action + duration - Before/after DOM snapshots — interactive clicking - Network — all HTTP requests - Console — log messages

Selectors — debugging strategies

// Count matching elements
const count = await page.locator('.item').count();
console.log(count);

// Check text content
const texts = await page.locator('li').allTextContents();
console.log(texts);

// Dump element handle properties
const box = await page.locator('#btn').boundingBox();
console.log(box);  // { x, y, width, height } or null if not visible

// Check visibility manually
const visible = await page.locator('#modal').isVisible();

Isolate a test run

npx playwright test tests/auth.spec.ts               # single file
npx playwright test -g "should login"                # by title substring
npx playwright test --project=chromium               # single browser
npx playwright test tests/auth.spec.ts:42            # line number

Test step annotations

test('checkout flow', async ({ page }) => {
  await test.step('navigate to cart', async () => {
    await page.goto('/cart');
  });

  await test.step('enter payment info', async () => {
    await page.locator('#card-number').fill('4242424242424242');
  });
});

Steps appear in the HTML report and trace viewer with their own timing.

CLI flags reference

FlagDescription
--debugOpen Inspector before first action
--uiInteractive UI mode
--headedRun with visible browser
--slowMo=NSlow actions by N ms
--timeout=NPer-test timeout ms
--retries=NNumber of retries
--workers=NWorker count
--reporter=XReporter type
--project=XRun specific project
-g "pattern"Filter by test title
--grep-invert "pat"Exclude by title
--listList tests without running
--update-snapshotsUpdate visual snapshots
--forbid-onlyFail if test.only present
--last-failedRe-run only previously failed tests
--pass-with-no-testsExit 0 even if no tests found