webapp-testing
SKILL.md
Web Application Testing Skill
Version: 1.1.0 Category: Development Last Updated: 2026-01-02
Overview
This toolkit enables testing local web applications using Playwright with Python for frontend verification, UI debugging, screenshot capture, and browser automation.
Quick Start
# Install dependencies
pip install playwright pytest requests
playwright install chromium
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("http://localhost:3000")
# Take screenshot
page.screenshot(path="screenshot.png")
# Get page content
content = page.content()
print(content)
browser.close()
When to Use
- Verifying frontend functionality after code changes
- Debugging UI behavior and layout issues
- Capturing screenshots for documentation
- Viewing browser console logs for errors
- Automating repetitive web interactions
- End-to-end testing of web applications
- Validating form submissions and user flows
Decision Tree
Static HTML
- Read files directly to find selectors
- Then automate with Playwright
Dynamic Webapps
- Check if server runs
- If not, start with helper script
- Perform reconnaissance (screenshots/DOM)
- Then automate
Running Servers
- Perform reconnaissance first
- Take screenshots
- Inspect DOM
- Then automate
Core Patterns
Wait for Dynamic Content
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("http://localhost:3000")
# CRITICAL: Wait for JS to execute
page.wait_for_load_state("networkidle")
# Now safe to interact
content = page.content()
browser.close()
Element Selection
By Text:
page.get_by_text("Submit").click()
page.get_by_text("Welcome", exact=False).wait_for()
By Role:
page.get_by_role("button", name="Submit").click()
page.get_by_role("textbox", name="Email").fill("test@example.com")
page.get_by_role("link", name="Home").click()
By CSS Selector:
page.locator(".btn-primary").click()
page.locator("#email-input").fill("test@example.com")
page.locator("[data-testid='submit']").click()
By ID:
page.locator("#submit-button").click()
Form Interaction
# Fill form
page.locator("#username").fill("testuser")
page.locator("#password").fill("password123")
page.locator("#remember").check()
page.get_by_role("button", name="Login").click()
# Wait for navigation
page.wait_for_url("**/dashboard")
Screenshots
# Full page
page.screenshot(path="full.png", full_page=True)
# Element only
page.locator(".main-content").screenshot(path="element.png")
# Viewport only
page.screenshot(path="viewport.png")
Console Logs
# Capture console output
console_messages = []
def handle_console(msg):
console_messages.append({
"type": msg.type,
"text": msg.text
})
page.on("console", handle_console)
page.goto("http://localhost:3000")
# Print captured logs
for msg in console_messages:
print(f"[{msg['type']}] {msg['text']}")
Network Monitoring
# Capture requests
requests = []
def handle_request(request):
requests.append({
"url": request.url,
"method": request.method
})
page.on("request", handle_request)
page.goto("http://localhost:3000")
# Print captured requests
for req in requests:
print(f"{req['method']} {req['url']}")
Server Management
Start Server Before Testing
import subprocess
import time
from playwright.sync_api import sync_playwright
# Start server
server = subprocess.Popen(
["npm", "run", "dev"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# Wait for server to start
time.sleep(3)
try:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("http://localhost:3000")
# ... test code ...
browser.close()
finally:
server.terminate()
Check Server Status
import requests
def is_server_running(url="http://localhost:3000"):
try:
response = requests.get(url, timeout=2)
return response.status_code == 200
except:
return False
Testing Patterns
Assertion Examples
from playwright.sync_api import expect
# Text content
expect(page.locator("h1")).to_have_text("Welcome")
# Visibility
expect(page.locator(".modal")).to_be_visible()
expect(page.locator(".loading")).to_be_hidden()
# Input values
expect(page.locator("#email")).to_have_value("test@example.com")
# Count
expect(page.locator(".item")).to_have_count(5)
# URL
expect(page).to_have_url("http://localhost:3000/dashboard")
Test Structure
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture(scope="session")
def browser():
with sync_playwright() as p:
browser = p.chromium.launch()
yield browser
browser.close()
@pytest.fixture
def page(browser):
page = browser.new_page()
yield page
page.close()
def test_homepage(page):
page.goto("http://localhost:3000")
assert page.title() == "My App"
def test_login(page):
page.goto("http://localhost:3000/login")
page.fill("#username", "testuser")
page.fill("#password", "password")
page.click("button[type='submit']")
page.wait_for_url("**/dashboard")
Usage Examples
Example 1: Screenshot Comparison Testing
from playwright.sync_api import sync_playwright
from pathlib import Path
def capture_page_state(url: str, output_dir: str):
"""Capture full page screenshot and HTML content."""
output = Path(output_dir)
output.mkdir(parents=True, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url)
page.wait_for_load_state("networkidle")
# Screenshot
page.screenshot(path=str(output / "screenshot.png"), full_page=True)
# HTML content
(output / "content.html").write_text(page.content())
browser.close()
return output
# Usage
capture_page_state("http://localhost:3000", "tests/snapshots/")
Example 2: Form Automation
def submit_contact_form(page, name, email, message):
"""Submit a contact form and verify success."""
page.goto("http://localhost:3000/contact")
# Fill form fields
page.fill("[name='name']", name)
page.fill("[name='email']", email)
page.fill("[name='message']", message)
# Submit
page.click("button[type='submit']")
# Wait for success message
page.wait_for_selector(".success-message")
return page.locator(".success-message").text_content()
Example 3: API Response Verification
def verify_api_call(page, endpoint_pattern):
"""Intercept and verify API calls."""
api_response = None
def handle_response(response):
nonlocal api_response
if endpoint_pattern in response.url:
api_response = response.json()
page.on("response", handle_response)
page.goto("http://localhost:3000/dashboard")
page.wait_for_load_state("networkidle")
return api_response
Debugging
Slow Motion
browser = p.chromium.launch(slow_mo=500) # 500ms delay between actions
Headed Mode
browser = p.chromium.launch(headless=False) # See the browser
Pause Execution
page.pause() # Opens Playwright Inspector
Trace Recording
context = browser.new_context()
context.tracing.start(screenshots=True, snapshots=True)
page = context.new_page()
# ... test code ...
context.tracing.stop(path="trace.zip")
# View with: playwright show-trace trace.zip
Best Practices
Do
- Wait appropriately using
networkidlefor dynamic content - Select elements by text, role, or data-testid over CSS
- Handle async operations with proper waits
- Clean up browsers and pages after tests
- Use fixtures for browser instance reuse
- Capture console logs for debugging
Don't
- Use arbitrary
time.sleep()for waits - Rely on fragile CSS selectors
- Skip error handling in test cleanup
- Run tests without checking server status
- Ignore network errors during testing
Error Handling
Common Errors
| Error | Cause | Solution |
|---|---|---|
TimeoutError |
Element not found in time | Use explicit waits or increase timeout |
Page closed |
Browser closed prematurely | Check cleanup order in fixtures |
Connection refused |
Server not running | Start server before tests |
Element not visible |
Hidden by CSS/JS | Wait for visibility state |
Target closed |
Navigation during action | Wait for navigation to complete |
Debugging Tips
# Increase default timeout
page.set_default_timeout(60000) # 60 seconds
# Take screenshot on failure
try:
page.click("#submit")
except Exception as e:
page.screenshot(path="error_screenshot.png")
raise
# Print page HTML for debugging
print(page.content())
Execution Checklist
- Playwright and chromium installed
- Server running before tests
- Proper wait states for dynamic content
- Screenshots captured for visual verification
- Console logs monitored for errors
- Network requests validated
- Fixtures clean up browser resources
- Error screenshots captured on failure
- Tests run in both headed and headless modes
Metrics
| Metric | Target | Description |
|---|---|---|
| Test Duration | <10s per test | Individual test execution time |
| Flakiness Rate | <2% | Tests passing consistently |
| Screenshot Match | 100% | Visual regression accuracy |
| Network Coverage | >90% | API endpoints tested |
Dependencies
pip install playwright pytest requests
playwright install chromium
Related Skills
- engineering-report-generator - Generate test reports
- data-pipeline-processor - Process test data
- parallel-file-processor - Batch screenshot processing
Version History
- 1.1.0 (2026-01-02): Upgraded to SKILL_TEMPLATE_v2 format with Quick Start, Usage Examples, Error Handling, Metrics, Execution Checklist
- 1.0.0 (2024-10-15): Initial release with Playwright patterns, server management, debugging tools
Weekly Installs
10
Repository
vamseeachanta/workspace-hubFirst Seen
Jan 24, 2026
Security Audits
Installed on
claude-code9
trae8
antigravity8
codex8
windsurf8
gemini-cli8