关于 webapp-testing
webapp-testing 是使用 Playwright 的 Python API 测试本地 Web 应用的技能。它为静态文件和运行中的应用提供流程,可检查交互、截图并查看控制台错误。
主要功能
- 01使用 Playwright Python 测试本地 Web 应用
- 02为静态或动态应用选择测试方式
- 03验证前端行为与交互
- 04截图并检查控制台错误
- 05生成自动化测试脚本
适用场景
- 01检查表单和导航流程
- 02调试 UI 渲染或行为
- 03截取截图用于视觉回归
- 04查找浏览器控制台中的 JavaScript 错误
MCP 相关
该技能介绍了通过 Puppeteer MCP 服务进行可选的浏览器自动化,并说明导航、截图、点击、输入表单、选择、悬停和页面求值等工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ oimiragieo/ agent-studio --skill webapp-testing安装后在 Claude Code 中输入/webapp-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
commands/webapp-testing.mdhooks/post-execute.cjspre-execute.cjsreferences/research-requirements.mdrules/webapp-testing.mdschemas/input.schema.jsonoutput.schema.jsonscripts/main.cjstemplates/implementation-template.md详细介绍
Web Application Testing Skill
改编自 Anthropic 官方技能仓库的 Web 应用测试工具包。提供使用 Python 版 Playwright 测试本地 Web 应用的系统化方法,涵盖前端验证、UI 调试、截图捕获和浏览器控制台日志分析。 - 使用 Playwright(Python)测试本地 Web 应用 - 验证前端功能 - 调试和检查 UI 行为 - 在关键交互点捕获浏览器截图 - 查看浏览器控制台日志并检测错误 - 测试静态 HTML 文件(直接读取 + 选择器识别) - 使用服务器生命周期管理测试动态 Web 应用 - 生成自动化测试脚本 - 发现元素并识别 CSS 选择器 - 管理页面加载状态(networkidle、domcontentloaded、load)概述
此技能将 Anthropic 的 webapp-testing 技能改编为 agent-studio 框架。它提供了一种在本地测试 Web 应用的系统化方法,并根据被测 Web 内容的类型选择合适的策略。
源代码仓库:https://github.com/anthropics/skills
许可证:MIT
工具:Playwright(Python API)
适用场景
- 验证 Web 应用的前端功能时
- 调试 UI 行为或视觉渲染问题时
- 捕获截图以进行视觉回归测试时
- 检查浏览器控制台中的 JavaScript 错误时
- 测试表单提交、导航流程和交互元素时
- 为 Web 应用生成自动化测试脚本时
铁律
NEVER INSPECT DOM BEFORE WAITING FOR NETWORKIDLE ON DYNAMIC APPS
动态 Web 应用会异步加载内容。在页面稳定之前检查 DOM,会导致结果不准确或不完整。在动态应用上检查渲染内容之前,务必调用 page.wait_for_load_state('networkidle')。
决策树:选择合适的方法
Is the target a static HTML file?
YES → Approach A: Direct Read
NO → Is there a running dev server?
YES → Approach B: Reconnaissance-then-Action
NO → Approach C: Helper Script First
方法 A:静态 HTML 文件
对于不需要服务器的静态 HTML 文件:
- 直接读取 HTML 文件
- 确定目标元素的 CSS 选择器
- 编写 Playwright 脚本打开文件并验证内容
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(f"file://{abs_path_to_html}")
# Verify content
title = page.title()
assert title == "Expected Title"
# Check element exists
element = page.query_selector("h1.main-heading")
assert element is not None
# Capture screenshot
page.screenshot(path="screenshot.png")
browser.close()
方法 B:运行中的服务器(先侦察,再操作)
当开发服务器已在运行时:
- 侦察:导航到应用并了解页面结构
- 等待稳定:
page.wait_for_load_state('networkidle') - 检查:查询元素、读取内容、检查控制台日志
- 操作:与表单、按钮和导航进行交互
- 验证:断言预期结果
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Capture console messages
console_messages = []
page.on("console", lambda msg: console_messages.append(
f"[{msg.type}] {msg.text}"
))
# Navigate and wait for stability
page.goto("http://localhost:3000")
page.wait_for_load_state("networkidle")
# Discover page structure
headings = page.query_selector_all("h1, h2, h3")
buttons = page.query_selector_all("button")
forms = page.query_selector_all("form")
links = page.query_selector_all("a[href]")
# Print discovered elements
for h in headings:
print(f"Heading: {h.text_content()}")
for btn in buttons:
print(f"Button: {btn.text_content()}")
# Screenshot before interaction
page.screenshot(path="before-interaction.png")
# Interact with form
page.fill("input[name='email']", "[email protected]")
page.fill("input[name='password']", "testpass123")
page.click("button[type='submit']")
page.wait_for_load_state("networkidle")
# Screenshot after interaction
page.screenshot(path="after-interaction.png")
# Check for console errors
errors = [m for m in console_messages if "[error]" in m.lower()]
if errors:
print(f"Console errors found: {errors}")
browser.close()
方法 C:没有运行中的服务器(辅助脚本)
当 Web 应用需要启动服务器时:
import subprocess
import time
from playwright.sync_api import sync_playwright
# Start the server
server_proc = subprocess.Popen(
["npm", "run", "dev"],
cwd="/path/to/project",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=False # Security: always shell=False
)
# Wait for server to be ready
time.sleep(5) # Or use a health check loop
try:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("http://localhost:3000")
page.wait_for_load_state("networkidle")
# Run tests...
page.screenshot(path="test-result.png")
browser.close()
finally:
server_proc.terminate()
server_proc.wait()
常见测试模式
模式 1:视觉回归检查
# Take baseline screenshot
page.screenshot(path="baseline.png", full_page=True)
# After changes, take comparison screenshot
page.screenshot(path="current.png", full_page=True)
# Compare (use image diff library)
模式 2:表单验证测试
# Test empty submission
page.click("button[type='submit']")
error_msgs = page.query_selector_all(".error-message")
assert len(error_msgs) > 0, "Expected validation errors for empty form"
# Test invalid email
page.fill("input[type='email']", "not-an-email")
page.click("button[type='submit']")
email_error = page.query_selector("input[type='email']:invalid")
assert email_error is not None
模式 3:导航流程测试
# Test navigation links
nav_links = page.query_selector_all("nav a")
for link in nav_links:
href = link.get_attribute("href")
link.click()
page.wait_for_load_state("networkidle")
assert page.url.endswith(href), f"Expected URL to end with {href}"
page.go_back()
page.wait_for_load_state("networkidle")
模式 4:响应式设计测试
viewports = [
{"width": 375, "height": 812, "name": "mobile"},
{"width": 768, "height": 1024, "name": "tablet"},
{"width": 1920, "height": 1080, "name": "desktop"},
]
for vp in viewports:
page.set_viewport_size({"width": vp["width"], "height": vp["height"]})
page.wait_for_load_state("networkidle")
page.screenshot(path=f"responsive-{vp['name']}.png")
关键注意事项
- 不要在 networkidle 之前检查 DOM:动态应用会异步加载内容。过早检查会导致结果不完整。
- 不要使用 shell=True:启动服务器进程时,出于安全考虑,务必使用
shell=False和数组参数。 - 不要硬编码等待时间:使用
page.wait_for_selector()或page.wait_for_load_state(),而不是time.sleep()。 - 不要忽略控制台错误:务必捕获并报告浏览器控制台错误——它们代表实际问题。
- 不要忘记清理:务必在
finally块中终止服务器进程。
前置条件
确保已安装 Playwright:
pip install playwright
playwright install chromium
与 Agent-Studio 集成
推荐工作流
- 使用
webapp-testing验证前端行为 - 将截图证据提供给
code-reviewer进行视觉审查 - 使用
tdd技能根据发现的模式生成测试套件 - 使用
accessibility技能验证 WCAG 合规性
互补技能
| 技能 | 关系 |
|---|---|
tdd | 根据 webapp-testing 的发现生成测试套件 |
accessibility | 功能测试后的 WCAG 合规性验证 |
frontend-expert | 为测试设计提供 UI/UX 模式指导 |
chrome-browser | 另一种浏览器自动化方式 |
test-generator | 根据测试模式生成测试代码 |
铁律
- 绝不在 NETWORKIDLE 之前检查 DOM — 动态 Web 应用会异步加载内容。在页面稳定之前检查 DOM,会产生错误或不完整的结果。
- 启动服务进程时绝不使用 shell=True — 始终使用
shell=False和数组参数(SE-01 安全要求)。 - 始终捕获控制台错误 — 浏览器控制台错误表明存在实际问题;绝不能在测试报告中忽略它们。
- 始终在 finally 块中终止服务进程 — 泄漏的服务进程会破坏后续测试运行并消耗资源。
- 绝不硬编码等待时间 — 使用
page.wait_for_selector()或page.wait_for_load_state(),而不是time.sleep()。
反模式
| 反模式 | 失败原因 | 正确做法 |
|---|---|---|
| 在 networkidle 之前检查 DOM | 动态内容尚未加载;断言会产生假阴性 | 检查前始终调用 page.wait_for_load_state('networkidle') |
使用 time.sleep() 等待 | 不稳定 — 在慢速机器上等待太短,在快速机器上等待太长 | 使用显式等待:wait_for_selector、wait_for_load_state |
| 忽略浏览器控制台错误 | 未检测到实际的 JS 错误;测试通过,但应用已损坏 | 每次测试运行都始终捕获并报告控制台错误 |
为服务进程使用 shell=True | 命令注入漏洞 | 始终使用 shell=False 和列表参数 |
| 不清理服务进程 | 后续运行时出现端口冲突和资源泄漏 | 使用 try/finally 确保调用 server_proc.terminate() |
Puppeteer MCP 浏览器自动化
如果要使用不依赖 Python、原生支持 Agent 的浏览器自动化,请使用来自 modelcontextprotocol/servers 的 Puppeteer MCP 服务:
设置
在 mcpServers 下添加到 .claude/settings.json:
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
}
MCP 工具参考
| 工具 | 用途 |
|---|---|
puppeteer_navigate | 导航到 URL 并等待页面加载 |
puppeteer_screenshot | 截取屏幕截图(整页或元素) |
puppeteer_click | 点击 CSS 选择器 |
puppeteer_fill | 用指定值填写表单输入项 |
puppeteer_select | 按值选择下拉选项 |
puppeteer_hover | 将鼠标悬停在元素上 |
puppeteer_evaluate | 在浏览器上下文中执行 JavaScript |
使用模式
// Navigate and capture state
mcp__puppeteer__puppeteer_navigate({ url: 'http://localhost:3000' });
mcp__puppeteer__puppeteer_screenshot({ name: 'initial-state', fullPage: true });
// Interact with forms
mcp__puppeteer__puppeteer_fill({ selector: 'input[name="email"]', value: '[email protected]' });
mcp__puppeteer__puppeteer_click({ selector: 'button[type="submit"]' });
mcp__puppeteer__puppeteer_screenshot({ name: 'after-submit' });
// Evaluate page state
mcp__puppeteer__puppeteer_evaluate({
script:
'JSON.stringify({ title: document.title, errors: [...document.querySelectorAll(".error")].map(e => e.textContent) })',
});
何时使用 Puppeteer MCP,何时使用 Playwright Python
| 场景 | 使用 |
|---|---|
| 在 Agent 工作流中快速验证页面 | Puppeteer MCP |
| 包含断言的复杂测试套件 | Playwright Python |
| 截取截图作为证据 | Puppeteer MCP |
| 表单交互和导航 | 两者均可 |
| CI 测试自动化 | Playwright Python |
| 嵌入 Agent 的浏览器检查 | Puppeteer MCP |
Memory Protocol(强制要求)
开始前:
阅读 .claude/context/memory/learnings.md
检查以下内容:
- 项目中现有的测试脚本或 Playwright 配置
- 先前会话中已知的页面选择器
- 先前发现的控制台错误或不稳定的测试模式
完成后:
- 发现的测试模式 ->
.claude/context/memory/learnings.md - 测试不稳定或浏览器问题 ->
.claude/context/memory/issues.md - 关于测试策略的决策 ->
.claude/context/memory/decisions.md
假设任务可能中断:如果没有记录在记忆中,就说明它没有发生。