webapp-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- Adapted from: awesome-claude-skills/webapp-testing -->
<!-- 改编自: awesome-claude-skills/webapp-testing -->

Web Application Testing with Playwright

使用Playwright进行Web应用测试

Test local web applications using Python Playwright scripts.
使用Python Playwright脚本测试本地Web应用。

Decision Tree

决策树

Is it static HTML?
├─ Yes → Read HTML file directly to identify selectors
│         ├─ Success → Write Playwright script using selectors
│         └─ Fails → Treat as dynamic (below)
└─ No (dynamic webapp) → Is the server already running?
    ├─ No → Start server, then test
    └─ Yes → Reconnaissance-then-action:
        1. Navigate and wait for networkidle
        2. Take screenshot or inspect DOM
        3. Identify selectors from rendered state
        4. Execute actions with discovered selectors
是否为静态HTML?
├─ 是 → 直接读取HTML文件以定位选择器
│         ├─ 成功 → 使用选择器编写Playwright脚本
│         └─ 失败 → 按动态应用处理(见下方)
└─ 否(动态Web应用) → 服务器是否已启动?
    ├─ 否 → 启动服务器后再进行测试
    └─ 是 → 先侦察再执行:
        1. 导航至页面并等待networkidle状态
        2. 截取屏幕截图或检查DOM
        3. 从渲染状态中定位选择器
        4. 使用找到的选择器执行操作

Basic Playwright Script

基础Playwright脚本

python
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')
    page.wait_for_load_state('networkidle')  # CRITICAL: Wait for JS

    # Your automation logic here
    page.screenshot(path='screenshot.png')

    browser.close()
python
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')
    page.wait_for_load_state('networkidle')  # 关键:等待JS加载完成

    # 在此处编写你的自动化逻辑
    page.screenshot(path='screenshot.png')

    browser.close()

Reconnaissance-Then-Action Pattern

侦察-执行模式

1. Inspect Rendered DOM

1. 检查渲染后的DOM

python
undefined
python
undefined

Take screenshot for visual inspection

截取全屏截图用于视觉检查

page.screenshot(path='/tmp/inspect.png', full_page=True)
page.screenshot(path='/tmp/inspect.png', full_page=True)

Get page content

获取页面内容

content = page.content()
content = page.content()

Find all buttons

查找所有按钮

buttons = page.locator('button').all() for btn in buttons: print(btn.inner_text())
undefined
buttons = page.locator('button').all() for btn in buttons: print(btn.inner_text())
undefined

2. Identify Selectors

2. 定位选择器

python
undefined
python
undefined

Find elements by text

通过文本查找元素

page.locator('text=Submit').click()
page.locator('text=Submit').click()

Find by role

通过角色查找

page.locator('role=button[name="Save"]').click()
page.locator('role=button[name="Save"]').click()

Find by CSS

通过CSS选择器查找

page.locator('#submit-btn').click() page.locator('.primary-action').click()
undefined
page.locator('#submit-btn').click() page.locator('.primary-action').click()
undefined

3. Execute Actions

3. 执行操作

python
undefined
python
undefined

Click

点击操作

page.locator('button:has-text("Login")').click()
page.locator('button:has-text("Login")').click()

Fill form

填写表单

page.locator('input[name="email"]').fill('user@example.com') page.locator('input[name="password"]').fill('password123')
page.locator('input[name="email"]').fill('user@example.com') page.locator('input[name="password"]').fill('password123')

Wait and verify

等待并验证

page.wait_for_selector('.success-message') assert page.locator('.success-message').is_visible()
undefined
page.wait_for_selector('.success-message') assert page.locator('.success-message').is_visible()
undefined

Common Pitfall

常见误区

python
undefined
python
undefined

❌ WRONG: Inspecting before page loads

❌ 错误:页面加载前就检查内容

page.goto('http://localhost:5173') content = page.content() # May be incomplete!
page.goto('http://localhost:5173') content = page.content() # 内容可能不完整!

✅ CORRECT: Wait for network idle first

✅ 正确:先等待networkidle状态

page.goto('http://localhost:5173') page.wait_for_load_state('networkidle') content = page.content() # Now complete
undefined
page.goto('http://localhost:5173') page.wait_for_load_state('networkidle') content = page.content() # 现在内容完整
undefined

Capture Console Logs

捕获控制台日志

python
from playwright.sync_api import sync_playwright

def handle_console(msg):
    print(f"Console {msg.type}: {msg.text}")

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.on('console', handle_console)
    page.goto('http://localhost:5173')
    page.wait_for_load_state('networkidle')
    browser.close()
python
from playwright.sync_api import sync_playwright

def handle_console(msg):
    print(f"控制台 {msg.type}: {msg.text}")

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.on('console', handle_console)
    page.goto('http://localhost:5173')
    page.wait_for_load_state('networkidle')
    browser.close()

Testing with Server Management

结合服务器管理进行测试

python
import subprocess
import time
from playwright.sync_api import sync_playwright
python
import subprocess
import time
from playwright.sync_api import sync_playwright

Start server

启动服务器

server = subprocess.Popen(['npm', 'run', 'dev'], cwd='./frontend') time.sleep(5) # Wait for server to start
try: with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto('http://localhost:5173') page.wait_for_load_state('networkidle') # ... tests ... browser.close() finally: server.terminate()
undefined
server = subprocess.Popen(['npm', 'run', 'dev'], cwd='./frontend') time.sleep(5) # 等待服务器启动
try: with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto('http://localhost:5173') page.wait_for_load_state('networkidle') # ... 测试逻辑 ... browser.close() finally: server.terminate()
undefined

Best Practices

最佳实践

  1. Always use headless mode:
    browser = p.chromium.launch(headless=True)
  2. Always wait for networkidle:
    page.wait_for_load_state('networkidle')
  3. Always close browser: Use
    with
    statement or explicit
    browser.close()
  4. Use descriptive selectors:
    text=
    ,
    role=
    , CSS selectors, or IDs
  5. Add appropriate waits:
    page.wait_for_selector()
    or
    page.wait_for_timeout()
  1. 始终使用无头模式
    browser = p.chromium.launch(headless=True)
  2. 始终等待networkidle状态
    page.wait_for_load_state('networkidle')
  3. 始终关闭浏览器:使用
    with
    语句或显式调用
    browser.close()
  4. 使用描述性选择器
    text=
    role=
    、CSS选择器或ID
  5. 添加适当的等待
    page.wait_for_selector()
    page.wait_for_timeout()

Required Packages

所需依赖包

bash
pip install playwright
playwright install chromium
bash
pip install playwright
playwright install chromium