搜索

PythonReactSEOPDF
GitHub Skill

webapp-testing

Patterns for testing local web applications using Playwright. Use when verifying frontend functionality, debugging UI behavior, capturing screenshots, or testing dynamic web apps. Provides decision tree for choosing approach, reconnaissance-then-action pattern, and multi-server testing.

5dlabs☆ 1 仓库 Stars0 仓库 Forks

安装与使用

请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。

查看来源 ↗使用指南

技能说明

Web Application Testing

Test local web applications using Playwright with a systematic approach.

Functional vs Visual Testing

CRITICAL: Choose the right approach based on what you're verifying.

Testing Type Method Returns Use For
Functional Accessibility tree / DOM inspection Text (parseable) Button exists, text appears, form works, element state
Visual Screenshot Image (not parseable) Layout, colors, styling, animations, visual regression
Functional Testing (Checking Behavior)

Use DOM inspection or accessibility tree queries when verifying behavior:

# Get all interactive elements
buttons = page.locator('button').all()
for btn in buttons:
    print(btn.text_content())  # Agent CAN read and verify this

# Verify element exists and has correct state
assert page.locator('text=Submit').is_visible()
assert page.locator('#email').input_value() == 'test@example.com'

For MCP browser tools: Use take_snapshot which returns the accessibility tree as text.

Visual Testing (Checking Appearance)

Use screenshots ONLY when verifying appearance:

# Capture for visual comparison
page.screenshot(path='dashboard.png', full_page=True)

For MCP browser tools: Use take_screenshot when checking layout, colors, or styling.

Common Mistake
# BAD: Taking screenshot to verify button exists
page.screenshot(path='check.png')  # Agent cannot "read" this image!

# GOOD: Use DOM/accessibility to verify button exists
assert page.locator('text=Submit').is_visible()

Decision Tree: Choose Your Approach

User task → Is it static HTML?
    │
    ├─ Yes → Read HTML file directly to identify selectors
    │         └─ Write Playwright script using discovered selectors
    │
    └─ No (dynamic webapp) → Is the server already running?
            │
            ├─ No → Start server first, then test
            │       (use process management or test framework)
            │
            └─ Yes → Use Reconnaissance-Then-Action pattern:
                    1. Navigate and wait for networkidle
                    2. Take screenshot or inspect DOM
                    3. Identify selectors from rendered state
                    4. Execute actions with discovered selectors

Reconnaissance-Then-Action Pattern

For dynamic apps, always inspect before acting:

Step 1: Navigate and Wait
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:5173')
    
    # CRITICAL: Wait for JS to execute
    page.wait_for_load_state('networkidle')
Step 2: Inspect Rendered DOM
# Option A: Screenshot for visual inspection
page.screenshot(path='/tmp/inspect.png', full_page=True)

# Option B: Get page content
content = page.content()

# Option C: Find specific elements
buttons = page.locator('button').all()
for btn in buttons:
    print(btn.text_content())
Step 3: Identify Selectors

From inspection results, determine the right selectors:

Selector Type Example When to Use
Text text=Submit Visible button/link text
Role role=button[name="Submit"] Accessibility-friendly
CSS #submit-btn, .primary-action Unique IDs or classes
Data attributes [data-testid="submit"] Test-specific attributes
Step 4: Execute Actions
# Now interact with discovered selectors
page.locator('text=Submit').click()
page.locator('#email').fill('test@example.com')
page.locator('form').press('Enter')

Common Pitfall

❌ Don't inspect DOM before waiting for networkidle on dynamic apps ✅ Do wait for page.wait_for_load_state('networkidle') before inspection

Without this wait, you'll see the initial HTML before JavaScript renders the actual UI.

Multi-Server Testing

When testing apps with separate frontend and backend:

import subprocess
import time

# Start backend
backend = subprocess.Popen(['python', 'server.py'], cwd='backend/')

# Start frontend  
frontend = subprocess.Popen(['npm', 'run', 'dev'], cwd='frontend/')

# Wait for servers to be ready
time.sleep(5)  # Or use health check polling

try:
    # Run your tests
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.goto('http://localhost:5173')
        # ... test logic
finally:
    backend.terminate()
    frontend.terminate()

Testing Patterns

Form Testing
# Fill form
page.locator('#name').fill('Test User')
page.locator('#email').fill('test@example.com')
page.locator('select#country').select_option('US')
page.locator('input[type="checkbox"]').check()

# Submit and verify
page.locator('button[type="submit"]').click()
page.wait_for_selector('.success-message')
assert page.locator('.success-message').is_visible()
Navigation Testing
# Click link and verify navigation
page.locator('text=About').click()
page.wait_for_url('**/about')
assert 'About' in page.title()
API Response Testing
# Intercept network requests
with page.expect_response('**/api/users') as response_info:
    page.locator('button.load-users').click()

response = response_info.value
assert response.status == 200
data = response.json()
assert len(data['users']) > 0
Visual Regression
# Compare screenshots
page.goto('http://localhost:5173/dashboard')
page.wait_for_load_state('networkidle')

# Take screenshot for comparison
page.screenshot(path='dashboard-current.png', full_page=True)

# Compare with baseline (use image diff tool)

Console & Error Monitoring

# Capture console messages
console_messages = []
page.on('console', lambda msg: console_messages.append(msg.text))

# Capture errors
errors = []
page.on('pageerror', lambda err: errors.append(str(err)))

# Run test
page.goto('http://localhost:5173')
page.wait_for_load_state('networkidle')

# Check for issues
assert len(errors) == 0, f"Page errors: {errors}"
assert not any('error' in msg.lower() for msg in console_messages)

Wait Strategies

Method Use When
wait_for_load_state('networkidle') Initial page load, SPA navigation
wait_for_selector('.element') Waiting for specific element to appear
wait_for_url('**/path') After navigation actions
wait_for_response('**/api/**') After triggering API calls
wait_for_timeout(1000) Last resort for timing-dependent UIs

Best Practices

  1. Always launch headless: browser = p.chromium.launch(headless=True)
  2. Always close browser: Use with context or explicit browser.close()
  3. Use descriptive selectors: Prefer text=, role=, or data-testid= over fragile CSS
  4. Add appropriate waits: Don't assume elements are immediately available
  5. Capture evidence: Screenshot on failures for debugging
  6. Isolate tests: Each test should set up its own state

Debugging Tips

# Slow down for debugging
browser = p.chromium.launch(headless=False, slow_mo=500)

# Pause for manual inspection
page.pause()

# Get element state
element = page.locator('#my-button')
print(f"Visible: {element.is_visible()}")
print(f"Enabled: {element.is_enabled()}")
print(f"Text: {element.text_content()}")

使用示例

查看来源仓库中的使用示例与配置说明。

查看来源 ↗

来源历史

收录时间
2026-01-20
索引更新时间
2026-03-03
Branch
main

收录与同步日期属于本站记录。源码修改历史请查看来源仓库。

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!