diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-19 15:24:51 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-19 15:24:51 -0500 |
| commit | 4ffa7417a359ef4eae09f61d7da4de06539462ca (patch) | |
| tree | b8eeb8aa5ec2344216c0f0cdcdcc82d0df307ce3 /playwright-py/SKILL.md | |
| parent | 11f5f003eef12bff9633ca8190e3c43c7dab6708 (diff) | |
| download | rulesets-4ffa7417a359ef4eae09f61d7da4de06539462ca.tar.gz rulesets-4ffa7417a359ef4eae09f61d7da4de06539462ca.zip | |
refactor(playwright): split into playwright-js + playwright-py variants
Rename `playwright-skill/` → `playwright-js/` and add `playwright-py/`
as a verbatim fork of Anthropic's official `webapp-testing` skill
(Apache-2.0). Cross-pollinate: each skill gains patterns and helpers
inspired by the other's strengths, with upstream semantics preserved.
## playwright-js (JS/TS stack)
Renamed from playwright-skill; upstream lackeyjb MIT content untouched.
New sections added (clearly marked, preserving upstream semantics):
- Static HTML vs Dynamic Webapp decision tree (core Anthropic methodology)
- Reconnaissance-Then-Action pattern (navigate → networkidle → inspect → act)
- Console Log Capture snippet (page.on console/pageerror/requestfailed)
Description updated to clarify JS/TS stack fit (React/Next/Vue/Svelte/Node)
and reference `/playwright-py` as the Python sibling.
## playwright-py (Python stack)
Verbatim fork of anthropics/skills/skills/webapp-testing; upstream SKILL.md
and bundled `scripts/with_server.py` + examples kept intact. New scripts
and examples added (all lackeyjb-style conveniences in Python):
Scripts:
scripts/detect_dev_servers.py Probe common localhost ports for HTTP
servers; outputs JSON of found services.
scripts/safe_actions.py safe_click, safe_type (retry-wrapped),
handle_cookie_banner (common selectors),
build_context_with_headers (env-var-
driven: PW_HEADER_NAME / PW_HEADER_VALUE /
PW_EXTRA_HEADERS='{…json…}').
Examples:
examples/login_flow.py Login form + wait_for_url.
examples/broken_links.py Scan visible external hrefs via HEAD.
examples/responsive_sweep.py Multi-viewport screenshots to /tmp.
SKILL.md gains 5 "Added:" sections documenting the new scripts, retry
helpers, env-header injection, and /tmp script discipline. Attribution
notes explicitly mark upstream vs local additions.
## Makefile
SKILLS: playwright-skill → playwright-js + playwright-py
deps target: extended Playwright step to install Python package +
Chromium via `python3 -m pip install --user playwright && python3 -m
playwright install chromium` when playwright-py/ is present. Idempotent
(detected via `python3 -c "import playwright"`).
## Usage
Both skills symlinked globally via `make install`. Invoke whichever
matches the project stack — cross-references in descriptions route you
to the right one. Run `make deps` once to install both runtimes.
Diffstat (limited to 'playwright-py/SKILL.md')
| -rw-r--r-- | playwright-py/SKILL.md | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/playwright-py/SKILL.md b/playwright-py/SKILL.md new file mode 100644 index 0000000..1dee60e --- /dev/null +++ b/playwright-py/SKILL.md @@ -0,0 +1,175 @@ +--- +name: playwright-py +description: Browser automation and UI testing with Playwright using the Python (sync_api) bindings. Native Python scripts using `playwright.sync_api`, server lifecycle management via `with_server.py` (can manage backend + frontend simultaneously), headless Chromium by default, reconnaissance-then-action methodology for dynamic pages. Ships bundled helpers (dev server probe, safe click/type with retries, cookie banner handler, env-driven header injection) and worked examples (login flow, broken-link scan, responsive viewport sweep). Use when testing a web app with a Python stack (Django, FastAPI, Flask), when wiring browser tests into pytest, or when backend and frontend need to be launched together. See also `/playwright-js` for JavaScript/TypeScript variant (React, Next.js, Vue frontends). +license: Complete terms in LICENSE.txt +--- + +# Web Application Testing + +To test local web applications, write native Python Playwright scripts. + +**Helper Scripts Available**: +- `scripts/with_server.py` - Manages server lifecycle (supports multiple servers) + +**Always run scripts with `--help` first** to see usage. DO NOT read the source until you try running the script first and find that a customized solution is abslutely necessary. These scripts can be very large and thus pollute your context window. They exist to be called directly as black-box scripts rather than ingested into your context window. + +## Decision Tree: Choosing Your Approach + +``` +User task → Is it static HTML? + ├─ Yes → Read HTML file directly to identify selectors + │ ├─ Success → Write Playwright script using selectors + │ └─ Fails/Incomplete → Treat as dynamic (below) + │ + └─ No (dynamic webapp) → Is the server already running? + ├─ No → Run: python scripts/with_server.py --help + │ Then use the helper + write simplified Playwright script + │ + └─ Yes → Reconnaissance-then-action: + 1. Navigate and wait for networkidle + 2. Take screenshot or inspect DOM + 3. Identify selectors from rendered state + 4. Execute actions with discovered selectors +``` + +## Example: Using with_server.py + +To start a server, run `--help` first, then use the helper: + +**Single server:** +```bash +python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py +``` + +**Multiple servers (e.g., backend + frontend):** +```bash +python scripts/with_server.py \ + --server "cd backend && python server.py" --port 3000 \ + --server "cd frontend && npm run dev" --port 5173 \ + -- python your_automation.py +``` + +To create an automation script, include only Playwright logic (servers are managed automatically): +```python +from playwright.sync_api import sync_playwright + +with sync_playwright() as p: + browser = p.chromium.launch(headless=True) # Always launch chromium in headless mode + page = browser.new_page() + page.goto('http://localhost:5173') # Server already running and ready + page.wait_for_load_state('networkidle') # CRITICAL: Wait for JS to execute + # ... your automation logic + browser.close() +``` + +## Reconnaissance-Then-Action Pattern + +1. **Inspect rendered DOM**: + ```python + page.screenshot(path='/tmp/inspect.png', full_page=True) + content = page.content() + page.locator('button').all() + ``` + +2. **Identify selectors** from inspection results + +3. **Execute actions** using discovered selectors + +## Common Pitfall + +❌ **Don't** inspect the DOM before waiting for `networkidle` on dynamic apps +✅ **Do** wait for `page.wait_for_load_state('networkidle')` before inspection + +## Best Practices + +- **Use bundled scripts as black boxes** - To accomplish a task, consider whether one of the scripts available in `scripts/` can help. These scripts handle common, complex workflows reliably without cluttering the context window. Use `--help` to see usage, then invoke directly. +- Use `sync_playwright()` for synchronous scripts +- Always close the browser when done +- Use descriptive selectors: `text=`, `role=`, CSS selectors, or IDs +- Add appropriate waits: `page.wait_for_selector()` or `page.wait_for_timeout()` + +## Reference Files + +- **examples/** - Examples showing common patterns: + - `element_discovery.py` - Discovering buttons, links, and inputs on a page + - `static_html_automation.py` - Using file:// URLs for local HTML + - `console_logging.py` - Capturing console logs during automation + - `login_flow.py` - Worked login example (added in this fork) + - `broken_links.py` - Scan visible external links for broken URLs (added in this fork) + - `responsive_sweep.py` - Screenshot multiple viewports for responsive QA (added in this fork) + +--- + +## Added: Dev Server Detection + +Before testing, see what's running on localhost. Run the bundled helper: + +```bash +python scripts/detect_dev_servers.py +``` + +Outputs JSON: `[{"port": 5173, "url": "http://localhost:5173", "server": "vite"}, ...]`. Use this to discover the target URL rather than hardcoding it. If nothing is found, either start the server manually or use `scripts/with_server.py`. + +## Added: Retry Helpers + +Dynamic pages sometimes fail a click or fill on the first try. `scripts/safe_actions.py` provides retry-wrapped wrappers and a cookie-banner handler: + +```python +from scripts.safe_actions import safe_click, safe_type, handle_cookie_banner + +page.goto(TARGET_URL) +page.wait_for_load_state('networkidle') +handle_cookie_banner(page) # clicks common accept buttons if present +safe_type(page, 'input[name="email"]', 'test@example.com') +safe_click(page, 'button[type="submit"]') +``` + +Each helper retries up to 3 times with a short delay and re-raises the last error if all attempts fail. + +## Added: Env-Driven Header Injection + +For authenticated testing without hardcoding tokens. Set env vars: + +```bash +export PW_HEADER_NAME="Authorization" +export PW_HEADER_VALUE="Bearer eyJhbGciOi…" +# or multiple: +export PW_EXTRA_HEADERS='{"X-API-Key": "…", "X-Tenant": "acme"}' +``` + +Then in your script: + +```python +from scripts.safe_actions import build_context_with_headers + +with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + context = build_context_with_headers(browser) # auto-applies env vars + page = context.new_page() + ... +``` + +Falls back to no extra headers when env vars are unset. + +## Added: Script Discipline + +Write ad-hoc Playwright automation scripts to `/tmp/pw-<topic>-<date>.py`, not into the project directory. Reasons: + +- OS reaps `/tmp` periodically; no stale test files to clean up +- Scripts don't clutter git status +- Keeps the project tree focused on code and not on investigation artifacts + +For reusable tests that belong to the project (pytest suites, CI scripts), commit them under `tests/` as usual. One-off investigation scripts go in `/tmp`. + +--- + +## Attribution + +Forked from [anthropics/skills/skills/webapp-testing](https://github.com/anthropics/skills/tree/main/skills/webapp-testing) — Apache 2.0 licensed. See `LICENSE.txt` in this directory for the original copyright and terms. + +**Local additions** (not upstream): +- `scripts/detect_dev_servers.py`, `scripts/safe_actions.py` — new helpers inspired by the sibling `playwright-js` skill (lackeyjb MIT) which bundles equivalent helpers in JavaScript. +- `examples/login_flow.py`, `examples/broken_links.py`, `examples/responsive_sweep.py` — worked examples. +- The five *Added:* sections above (Dev Server Detection, Retry Helpers, Env-Driven Header Injection, Script Discipline, and updated Reference Files list). + +The upstream skill is self-contained and headless-by-default; the additions here pair the Python side with the same conveniences Craig's `playwright-js` fork has on the JavaScript side, without changing upstream semantics.
\ No newline at end of file |
