| name | webapp-testing |
| description | Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. |
| license | Complete terms in LICENSE.txt |
Web Application Testing Toolkit
Test local web applications using Playwright with Python.
Dependencies
pip install playwright
playwright install chromium
Decision Tree
- Static HTML? → Read the file directly
- Dynamic app? → Use
scripts/with_server.py to manage the server
- Need screenshots? → Use Playwright with
page.screenshot()
Core Pattern
import asyncio
from playwright.async_api import async_playwright
async def test_app():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("http://localhost:3000")
await page.wait_for_load_state('networkidle')
title = await page.title()
print(f"Title: {title}")
await page.screenshot(path="screenshot.png")
await browser.close()
asyncio.run(test_app())
Server Management (scripts/with_server.py)
The with_server.py helper manages server lifecycle:
python scripts/with_server.py --help
python scripts/with_server.py \
--server "npm run dev" \
--port 5173 \
-- python my_test.py
python scripts/with_server.py \
--server "npm run dev" --port 5173 \
--server "python api.py" --port 8000 \
-- python my_test.py
Treat bundled scripts as opaque tools — use --help rather than reading source.
Best Practices
Always Wait for Load
await page.wait_for_load_state('networkidle')
await page.wait_for_selector('.main-content')
Descriptive Selectors
await page.click('button[role="submit"]')
await page.click('text=Submit')
await page.click('.submit-button')
await page.click('div:nth-child(3) > button')
Always Close Browser
try:
pass
finally:
await browser.close()
Common Operations
Click and Navigate
await page.click('a[href="/dashboard"]')
await page.wait_for_url('**/dashboard')
await page.wait_for_load_state('networkidle')
Fill Forms
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'password123')
await page.click('button[type="submit"]')
Extract Content
text = await page.inner_text('h1')
items = await page.query_selector_all('.list-item')
texts = [await item.inner_text() for item in items]
Console Logging
page.on("console", lambda msg: print(f"Console: {msg.text}"))
Check Visibility
is_visible = await page.is_visible('.error-message')
if is_visible:
error = await page.inner_text('.error-message')
print(f"Error: {error}")
Static HTML Testing
For static files, just read them directly — no server needed:
from pathlib import Path
from html.parser import HTMLParser
content = Path("index.html").read_text()
Or use Playwright with file:// URL:
await page.goto(f"file://{Path('index.html').absolute()}")