---
name: playwright-cli
trigger: /playwright-cli
description: >
  Automate browser interactions headlessly for testing, uploads, form
  submission, and screenshot capture using the playwright-cli terminal
  command. Use for scripted browser work and Playwright test runs. NOT for
  driving a real user Chrome session interactively, and not a substitute for
  Playwright MCP browser tools when those are already active.
user_invocable: true
---

> **Public / shared version** — trimmed from a private Claude Code setup and
> posted at smereski.com as a reusable pattern.

# playwright-cli — headless browser automation from the terminal

Automate browser interactions via `playwright-cli` — a terminal-first wrapper
around Playwright. Useful for E2E testing, browser-driven form submission,
screenshot capture, cookie management, and any scripted browser workflow.

## Ground rules (read first)

- **Refs come from snapshots, never from memory.** Every `e<N>` ref must
  appear in the MOST RECENT snapshot output. After any navigation, click, or
  reload, old refs are stale — take a fresh `playwright-cli snapshot` before
  the next interaction. Guessed refs are the #1 failure mode.
- **Verify each step.** After an action, confirm the expected URL/element/text
  is present before proceeding. "Done" means the final snapshot shows the
  expected end state — not that commands exited 0.
- **Retry cap.** If a ref/selector fails: re-snapshot once, retry once. Two
  failures on the same element → stop and report what the snapshot shows.
- **Destructive commands** — only on explicit user request: `kill-all`,
  `close-all`, `delete-data`, `cookie-clear`, `localstorage-clear`.
- **Ambiguity stop:** no URL given and it isn't obvious from context → ask,
  don't guess a URL.
- Always `playwright-cli close` sessions you opened when done.

## Installation

```bash
# Check if available
playwright-cli --version

# If missing, try via npx first
npx --no-install playwright-cli --version

# Install globally only with explicit user approval
npm install -g @playwright/cli@latest
```

## Quick start

```bash
playwright-cli open
playwright-cli goto https://example.com
playwright-cli snapshot
playwright-cli click e15
playwright-cli type "search query"
playwright-cli press Enter
playwright-cli snapshot
playwright-cli close
```

## Core commands

### Navigation

```bash
playwright-cli open
playwright-cli open https://example.com/
playwright-cli goto https://example.com/page
playwright-cli go-back
playwright-cli go-forward
playwright-cli reload
playwright-cli close
```

### Interaction

```bash
playwright-cli click e3
playwright-cli dblclick e7
playwright-cli fill e5 "user@example.com" --submit   # --submit presses Enter
playwright-cli type "text to type"
playwright-cli press Enter
playwright-cli press ArrowDown
playwright-cli hover e4
playwright-cli select e9 "option-value"
playwright-cli check e12
playwright-cli uncheck e12
playwright-cli upload ./document.pdf
playwright-cli drag e2 e8
playwright-cli drop e4 --path=./image.png
playwright-cli drop e4 --data="text/plain=hello world"
playwright-cli dialog-accept
playwright-cli dialog-accept "confirmation text"
playwright-cli dialog-dismiss
playwright-cli resize 1920 1080
```

### Snapshots and screenshots

```bash
playwright-cli snapshot
playwright-cli snapshot --filename=after-click.yaml
playwright-cli snapshot "#main"          # snapshot a specific element
playwright-cli snapshot --depth=4        # limit depth for large pages
playwright-cli snapshot e34              # partial snapshot of element
playwright-cli snapshot --boxes          # include bounding boxes
playwright-cli screenshot
playwright-cli screenshot e5             # screenshot of element
playwright-cli screenshot --filename=page.png
playwright-cli pdf --filename=page.pdf
```

### Targeting elements

```bash
# Preferred: use refs from snapshot
playwright-cli click e15

# CSS selector
playwright-cli click "#main > button.submit"

# Playwright role locator
playwright-cli click "getByRole('button', { name: 'Submit' })"

# Test ID
playwright-cli click "getByTestId('submit-button')"

# Inspect attributes not visible in snapshot
playwright-cli eval "el => el.id" e5
playwright-cli eval "el => el.getAttribute('data-testid')" e5
```

### Storage

