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
undefinedpython
undefinedTake 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())
undefinedbuttons = page.locator('button').all()
for btn in buttons:
print(btn.inner_text())
undefined2. Identify Selectors
2. 定位选择器
python
undefinedpython
undefinedFind 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()
undefinedpage.locator('#submit-btn').click()
page.locator('.primary-action').click()
undefined3. Execute Actions
3. 执行操作
python
undefinedpython
undefinedClick
点击操作
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()
undefinedpage.wait_for_selector('.success-message')
assert page.locator('.success-message').is_visible()
undefinedCommon Pitfall
常见误区
python
undefinedpython
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
undefinedpage.goto('http://localhost:5173')
page.wait_for_load_state('networkidle')
content = page.content() # 现在内容完整
undefinedCapture 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_playwrightpython
import subprocess
import time
from playwright.sync_api import sync_playwrightStart 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()
undefinedserver = 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()
undefinedBest Practices
最佳实践
- Always use headless mode:
browser = p.chromium.launch(headless=True) - Always wait for networkidle:
page.wait_for_load_state('networkidle') - Always close browser: Use statement or explicit
withbrowser.close() - Use descriptive selectors: ,
text=, CSS selectors, or IDsrole= - Add appropriate waits: or
page.wait_for_selector()page.wait_for_timeout()
- 始终使用无头模式:
browser = p.chromium.launch(headless=True) - 始终等待networkidle状态:
page.wait_for_load_state('networkidle') - 始终关闭浏览器:使用语句或显式调用
withbrowser.close() - 使用描述性选择器:、
text=、CSS选择器或IDrole= - 添加适当的等待:或
page.wait_for_selector()page.wait_for_timeout()
Required Packages
所需依赖包
bash
pip install playwright
playwright install chromiumbash
pip install playwright
playwright install chromium