# Playwright Skill

> Playwright Expert

- Skill: `sirhamza/playwright-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sirhamza/playwright-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sirhamza/playwright-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: SirHamza (https://skillmd.com/u/sirhamza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sirhamza/playwright-skill

---


# Playwright Expert

## Overview
Expert-level mastery of Playwright — Microsoft's end-to-end testing framework for web applications. Covers browser automation, test architecture, CI/CD integration, debugging, and performance testing across Chromium, Firefox, and WebKit.

---

## 1. Core Concepts & Setup

- Playwright architecture: browser → context → page hierarchy
- Install: `npm init playwright@latest` or `npm i -D @playwright/test`
- Browser installation: `npx playwright install` (chromium, firefox, webkit)
- `playwright.config.ts` — projects, baseURL, retries, reporter, timeout, workers
- Running tests: `npx playwright test`, `--headed`, `--debug`, `--ui`
- Test sharding: `--shard=1/4` for parallel CI runs
- `use` options: viewport, locale, timezone, geolocation, permissions, storageState

---

## 2. Test Authoring

- `test()`, `test.describe()`, `test.beforeEach()`, `test.afterAll()`
- `test.only()`, `test.skip()`, `test.fixme()`, `test.fail()`
- `test.extend()` for custom fixtures
- `expect()` assertions — `toBeVisible`, `toHaveText`, `toHaveURL`, `toHaveValue`, `toBeChecked`, `toBeEnabled`, `toHaveCount`, `toContainText`, `toHaveAttribute`, `toHaveClass`, `toHaveScreenshot`
- Soft assertions: `expect.soft()` — continue after failure
- `expect.poll()` for custom async polling
- `test.step()` for structured test steps (shown in reports)
- Parameterized tests via `test.describe.configure({ mode: 'parallel' })`

---

## 3. Locators (Modern API)

- `page.getByRole()` — accessible role + name (preferred)
- `page.getByText()`, `page.getByLabel()`, `page.getByPlaceholder()`
- `page.getByAltText()`, `page.getByTitle()`, `page.getByTestId()`
- `page.locator('css=...')`, `page.locator('xpath=...')`
- Chaining: `page.getByRole('list').getByRole('listitem')`
- Filtering: `.filter({ hasText: '...' })`, `.filter({ has: locator })`
- `.first()`, `.last()`, `.nth(n)`, `.count()`
- `locator.and()`, `locator.or()` for combining locators
- Frame locators: `page.frameLocator('iframe').getByRole(...)`
- Shadow DOM: `locator.locator('pierce/css')`

---

## 4. Actions & Interactions

- `click()`, `dblclick()`, `rightClick()`, `tap()`
- `fill()`, `clear()`, `type()`, `pressSequentially()`
- `press()` — keyboard keys (`Enter`, `Tab`, `Escape`, `Control+A`)
- `hover()`, `focus()`, `blur()`
- `selectOption()` — by value, label, or index
- `check()`, `uncheck()`, `setChecked()`
- `setInputFiles()` — file upload (single, multiple, drag-drop)
- `dragAndDrop(source, target)`
- `scrollIntoViewIfNeeded()`, `scrollTo()`
- `evaluate()`, `evaluateHandle()` — run JS in browser context
- `dispatchEvent()` for synthetic events

---

## 5. Navigation & Waiting

- `page.goto(url, { waitUntil: 'networkidle' | 'domcontentloaded' | 'load' | 'commit' })`
- `page.reload()`, `page.goBack()`, `page.goForward()`
- `page.waitForURL()`, `page.waitForLoadState()`
- `page.waitForSelector()` (legacy), prefer `expect(locator).toBeVisible()`
- `page.waitForFunction()` — custom JS condition
- `page.waitForTimeout()` — use sparingly (anti-pattern in most cases)
- `page.waitForResponse()`, `page.waitForRequest()` for network
- `locator.waitFor({ state: 'visible' | 'hidden' | 'attached' | 'detached' })`

---

## 6. Network Interception & Mocking

- `page.route(url, handler)` — intercept and modify requests
- `route.fulfill({ status, body, json, contentType })` — mock responses
- `route.abort()` — block requests
- `route.continue()` — pass through with optional modifications
- `page.routeFromHAR(harFile)` — replay recorded network traffic
- `context.routeFromHAR()` for context-level routing
- `page.on('request', cb)`, `page.on('response', cb)` — event listeners
- Glob patterns: `page.route('**/api/**', ...)`, regex supported
- `page.waitForResponse(urlOrPredicate)` — assert specific API calls

---

## 7. Authentication & State

- `storageState` — save/load cookies + localStorage
  ```ts
  await context.storageState({ path: 'auth.json' })
  use: { storageState: 'auth.json' }
  ```
- Global setup with `globalSetup` in config for one-time auth
- `browser.newContext({ httpCredentials: { username, password } })` — basic auth
- Cookie management: `context.addCookies()`, `context.clearCookies()`
- `page.context().storageState()` — extract current session

---

## 8. Fixtures & Page Object Model

- Built-in fixtures: `page`, `browser`, `context`, `browserName`, `request`
- Custom fixtures via `test.extend<{ myFixture: MyType }>({ ... })`
- Scoped fixtures: `{ scope: 'worker' | 'test' }` for performance
- Page Object Model pattern:
  ```ts
  class LoginPage {
    constructor(private page: Page) {}
    async login(user: string, pass: string) {
      await this.page.getByLabel('Email').fill(user)
      await this.page.getByLabel('Password').fill(pass)
      await this.page.getByRole('button', { name: 'Sign in' }).click()
    }
  }
  ```
- Fixture composition — inject page objects into tests cleanly
- `mergeTests()` for combining fixture sets across modules

---

## 9. Visual Testing & Screenshots

- `expect(page).toHaveScreenshot('name.png')` — pixel diff
- `expect(locator).toHaveScreenshot()` — element screenshot
- `toHaveScreenshot({ maxDiffPixels, threshold, animations: 'disabled' })`
- `page.screenshot({ fullPage: true, clip: { x, y, width, height } })`
- Update snapshots: `npx playwright test --update-snapshots`
- `expect(page).toMatchAriaSnapshot()` — accessibility tree snapshot

---

## 10. API Testing

- `request` fixture for context-level HTTP requests
- `const apiContext = await playwright.request.newContext({ baseURL, extraHTTPHeaders })`
- `apiContext.get()`, `.post()`, `.put()`, `.delete()`, `.patch()`
- `await expect(response).toBeOK()`
- `response.json()`, `response.text()`, `response.body()`
- Combine UI + API: authenticate via API, test via UI

---

## 11. Multi-Tab, Frames & Popups

- `context.newPage()` — open new tab
- `page.on('popup', popup => ...)` — handle popups
- `Promise.all([page.waitForEvent('popup'), page.click(...)])` — race pattern
- `page.frames()`, `page.frame({ name | url })`, `page.mainFrame()`
- `page.frameLocator('iframe[name="..."]')` — modern frame locator
- Multiple browser contexts: isolated sessions, different users
- `page.on('dialog', dialog => dialog.accept())` — alert/confirm/prompt

---

## 12. Mobile & Device Emulation

- `devices` from `@playwright/test` — iPhone, Pixel, iPad presets
  ```ts
  use: { ...devices['iPhone 14'] }
  ```
- `viewport`, `userAgent`, `isMobile`, `hasTouch`, `deviceScaleFactor`
- `page.emulateMedia({ colorScheme: 'dark', reducedMotion: 'reduce' })`
- Geolocation: `context.setGeolocation({ latitude, longitude })`
- Permissions: `context.grantPermissions(['geolocation', 'notifications'])`
- Network throttling via CDP: `client.send('Network.emulateNetworkConditions', ...)`

---

## 13. Debugging

- `npx playwright test --debug` — Playwright Inspector
- `npx playwright test --ui` — UI mode (watch mode, time travel)
- `await page.pause()` — pause in debug mode
- `PWDEBUG=1` environment variable
- `slowMo` option: `use: { launchOptions: { slowMo: 500 } }`
- `--trace on` — record trace files
- `npx playwright show-trace trace.zip` — Trace Viewer
- `page.on('console', msg => console.log(msg.text()))` — browser logs
- `page.on('pageerror', err => ...)` — uncaught exceptions
- `test.use({ video: 'on' })` — record video on failure

---

## 14. Reporters & CI/CD

- Built-in reporters: `list`, `dot`, `line`, `html`, `json`, `junit`, `github`
- HTML report: `npx playwright show-report`
- `PLAYWRIGHT_JSON_OUTPUT_NAME` for CI artifact collection
- GitHub Actions:
  ```yaml
  - uses: microsoft/playwright-github-action@v1
  - run: npx playwright test
  - uses: actions/upload-artifact@v3
    with:
      name: playwright-report
      path: playwright-report/
  ```
- Docker: `mcr.microsoft.com/playwright:v1.x.x-jammy`
- Sharding: `--shard=1/4` across matrix jobs
- `retries: 2` in config for flake mitigation

---

## 15. Advanced Patterns

- `test.describe.configure({ mode: 'parallel' })` — parallel test suites
- `expect.extend()` — custom matchers
- `page.addInitScript()` — inject script before page load
- `context.addInitScript()` — context-wide script injection
- Accessibility testing with `@axe-core/playwright`:
  ```ts
  const accessibilityScanResults = await new AxeBuilder({ page }).analyze()
  expect(accessibilityScanResults.violations).toEqual([])
  ```
- CDP (Chrome DevTools Protocol): `await page.context().newCDPSession(page)`
- `browser.newPersistentContext()` — persistent user data dir
- `playwright codegen <url>` — record and generate test code
- Component testing: `@playwright/experimental-ct-react` (React, Vue, Svelte)
- `page.clock.install()` — control timers, Date, setTimeout

---

## 16. TypeScript Best Practices

- Full TypeScript support out of the box with `@playwright/test`
- Use `Locator`, `Page`, `BrowserContext`, `Browser` types from `'@playwright/test'`
- `PlaywrightTestConfig` for typed config
- Strict mode: `use: { strictMode: true }` — fail on multiple element matches
- `expect` return type assertions with `toPass()` for polling

---

## Core Competency Summary

- Architect scalable test suites using POM and fixtures
- Write reliable, flake-resistant tests with proper locators and waits
- Mock and intercept network traffic for isolated tests
- Perform visual regression, accessibility, and API testing
- Debug with Trace Viewer, UI mode, and Inspector
- Integrate into CI/CD pipelines with sharding and HTML reports
- Emulate mobile devices, dark mode, and geolocation
- Leverage TypeScript, custom matchers, and advanced CDP features