```bash
# Save/load browser state (cookies, localStorage — for persistent sessions)
playwright-cli state-save auth.json
playwright-cli state-load auth.json

# Cookies
playwright-cli cookie-list
playwright-cli cookie-get session_id
playwright-cli cookie-set session_id abc123 --domain=example.com --httpOnly --secure
playwright-cli cookie-delete session_id
playwright-cli cookie-clear

# LocalStorage
playwright-cli localstorage-list
playwright-cli localstorage-get theme
playwright-cli localstorage-set theme dark
playwright-cli localstorage-clear

# SessionStorage
playwright-cli sessionstorage-list
playwright-cli sessionstorage-get step
playwright-cli sessionstorage-set step 3
playwright-cli sessionstorage-clear
```

### Network

```bash
playwright-cli route "**/*.jpg" --status=404
playwright-cli route "https://api.example.com/**" --body='{"mock": true}'
playwright-cli route-list
playwright-cli unroute "**/*.jpg"
playwright-cli unroute
playwright-cli requests
playwright-cli request 5
playwright-cli console
playwright-cli console warning
```

### Tabs

```bash
playwright-cli tab-list
playwright-cli tab-new
playwright-cli tab-new https://example.com/page
playwright-cli tab-close
playwright-cli tab-select 0
```

### Sessions (named browsers)

```bash
# Named session with persistent profile (survives close/reopen)
playwright-cli -s=mysession open example.com --persistent
playwright-cli -s=mysession click e6
playwright-cli -s=mysession close

playwright-cli list           # list all sessions
playwright-cli close-all      # close all browsers
playwright-cli kill-all       # force-kill all browser processes
```

### Browser choice

```bash
playwright-cli open --browser=chrome
playwright-cli open --browser=firefox
playwright-cli open --browser=webkit
playwright-cli open --browser=msedge
```

## Persistent profiles (for logged-in sessions)

To reuse an authenticated session across runs:
```bash
# First run: log in manually, then save state
playwright-cli open --persistent
# ... navigate and log in ...
playwright-cli state-save auth.json
playwright-cli close

# Subsequent runs: load saved state
playwright-cli open
playwright-cli state-load auth.json
playwright-cli goto https://example.com/dashboard
```

## Raw output (for piping)

```bash
playwright-cli --raw eval "JSON.stringify(performance.timing)" | jq '.loadEventEnd - .navigationStart'
playwright-cli --raw snapshot > before.yml
playwright-cli click e5
playwright-cli --raw snapshot > after.yml
diff before.yml after.yml
TOKEN=$(playwright-cli --raw cookie-get session_id)
```

## Tracing and video

```bash
playwright-cli tracing-start
playwright-cli click e4
playwright-cli fill e7 "test"
playwright-cli tracing-stop

playwright-cli video-start video.webm
playwright-cli video-chapter "Chapter Title" --description="Details"
playwright-cli video-stop
```

## Patterns

### E2E test flow

```bash
playwright-cli open https://example.com/form
playwright-cli snapshot
playwright-cli fill e1 "user@example.com"
playwright-cli fill e2 "password123"
playwright-cli click e3
playwright-cli snapshot   # verify logged in
playwright-cli close
```

### Multi-tab workflow

```bash
playwright-cli open https://app.example.com
playwright-cli tab-new https://app.example.com/admin
playwright-cli tab-list
playwright-cli tab-select 0
playwright-cli snapshot
playwright-cli close
```

### Diff before/after a click

```bash
playwright-cli open https://example.com
playwright-cli --raw snapshot > before.yml
playwright-cli click e5
playwright-cli --raw snapshot > after.yml
diff before.yml after.yml
playwright-cli close
```

## Gotchas

- **Some sites block headless mode** (bot detection, Kasada, DataDome). Use
  headed mode instead: `playwright-cli open --browser=chrome` with a display
  available, or use `xvfb-run` on Linux servers.
- **Cookie banners and consent dialogs** must be dismissed before interacting
  with page content — take a snapshot first and handle any overlay.
- **Shadow DOM elements** may not appear in the snapshot tree. Use
  `playwright-cli eval` with a CSS selector that pierces the shadow root.
- **Refs are session-scoped.** After `playwright-cli close`, refs from that
  session are gone.
- **Never attach to the user's running browser** with `--cdp` or `--extension`
  unless the user explicitly asks for it — this can interfere with an active
  browsing session.
- **`--persistent` profiles** accumulate state. Clear with
  `playwright-cli delete-data` if a fresh session is needed.
