Overview
Playwright is the default browser automation tool for agent workflows. Use it for screenshots, form automation, auth state capture, and UI regression verification.
Tool Selection
| Task | Tool | |------|------| | Local screenshots / UI reproduction | Playwright (local, free, default) | | Headless automation with annotated snapshots | playwright-cli agent tool | | Site blocks headless / anti-bot needed | Steel.dev | | Need video proof of state | Hyperbrowser | | Just need page content, no browser | Firecrawl |
Default to local Playwright. Only escalate to cloud tools when the site blocks headless.
playwright-cli Quick Reference
# Open browser
playwright-cli open
# Navigate
playwright-cli goto https://example.com
# Interact using snapshot refs
playwright-cli click e15
playwright-cli type "search query"
playwright-cli press Enter
# Screenshot
playwright-cli screenshot
# Close
playwright-cli close
npx playwright Commands
# Viewport screenshot
npx playwright screenshot https://example.com out.png
# Full-page screenshot
npx playwright screenshot --full-page https://example.com out.png
# PDF capture
npx playwright pdf https://example.com out.pdf
# Save auth state
npx playwright open --save-storage=auth.json https://example.com
# Reuse auth state
npx playwright open --load-storage=auth.json https://example.com
Browser Selection
playwright-cli open --browser=chrome
playwright-cli open --browser=firefox
playwright-cli open --browser=webkit
playwright-cli open --browser=msedge
Session Management
# Named persistent session
playwright-cli -s=mysession open https://example.com --persistent
# Continue in same session
playwright-cli -s=mysession click e6
# Close named session
playwright-cli -s=mysession close
Scripted Auth Capture
Capture a logged-in session for reuse across automation runs:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
ctx = browser.new_context()
page = ctx.new_page()
page.goto("https://app.example.com/login")
input("Log in manually, then press Enter...")
ctx.storage_state(path="auth.json")
browser.close()
Then replay in automation:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
ctx = browser.new_context(storage_state="auth.json")
page = ctx.new_page()
page.goto("https://app.example.com/dashboard")
page.screenshot(path="dashboard.png")
Parallel Sessions
from playwright.sync_api import sync_playwright
from concurrent.futures import ThreadPoolExecutor
def capture(url):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(url)
page.screenshot(path=f"{url.split('//')[1].replace('/', '_')}.png")
browser.close()
urls = ["https://site1.com", "https://site2.com", "https://site3.com"]
with ThreadPoolExecutor(max_workers=3) as executor:
executor.map(capture, urls)
Form Automation Pattern
page.fill("input[name='email']", "user@example.com")
page.fill("input[name='password']", "secret")
page.click("button[type='submit']")
page.wait_for_url("**/dashboard")
page.screenshot(path="post-login.png")
Waiting Strategies
# Wait for network idle
page.goto(url, wait_until="networkidle")
# Wait for specific element
page.wait_for_selector(".dashboard-loaded")
# Wait for navigation
page.wait_for_url("**/success")
# Wait for response
with page.expect_response("**/api/data") as resp:
page.click("#load-data")
Snapshot-Based Navigation
After any playwright-cli command, a snapshot file is generated describing interactive elements with reference IDs (e.g., e15, e22). Reference these IDs for subsequent actions instead of CSS selectors.
playwright-cli goto https://app.example.com
# Snapshot shows: e15 = "Login button", e22 = "Email input"
playwright-cli click e22
playwright-cli type "user@example.com"
playwright-cli click e15
Proof of Work Pattern
For agent QA deliveries, screenshot every key state:
states = [
("https://app.example.com", "proof-landing.png"),
("https://app.example.com/login", "proof-login.png"),
("https://app.example.com/dashboard", "proof-dashboard.png"),
]
for url, filename in states:
page.goto(url)
page.screenshot(path=filename, full_page=True)