Skip to main content

Overview

The Web App Testing skill provides a comprehensive toolkit for testing local web applications using Playwright. It enables you to verify frontend functionality, debug UI behavior, capture browser screenshots, and analyze browser console logs.

When to Use This Skill

Use the Web App Testing skill when you need to:
  • Test local web applications with automated browser interactions
  • Verify frontend functionality and UI behavior
  • Debug rendering issues or JavaScript errors
  • Capture screenshots for visual verification
  • Analyze browser console logs and network activity
  • Test both static HTML and dynamic web applications

Core Approach

Write native Python Playwright scripts for all testing tasks. The skill provides helper scripts that manage complex workflows as black boxes.
Always run scripts with --help first to see usage before reading source code. These scripts can be very large and will pollute your context window. They’re designed to be called directly, not ingested.

Decision Tree: Choosing Your Approach

Helper Scripts

Server Management: with_server.py

Manages server lifecycle for testing dynamic applications. Supports multiple concurrent servers. Single server example:
Multiple servers example (backend + frontend):
The script:
  1. Starts all specified servers in the background
  2. Waits for each port to become available (health check)
  3. Runs your automation script
  4. Automatically cleans up and stops servers when done
Your automation script only needs to contain Playwright logic—no server management code required.

Writing Automation Scripts

Basic Structure

Critical: Always use headless=True for Chromium. When using with_server.py, servers are already running and ready—no startup code needed.

Reconnaissance-Then-Action Pattern

For dynamic applications, follow this three-step pattern:
1

Inspect Rendered DOM

2

Identify Selectors

Based on inspection results, identify the selectors you need:
  • Text-based: text=Submit
  • Role-based: role=button[name="Submit"]
  • CSS selectors: .submit-btn, #submit-button
  • IDs: id=submit-btn
3

Execute Actions

Common Patterns

Static HTML Testing

Element Discovery

Capturing Console Logs

Waiting Strategies

Best Practices

Before writing custom server management code:
  1. Check if a script exists in scripts/
  2. Run it with --help to see usage
  3. Invoke it directly without reading the source
This prevents context pollution from large utility scripts.
Don’t inspect the DOM before waiting for networkidle on dynamic apps:Don’t do this:
Do this:
Prefer selectors that are resilient to changes:Good selectors:
  • text=Submit - Based on visible text
  • role=button[name="Submit"] - Based on accessibility role
  • [data-testid="submit-btn"] - Based on test IDs
Fragile selectors:
  • .MuiButton-root-123 - Generated class names
  • div > div > button:nth-child(3) - Positional selectors
The skill examples use sync_playwright() for synchronous scripts, which are simpler for most testing scenarios.For asynchronous testing (e.g., with async/await patterns), use async_playwright():

Common Pitfalls

Common Mistake: Inspecting DOM before JavaScript finishes executingDynamic web apps often render content via JavaScript after initial page load. Always wait for networkidle before inspecting or interacting with the page.

Pitfall: Race Conditions

Pitfall: Not Closing Browser

Reference Files

The skill includes example files in the examples/ directory:
  • 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

Testing Checklist

Before running your automation:
  • Determined if the app is static or dynamic
  • Identified if server management is needed
  • Ran helper scripts with --help if needed
  • Set headless=True for Chromium
  • Added wait_for_load_state('networkidle') for dynamic apps
  • Used descriptive, resilient selectors
  • Added appropriate waits for elements
  • Included browser.close() for cleanup
  • Captured console logs if debugging