How to Test Local Web Applications with Playwright Using Claude Skills
The Webapp-Testing skill from the ComposioHQ/awesome-claude-skills repository provides a two-layer architecture combining automated server lifecycle management with Playwright browser automation to test locally hosted web applications reliably.
The ComposioHQ/awesome-claude-skills repository includes a specialized Webapp-Testing skill designed to eliminate the friction of manually orchestrating development servers before running browser tests. This skill leverages a clean separation between server management and test logic, allowing you to test local web applications with Playwright using Claude Skills without complex setup scripts or manual process coordination.
Understanding the Webapp-Testing Skill Architecture
According to the source code in webapp-testing/SKILL.md, the skill implements a deliberate architectural split into two distinct layers that keep automation scripts focused and maintainable.
Server Management Layer
The Server Management Layer resides in webapp-testing/scripts/with_server.py. This helper script acts as a black-box orchestrator that handles the complete lifecycle of one or more development servers. It starts the server processes, polls until the configured ports become available, and then executes your Playwright automation script. This design ensures your test code assumes the server is already running, eliminating the need for complex startup logic within the automation script itself.
Playwright Automation Layer
The Playwright Automation Layer contains only browser interaction logic using the synchronous Playwright API. Because the server management is handled externally, these scripts remain compact and focused solely on navigation, DOM interaction, and assertions. The skill ships with multiple example scripts in webapp-testing/examples/ that demonstrate common patterns including DOM inspection, selector discovery, and console log capture.
Running Tests Against Local Development Servers
The with_server.py script supports both single and multi-server configurations, making it suitable for testing simple static sites or complex full-stack applications with separate backend and frontend processes.
Single Server Setup
For applications with a single development server, pass the server command and port to the helper script, followed by your Playwright automation file:
python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py
The script waits until port 5173 accepts connections before executing your_automation.py, ensuring the local web application is fully initialized before Playwright attempts to connect.
Multi-Server Backend and Frontend
For applications requiring multiple services (such as a backend API and a frontend dev server), specify multiple --server and --port pairs:
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
This pattern, as implemented in webapp-testing/scripts/with_server.py, starts both servers concurrently and waits for all specified ports to become available before launching the test suite.
Writing Reliable Playwright Automation Scripts
The skill enforces several architectural patterns to ensure test stability and reduce flakiness when testing local web applications.
The Reconnaissance-Then-Action Pattern
The webapp-testing/SKILL.md documentation emphasizes a Reconnaissance-Then-Action workflow for dynamic applications. First wait for networkidle, capture screenshots or DOM content, and then derive reliable selectors before performing actions. This pattern prevents selector instability that occurs when inspecting the DOM before the application has fully hydrated.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Navigate to the locally-served app
page.goto("http://localhost:5173")
page.wait_for_load_state("networkidle") # Critical for dynamic apps
# Action phase: interact with stable elements
page.click('text="Submit"')
page.screenshot(path="tmp/final.png", full_page=True)
browser.close()
Element Discovery and Selector Extraction
For complex applications where selectors are unknown, the skill provides webapp-testing/examples/element_discovery.py. This script demonstrates the discovery phase of the pattern, capturing screenshots and enumerating available elements before automation:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("http://localhost:5173")
page.wait_for_load_state("networkidle")
# Reconnaissance: capture visual state
page.screenshot(path="/tmp/inspect.png", full_page=True)
# Enumerate interactive elements
buttons = page.locator("button").all()
print("Found buttons:", [b.text_content() for b in buttons])
browser.close()
Key Architectural Patterns from the Source Code
Several design decisions in the ComposioHQ/awesome-claude-skills repository optimize the skill for LLM-assisted development and reliable test execution:
-
Black-box helper scripts: The
with_server.pyutility is designed to operate as a black box invoked via command line. Runpython scripts/with_server.py --helpto view options. This keeps the LLM context window focused on test logic rather than server management code. -
Synchronous Playwright API: All examples in
webapp-testing/examples/usesync_playwright()rather than the asynchronous API. This synchronous style simplifies script generation in the Claude environment and reduces complexity for single-threaded test scenarios. -
Decision tree workflow: The
SKILL.mdfile includes a decision tree that guides users from static HTML testing to dynamic application testing, explicitly recommending when to use the server helper versus pure Playwright scripts. -
Best-practice enforcement: The skill enforces headless Chromium launches, explicit waits using
wait_for_load_state('networkidle')andwait_for_selector(), and mandatory browser cleanup viabrowser.close()to prevent resource leaks.
Summary
- The Webapp-Testing skill uses a two-layer architecture separating server lifecycle management from browser automation logic.
- The
with_server.pyhelper handles single or multi-server startup, waiting for ports to become available before executing Playwright scripts. - All examples use the synchronous Playwright API (
sync_playwright()) for compatibility with Claude-generated code. - The Reconnaissance-Then-Action pattern requires waiting for
networkidlebefore inspecting DOM elements to ensure selector stability. - Example scripts in
webapp-testing/examples/demonstrate element discovery, console logging, and static HTML automation patterns.
Frequently Asked Questions
How do I test a local web application with Playwright if it requires a running development server?
Use the with_server.py helper script from the Webapp-Testing skill. Execute your test command prefixed with the helper that starts your dev server: python scripts/with_server.py --server "npm run dev" --port 5173 -- python test_script.py. The helper manages the server lifecycle automatically, ensuring the port is ready before your Playwright script runs.
Why does the skill use synchronous Playwright instead of async?
The skill intentionally uses sync_playwright() because synchronous code is easier to generate and debug within the Claude environment. According to webapp-testing/SKILL.md, the synchronous API eliminates the complexity of async/await syntax while providing identical browser control capabilities for most testing scenarios.
What is the Reconnaissance-Then-Action pattern in webapp testing?
This pattern requires waiting for the networkidle load state after navigation, then capturing screenshots or DOM snapshots before attempting to interact with elements. As documented in the skill's decision tree, this prevents flaky tests caused by attempting to click elements that haven't fully rendered or hydrated in dynamic JavaScript applications.
How do I discover reliable selectors for a local web application I didn't build?
Use the element_discovery.py example from webapp-testing/examples/. This script waits for networkidle, captures a full-page screenshot to /tmp/inspect.png, and programmatically lists all button elements with their text content. This reconnaissance phase allows you to identify stable text-based or structural selectors before writing your final automation script.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →