{"id":"f4a45e16-139d-4453-ad72-cf44b22c0a22","shortId":"2ABubM","kind":"skill","title":"Webapp Testing","tagline":"Skills skill by Anthropics","description":"# Web Application Testing\n\nTo test local web applications, write native Python Playwright scripts.\n\n**Helper Scripts Available**:\n- `scripts/with_server.py` - Manages server lifecycle (supports multiple servers)\n\n**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.\n\n## Decision Tree: Choosing Your Approach\n\n```\nUser task → Is it static HTML?\n    ├─ Yes → Read HTML file directly to identify selectors\n    │         ├─ Success → Write Playwright script using selectors\n    │         └─ Fails/Incomplete → Treat as dynamic (below)\n    │\n    └─ No (dynamic webapp) → Is the server already running?\n        ├─ No → Run: python scripts/with_server.py --help\n        │        Then use the helper + write simplified Playwright script\n        │\n        └─ Yes → Reconnaissance-then-action:\n            1. Navigate and wait for networkidle\n            2. Take screenshot or inspect DOM\n            3. Identify selectors from rendered state\n            4. Execute actions with discovered selectors\n```\n\n## Example: Using with_server.py\n\nTo start a server, run `--help` first, then use the helper:\n\n**Single server:**\n```bash\npython scripts/with_server.py --server \"npm run dev\" --port 5173 -- python your_automation.py\n```\n\n**Multiple servers (e.g., backend + frontend):**\n```bash\npython scripts/with_server.py \\\n  --server \"cd backend && python server.py\" --port 3000 \\\n  --server \"cd frontend && npm run dev\" --port 5173 \\\n  -- python your_automation.py\n```\n\nTo create an automation script, include only Playwright logic (servers are managed automatically):\n```python\nfrom playwright.sync_api import sync_playwright\n\nwith sync_playwright() as p:\n    browser = p.chromium.launch(headless=True) # Always launch chromium in headless mode\n    page = browser.new_page()\n    page.goto('http://localhost:5173') # Server already running and ready\n    page.wait_for_load_state('networkidle') # CRITICAL: Wait for JS to execute\n    # ... your automation logic\n    browser.close()\n```\n\n## Reconnaissance-Then-Action Pattern\n\n1. **Inspect rendered DOM**:\n   ```python\n   page.screenshot(path='/tmp/inspect.png', full_page=True)\n   content = page.content()\n   page.locator('button').all()\n   ```\n\n2. **Identify selectors** from inspection results\n\n3. **Execute actions** using discovered selectors\n\n## Common Pitfall\n\n❌ **Don't** inspect the DOM before waiting for `networkidle` on dynamic apps\n✅ **Do** wait for `page.wait_for_load_state('networkidle')` before inspection\n\n## Best Practices\n\n- **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. \n- Use `sync_playwright()` for synchronous scripts\n- Always close the browser when done\n- Use descriptive selectors: `text=`, `role=`, CSS selectors, or IDs\n- Add appropriate waits: `page.wait_for_selector()` or `page.wait_for_timeout()`\n\n## Reference Files\n\n- **examples/** - Examples showing common patterns:\n  - `element_discovery.py` - Discovering buttons, links, and inputs on a page\n  - `static_html_automation.py` - Using file:// URLs for local HTML\n  - `console_logging.py` - Capturing console logs during automation","tags":["webapp","testing","skills","anthropics"],"capabilities":["skill","source-anthropics","category-skills"],"categories":["skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/anthropics/skills/webapp-testing","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"install_from":"skills.sh"}},"qualityScore":"0.500","qualityRationale":"deterministic score 0.50 from registry signals: · indexed on skills.sh · published under anthropics/skills","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T02:40:12.507Z","embedding":null,"createdAt":"2026-04-18T20:23:47.350Z","updatedAt":"2026-04-24T02:40:12.507Z","lastSeenAt":"2026-04-24T02:40:12.507Z","tsv":"'/tmp/inspect.png':295 '1':146,288 '2':152,304 '3':158,310 '3000':211 '4':164 '5173':194,219,262 'abslut':58 'accomplish':349 'action':145,166,286,312 'add':404 'alreadi':126,264 'alway':30,251,389 'anthrop':6 'api':238 'app':329 'applic':8,14 'approach':94 'appropri':405 'autom':225,280,441 'automat':234 'avail':22,358 'backend':200,207 'bash':186,202 'best':340 'black':80,346 'black-box':79 'box':81,347 'browser':247,392 'browser.close':282 'browser.new':258 'bundl':343 'button':302,423 'call':76 'captur':437 'category-skills' 'cd':206,213 'choos':92 'chromium':253 'close':390 'clutter':371 'common':316,366,419 'complex':367 'consid':352 'consol':438 'console_logging.py':436 'content':299 'context':70,88,373 'creat':223 'critic':273 'css':400 'custom':55 'decis':90 'descript':396 'dev':192,217 'direct':77,105,382 'discov':168,314,422 'dom':157,291,322 'done':394 'dynam':118,121,328 'e.g':199 'element_discovery.py':421 'exampl':170,416,417 'execut':165,278,311 'exist':73 'fails/incomplete':115 'file':104,415 'find':52 'first':35,50,179 'frontend':201,214 'full':296 'handl':365 'headless':249,255 'help':34,132,178,362,376 'helper':20,136,183 'html':100,103,435 'id':403 'identifi':107,159,305 'import':239 'includ':227 'ingest':85 'input':426 'inspect':156,289,308,320,339 'invok':381 'js':276 'larg':65 'launch':252 'lifecycl':26 'link':424 'load':270,335 'local':12,434 'localhost':261 'log':439 'logic':230,281 'manag':24,233 'mode':256 'multipl':28,197 'nativ':16 'navig':147 'necessari':59 'networkidl':151,272,326,337 'npm':190,215 'one':354 'p':246 'p.chromium.launch':248 'page':257,259,297,429 'page.content':300 'page.goto':260 'page.locator':301 'page.screenshot':293 'page.wait':268,333,407,411 'path':294 'pattern':287,420 'pitfal':317 'playwright':18,111,139,229,241,244,385 'playwright.sync':237 'pollut':68 'port':193,210,218 'practic':341 'python':17,130,187,195,203,208,220,235,292 'rather':83 'read':41,102 'readi':267 'reconnaiss':143,284 'reconnaissance-then-act':142,283 'refer':414 'reliabl':369 'render':162,290 'result':309 'role':399 'run':31,47,127,129,177,191,216,265 'screenshot':154 'script':19,21,32,49,61,82,112,140,226,344,357,360,364,388 'scripts/with_server.py':23,131,188,204 'see':37,378 'selector':108,114,160,169,306,315,397,401,409 'server':25,29,125,176,185,189,198,205,212,231,263 'server.py':209 'show':418 'simplifi':138 'singl':184 'skill':3,4 'solut':56 'sourc':43 'source-anthropics' 'start':174 'state':163,271,336 'static':99 'static_html_automation.py':430 'success':109 'support':27 'sync':240,243,384 'synchron':387 'take':153 'task':96,351 'test':2,9,11 'text':398 'thus':67 'timeout':413 'treat':116 'tree':91 'tri':46 'true':250,298 'url':432 'usag':38,379 'use':113,134,171,181,313,342,375,383,395,431 'user':95 'wait':149,274,324,331,406 'web':7,13 'webapp':1,122 'whether':353 'window':71,89,374 'with_server.py':172 'without':370 'workflow':368 'write':15,110,137 'yes':101,141 'your_automation.py':196,221","prices":[{"id":"d5110922-b0b8-4cb1-a83f-71b946b497d8","listingId":"f4a45e16-139d-4453-ad72-cf44b22c0a22","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"anthropics","category":"skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:23:47.350Z"}],"sources":[{"listingId":"f4a45e16-139d-4453-ad72-cf44b22c0a22","source":"github","sourceId":"anthropics/skills/webapp-testing","sourceUrl":"https://github.com/anthropics/skills/tree/main/skills/webapp-testing","isPrimary":false,"firstSeenAt":"2026-04-18T21:24:31.475Z","lastSeenAt":"2026-04-24T00:50:10.686Z"},{"listingId":"f4a45e16-139d-4453-ad72-cf44b22c0a22","source":"skills_sh","sourceId":"anthropics/skills/webapp-testing","sourceUrl":"https://skills.sh/anthropics/skills/webapp-testing","isPrimary":true,"firstSeenAt":"2026-04-18T20:23:47.350Z","lastSeenAt":"2026-04-24T02:40:12.507Z"}],"details":{"listingId":"f4a45e16-139d-4453-ad72-cf44b22c0a22","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"anthropics","slug":"webapp-testing","source":"skills_sh","category":"skills","skills_sh_url":"https://skills.sh/anthropics/skills/webapp-testing"},"updatedAt":"2026-04-24T02:40:12.507Z"}}