关于 browser-qa
面向 Web 界面和 API 流程测试的浏览器 QA skill,通过真实浏览器执行步骤并收集截图、录屏及可用的网络和控制台证据。它按工具可用性选择 T3、Playwright MCP 或 agent-browser,并要求明确报告缺失证据和失败步骤。
主要功能
- 01按优先级选择浏览器驱动
- 02每次交互前重新检查页面
- 03逐步保存截图并记录测试流程
- 04检查 API 请求与控制台错误
- 05执行变更预检并生成结构化结果
适用场景
- 01验证 Web 界面的用户操作流程
- 02用测试矩阵检查 API 端点行为
- 03为变更流程收集截图和录屏证据
MCP 相关
可通过 Playwright MCP 的浏览器工具驱动测试;若该工具不可用,则使用 T3 预览工具或 agent-browser CLI。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bhagyamudgal/ skills --skill browser-qa安装后在 Claude Code 中输入/browser-qa确认已加载。
详细介绍
我会通过完整流程操作真实浏览器,并且只有在亲眼看到流程正常运行后才会判定完成。每一步都会截取屏幕截图,完整运行过程会录制视频,会对触发 API 的操作进行网络检查,并检查控制台是否出现新错误。
驱动程序优先级
- 如果工具列表中有
t3-code_preview_snapshot,我只使用 T3 预览工具。先用t3-code_preview_open打开页面,再用t3-code_preview_navigate前往 URL,然后循环调用t3-code_preview_snapshot,再调用t3-code_preview_click或t3-code_preview_type。每次交互前都会重新截取屏幕快照,因为引用可能会过期。我会保持open=true,让用户看到运行过程。 - 否则,如果有 Playwright MCP 的
browser_navigate,我会原样使用 Playwright 流程。 - 否则,我会使用
agent-browserCLI(open、snapshot -i、click @eN、fill @eN)。对于 CLI 无法提供的证据,我会报告为缺失,绝不擅自假定。
Playwright 到 T3 的映射:browser_navigate 对应 t3-code_preview_navigate,browser_snapshot 对应 t3-code_preview_snapshot,browser_click 对应 t3-code_preview_click,browser_type 对应 t3-code_preview_type,browser_press 对应 t3-code_preview_press,browser_wait_for 对应 t3-code_preview_wait_for。屏幕截图通过设置 save=true 调用 t3-code_preview_snapshot 获取,该调用会返回一个 screenshotPath,需要将其移入 .qa/。
录制
我会录制每次测试流程运行,而不只是截取屏幕截图。在 T3 上,我会在第一步之前调用 t3-code_preview_recording_start,并在最后一步之后调用 t3-code_preview_recording_stop,以保存本地证据文件。使用 agent-browser CLI 时,我会用 record start .qa/<flow-name>.webm 和 record stop 标记录制的开始和结束。Playwright MCP 没有视频工具,因此此时会以屏幕截图序列作为记录,并报告视频不可用,绝不伪造视频。
录制分辨率至少为 1920x1080;对于可设置帧率的驱动程序,帧率至少设为 30 fps。在 T3 上,我会在开始录制前使用 t3-code_preview_resize 将视口设为不小于该尺寸。CLI 的 record start 会以自己的尺寸打开全新上下文,因此之前设置的视口不会保留:执行 record stop 后,我会检查文件尺寸;如果低于最低要求,我会如实报告,而不会声称符合要求。两种驱动程序都无法设置帧率,因此我会使用录制器的默认值,并在文件生成后报告该值。
输入
- 自然语言流程,例如“创建一个包含 3 种食材的食谱,并验证营养数据是否计算正确”。
- URL 加操作说明,例如
http://localhost:3000/orders加上“点击 New Order,填写供应商并提交”。 - API 端点加测试矩阵,例如
GET /v1/shop/:clientId/patients-order-overview加上“验证标签筛选、日期筛选、分页、错误处理和数量一致性”。此时,我会直接测试端点,使用 curl 或在已认证会话中发起页面内请求,逐项汇总测试用例,然后生成下方的 QA 测试结果报告。
未提供 URL 时,我默认使用 http://localhost:3000。
UI 更改刚完成但未指定流程时,我会询问要测试哪个流程,例如“测试 localhost:3000/orders 中的订单创建”。
步骤 1:准备
mkdir -p .qa
[ -s .gitignore ] && [ -n "$(tail -c1 .gitignore)" ] && printf '\n' >> .gitignore
grep -qxF '.qa/' .gitignore 2>/dev/null || printf '.qa/\n' >> .gitignore
我会使用 curl -s -o /dev/null -w "%{http_code}" <url> 检查开发服务器是否正在运行。如果无法访问,我会请用户启动服务器。
第 2 步:身份验证检查
- 我使用上述优先级中选定的驱动程序打开 URL,并对页面进行快照。
- 我会查找登录表单指示,例如
input[type=password]、URL 中的/login,或与身份验证相关的文本。 - 找到这些指示时,说明当前会话尚未登录。我会让用户在浏览器中手动登录,然后说“完成”。之后我会再次检查。
- 没找到任何指示并不能证明什么。未登录的页面也可能在没有可见表单的情况下呈现。我会先确认登录后的标记,例如账户名称、注销或个人资料控件,或用户专属内容。只有确认存在标记后,我才会继续执行测试。若没有标记,我会将会话视为未知,并请用户登录并确认。
第 3 步:执行测试流程
我会将用户的流程解析为编号步骤,并启动一个子 Agent。子 Agent 使用我在第 2 步中选定的驱动程序,并遵循上述驱动程序优先级。
在 T3 中,每次调用 Playwright 时都会按上述映射进行转换,然后运行下方的提示词模板。T3 的网络和控制台证据来自子 Agent 安装的收集器:在第一步之前安装,并在每次导航后重新安装;在安装后建立基线,然后在每次触发 API 的操作后读取数据。如果页面阻止安装收集器,则报告该证据类型不可用,绝不伪造。在 Playwright MCP 上,模板保持不变。在 agent-browser CLI 回退方案中,browser_navigate 转换为 open,browser_snapshot 转换为 snapshot -i,交互操作转换为 click @eN 和 fill @eN "text",截图、网络和控制台证据则使用 agent-browser --help 中最接近的 agent-browser 对应功能。证据标准不会降低。每个步骤仍然都需要截图;CLI 无法生成的证据必须报告为缺失,绝不能擅自假定其存在。
变更预检(主 Agent,在派发之前)
我会在子 Agent 创建之前完成此步骤。派发的子 Agent 无法与用户沟通,因此,如果在子 Agent 内部得出 confirmation-required 结论,流程就会卡住:浏览器仍开着,却没人能回应。
当 URL 看起来像生产环境,或任何已解析步骤会修改共享数据时,我会在此处调用 preflight-mutations。我会提供准确的环境 URL、已通过身份验证的账户和工作区、操作和记录 ID、所有权、测试前的记录快照、还原或补偿步骤,以及用户授权。我会在主 Agent 中应用其结果契约。若结果为 confirmation-required,我会向用户展示确认卡片,并在派发之前取得用户决定。若结果为 blocked,我会报告其 后续操作,且不派发任何内容。只有 ready 卡片才会作为 {ready card} 插入下方的提示词中。
仅涉及一次性数据的本地流程无需使用此门控。我会将 not-applicable, local flow, disposable data only 作为 {ready card} 传入。
子 Agent 提示词模板
使用 {driver: T3 preview tools | Playwright MCP tools | agent-browser CLI},在 {URL} 执行以下 QA 测试流程。认证检查已在浏览器会话中打开 {URL}。
测试步骤。{用户流程描述中的编号步骤}。
变更授权。{ready card}。
该卡片已获授权。不要自行调用
preflight-mutations,因为你无法回答它可能提出的问题。对于每次共享状态交互,在写入前立即重新读取并比较目标当前的 guards。只要它们匹配,就按该卡片继续操作。若 guard 发生变化,请停止待执行的交互,并将尚未执行的剩余操作交还主 Agent 重新预检,而不是写入。写入后,执行卡片规定的权威回读,根据观察到的状态更新 guards,并将该项记录为landed、failed或reconcile-required。结果不明确时,记录为reconcile-required。停止该项操作,并报告以便根据权威状态解决。绝不要自行重试。对于每一步,先获取快照(T3 使用
t3-code_preview_snapshot,Playwright 使用browser_snapshot,CLI 使用snapshot -i)。页面一旦变化,Refs 就会立即失效,因此每次交互前都要重新获取快照。使用同一驱动执行交互。将证据保存为截图文件.qa/<NN>-<step-name>.png:在 T3 上调用t3-code_preview_snapshot并设置save=true,然后将返回的screenshotPath移至该路径;在 Playwright 上调用browser_take_screenshot并使用该文件名;在 CLI 上使用screenshot。任何会触发 API 的操作之后,都要检查网络,并检查是否有新错误:在 T3 上通过t3-code_preview_evaluate读取预装的收集器;在 Playwright 上使用browser_network_requests和browser_console_messages;在 CLI 上使用network requests和console。如果驱动无法生成某种证据,请报告该证据不可用,不要伪造。没有证据的步骤判定为 FAIL。证据收集器(仅限 T3)。在第一个测试步骤之前,通过
t3-code_preview_evaluate安装网络和控制台收集器:包装 fetch 和 XMLHttpRequest,监听 error 和 unhandledrejection 事件,并将日志写入页面全局数组,然后记录流程开始前的基线。每次导航后都要重新安装收集器,因为导航会销毁页面全局变量。之后每次检查网络和控制台时,都要读取这些数组;读取时若数组不存在,请报告该步骤的证据不可用,而不是报告无异常。录制。在第一个测试步骤之前开始录制,并在最后一个测试步骤之后停止录制,遵循上文的 Recording 部分。报告结果时,使用
Recording: <path>;若驱动无法录制,则使用Recording: unavailable。按照下文第 4 步的格式报告每一步。每个编号步骤都必须标注 PASS 或 FAIL。无法执行的步骤也必须标注 FAIL,不得省略。
清理。若录制仍在进行,先停止录制。在 Playwright MCP 上,调用
browser_close。在 T3 上,保持预览标签页打开,以便用户保留观看内容。在agent-browserCLI 上,运行record stop后再运行agent-browser close。
第 4 步:报告
QA: <flow description>
Step 1: <description>
PASS: <observation>
Step 2: <description>
PASS: <observation>
API: POST /api/endpoint -> 201
Step 3: <description>
FAIL: Expected X, got Y
Screenshot: .qa/03-step-name.png
Console errors: <list or "none">
Screenshots: .qa/ (<N> files)
Recording: <path or "unavailable">
VERDICT: <ALL PASS | PARTIAL | FAIL> (N/M steps)
失败时,我会提供预期结果与实际结果的准确对比,并引用截图。
上方的录制路径是交接给 file-pr 的路径;它会在创建 PR 时附上录制文件。我会报告该路径,然后停止;CLAUDE.md 中的延迟附加规则涵盖了 PR 创建后才生成的录制文件。
QA 测试结果报告(端点和 API QA)
当测试针对的是 API 或端点,而不只是点击路径时,我会构建测试矩阵,并在下方报告结构化结果,与第 4 步的浏览器报告一起提供,或以此替代浏览器报告。我会按类别、筛选条件、错误处理、分页和一致性对用例分组,然后逐一直接执行,并记录实际响应与预期响应的对照结果。若已发出请求但没有捕获到响应,该用例就判定为 FAIL,绝不会假定通过。
端点执行与身份验证。第 2 步和第 3 步只通过浏览器进行身份验证和操作,因此仅测试端点的运行需要独立的执行器和经过身份验证的上下文。
运行任何端点用例之前,我都会记录会话来源,即第 2 步进行身份验证时使用的协议、主机和端口。每个目标都会与该值核对,而“测试环境”会将其报告为“会话来源”,而不是定义它。我会计算这三项,而不是从 URL 中读取。我只接受 http 和 https 目标,并在任何审批或发送请求之前拒绝其他所有协议,包括 file、ftp 和 gopher;无论用例是否携带凭据,都一律如此,这样运行器就不会在未经我同意的情况下读取本地文件或建立非 HTTP 连接。我会拒绝 authority 中包含用户信息、百分号转义或反斜杠的任何 URL,然后将协议和主机转换为小写,将主机转换为 ASCII Punycode,去掉一个尾随点,并填入默认端口:HTTPS 为 443,HTTP 为 80。其他任何来源都需要单独审批,包括与浏览器应用不在同一主机上的 API。提及或粘贴端点不等于审批。我会向用户展示规范化后的来源,并说明将发送哪些凭据,只按名称标出会话 Cookie 和防 CSRF 令牌,绝不显示其值;我会在同一轮中等待答复,并将获批的来源记录在“测试环境”中。若未获审批,该用例就会被标记为 BLOCKED,我不会发送任何请求,无论是否携带凭据;这样,拼写错误或粘贴的链接都无法获取有效会话或预置记录 ID。
在任一执行路径中,携带凭据的请求都必须使用 HTTPS,除非主机是环回字面量、127.0.0.0/8 内的地址、::1、::ffff:127.0.0.0/104,或裸名称 localhost。我会直接从 URL 中读取该字面量,绝不接受自行解析出的名称,因为 curl 和浏览器在连接前都会再次解析;因此,即使我的检查已通过,重绑定或混合的 A 与 AAAA 响应仍可能将请求导向公网地址。对于不属于这些字面量的纯 HTTP 主机,任一执行路径都不会携带凭据,因此相关用例会被标记为 BLOCKED,而不会在明文传输下进行测试。
我会通过以下两种方式之一获取该上下文,并且两种方式都会先运行这些检查:
- 将第 2 步的会话导出到其中的
curl,包括其 Cookie 和 anti-CSRF 令牌。我从不传递-L,无论是否携带凭据,因此每个请求都只会发往我检查过的源站。3xx 是此案例中观察到的结果:我记录状态码和Location,根据预期响应对它们进行评分,绝不发起下一跳请求。我通过-b jar.txt使用限定主机的 Cookie jar 携带会话;当 jar 中 Cookie 的域不覆盖已批准的端点源站时,我会仅针对该源站设置显式的Cookie标头。只有端点不需要会话时,才仅需使用未经身份验证的普通curl。 - 通过驱动程序的 evaluate 钩子发起页面内请求。运行钩子的页面也必须通过源站检查,因为
fetch会与当前打开页面的源站相同,而浏览器会自行附加会话 Cookie。同源fetch不会添加 anti-CSRF 标头,因此我会调用应用自身的请求辅助函数(即它已用于 API 调用的客户端,由其附加令牌),或者从 Cookie 或 meta 标签中读取 anti-CSRF 令牌,并将其设置在RequestInit.headers中。跨源请求还需要credentials: "include"和匹配的 CORS。fetch会自行跟随重定向,并将 anti-CSRF 标头和 Cookie 带到下一跳,因此我会在每个 evaluate 钩子请求中设置redirect: "error"。仅有拒绝并不能证明发生了重定向或请求已发出:我会记录观察到的错误,并利用可用的网络证据区分已证实未发出(BLOCKED)和已发出但没有响应(FAIL)。如果仍无法确定是否已发出,我会将其报告为 FAIL 并说明这一不确定性,绝不假定发生了重定向或测试通过,也不会改用 curl 重试,因为服务器可能已经处理了该请求。需要重定向状态码或Location的案例,从一开始就通过 curl 流程执行。
我绝不接受未经身份验证的响应作为案例结果;如果无法获取已验证身份的上下文,我会将这些案例记录为 BLOCKED,而不是测试错误的对象。会修改共享数据的端点案例,在发出请求前也会执行与第 3 步相同的变更预检。如果没有操作 UI,则截图和录制不适用,因此每个案例都以捕获的请求和响应作为证据。
我会一次性捕获环境,以确保运行可复现;每行案例都会记录完整的请求输入,包括路径、查询、请求体和所有非机密标头,而不只是查询参数。重放时会使用新配置的第 2 步会话进行身份验证,因此表格中不会携带该会话:写入任何请求或响应值之前,我都会删去凭据、会话令牌、身份验证标头、Cookie 和个人数据,只保留案例所需的功能性证据、计数、ID、状态码和错误消息。我会在 Result 列中使用 PASS、FAIL 或 BLOCKED 文本,绝不使用勾选表情符号。
报告模板
## QA Test Results - <feature or endpoint>
### Test Environment
- Date: <YYYY-MM-DD>
- Session origin: <normalized origin Step 2 authenticated against>
- Approved endpoint origin: <normalized origin and the date approved, when it differs from the session origin>
- Backend: <url> (<database>)
- Test data: <seeded records and their IDs>
- Auth: <account, role, IDs>
### API Endpoint: `<METHOD /path>`
#### <Category> Tests
| # | Test | Request | Expected | Result |
|---|------|---------|----------|--------|
| 1 | <case> | `<path, query, body, safe headers; secrets redacted>` | <expected> | PASS/FAIL/BLOCKED: <actual observed, or the blocker> |
#### Error Handling Tests
| # | Test | Request | Expected | Result |
|---|------|---------|----------|--------|
| N | <bad input> | `<path, query, body, safe headers; secrets redacted>` | <status and message> | PASS/FAIL/BLOCKED: <observed status and message, or the blocker> |
### Browser UI Testing
- <what rendered, with screenshot paths under .qa/>
- <blockers hit, stated plainly, never hidden>
### Findings
- <what passed, what failed, and any behavior that is by design>
VERDICT: <ALL PASS | PARTIAL | FAIL | BLOCKED> (<passed>/<executed> cases, <blocked> blocked)
我会为每个类别保留一张表,并且只添加端点实际具有的类别。请求从未发出的案例为 BLOCKED,绝不标为 PASS 或 FAIL;上述未经身份验证响应规则所阻止的案例也是如此。请求的字节一旦离开客户端,就视为已发出,因此连接被拒绝或 DNS 查询失败都不算已发出。如果 fetch 被拒绝后仍无法确定请求是否已发出,我会将该案例计为已执行并标记为 FAIL,同时说明是否发出尚不确定;我绝不会声称请求已发出或重试。我会列出被阻止的案例及其阻止原因,不将其计入已执行数量;如果其他案例已执行,结论最高只能为 PARTIAL;如果没有案例执行,则为 BLOCKED。Browser UI Testing 部分会沿用上述浏览器流程中的截图和录制,因此混合运行会在同一处报告点击路径及其背后的端点。发现项会明确说明失败和按设计运行的行为;不加说明地省略内容,会让人误以为案例通过,而它并未通过。