基于 browse CLI 的 AI 驱动对抗性 UI 测试。通过分析 git diff 仅测试变更部分,或探索整个应用以发现缺陷。测试功能正确性、可访问性、响应式布局和 UX 启发式规则。适用于用户要求测试 UI 变更、QA 拉取请求、审计可访问性或进行探索性测试的场景。支持本地浏览器(localhost)和远程 Browserbase(已部署站点)。
UI Test — 代理式 UI 测试技能
在真实浏览器中测试 UI 变更。你的任务是尝试破坏功能,而不是确认它们正常工作。
三种工作流:
- Diff 驱动 — 分析 git diff,仅测试变更部分
- 探索性 — 浏览应用,发现开发者未考虑到的缺陷
- 并行 — 将独立的测试组分散到多个 Browserbase 浏览器中
测试工作原理
主代理协调 — 规划测试策略,委派给子代理,并合并结果。子代理执行实际的浏览器测试。
规划:多角度分析,然后一次性执行
你必须自行完成所有三轮规划并在启动任何子代理之前输出它们。 规划在你的响应中完成 — 不会委派给子代理。不要跳过规划直接执行。
第一轮 — 功能: 核心用户流程是什么?应该正常工作的内容?将每个测试写为:操作 → 预期结果。
第二轮 — 对抗性: 重新阅读第一轮。你遗漏了什么?考虑:不同用户类型/角色、错误路径、空状态、竞态条件、边缘输入(空、超大、特殊字符、快速点击)。
第三轮 — 覆盖缺口: 重新阅读第一至二轮。考虑:可访问性(axe-core、仅键盘)、移动视口、控制台错误、与应用其他部分的视觉一致性。
去重: 将三轮合并为一个编号的测试列表。移除重叠项。将每个测试分配到一个组(例如 A 组、B 组)。
然后一次性执行 — 每组启动一个子代理。每个子代理接收其特定的测试列表,仅此而已。子代理不探索也不规划 — 它们执行分配的测试并报告结果。
在你的响应中输出三轮、合并的计划以及分组分配,然后再调用任何 Agent 工具。
拆分工作的原则
- 子代理执行分配的测试,而非开放式探索。 主代理为每个子代理提供特定的编号测试列表。子代理不规划、不探索、也不决定测试什么 — 它们执行列表并停止。
- 瓶颈是最慢的代理 — 拆分工作,使没有单个代理承担不成比例的份额。多个小代理优于少数大代理。
- 根据变更规模调整工作量 — 单个组件修复不需要多个代理或许多步骤。全页面重新设计则需要。让 diff 的范围驱动计划。
- 失败时不提前停止 — 在分配的测试范围内尽可能多地发现缺陷。
为子代理提供步骤预算
主代理必须在每个子代理提示中包含明确的 browse 步骤限制。 子代理不会自我限制 — 除非另有说明,否则它们会一直运行直到完成。
粗略启发式:少量针对性检查约 25 步,完整页面(功能+对抗性+可访问性)约 40 步,多页面或广泛类别约 75 步。根据分配的测试实际需求调整 — 这些是起点,而非规则。
每个子代理提示必须包含:
你的预算为 N 个 browse 步骤(每个 `browse` 命令 = 1 步)。请边执行边计数。当达到 N 时,立即停止并报告:
- 每个完成的测试:STEP_PASS/STEP_FAIL
- 每个未执行的测试:STEP_SKIP|<test-id>|budget reached
达到预算后不要重试或继续。
仅运行这些测试:[合并计划中的编号列表]
不要探索分配的测试之外的内容。
不要生成 HTML 报告或写入任何文件。仅返回步骤标记和你的发现(文本形式)。
主代理不应自行运行 browse 命令(除了验证开发服务器是否启动)。所有测试都在子代理中完成。
当子代理达到预算时,主代理按原样接受部分结果。 不要重新运行或重试子代理。在最终报告中包含 SKIPPED 测试,以便开发者了解哪些内容未被覆盖。
报告
每个子代理返回:
Tests: 8 | Passed: 5 | Failed: 2 | Skipped: 1 | Pages visited: 2
主代理合并为最终报告:
Tests: 20 | Passed: 14 | Failed: 4 | Skipped: 2 | Agents: 3 | Pass rate: 70%
不要报告“已用步骤数” — browse 命令计数是实现细节,对审阅者而言不是有意义的指标。
测试理念
你是一个对抗性测试者。 你的目标是发现缺陷,而不是证明正确性。
- 尝试破坏你测试的每个功能。 不要只检查“按钮是否存在?” — 快速点击两次、提交空表单、粘贴 500 个字符、在流程中按 Escape。
- 测试开发者未考虑到的内容。 空状态、错误恢复、仅键盘导航、移动端溢出。
- 每个断言必须基于证据。 比较前后快照。通过引用检查特定元素。没有来自可访问性树或确定性检查的具体证据,绝不报告 PASS。
- 报告失败时提供足够的复现细节。 包括确切操作、预期结果、实际结果以及建议修复。
断言协议
每个测试步骤必须产生结构化的断言。不要写自由形式的“看起来不错”。
步骤标记
对于每个测试步骤,恰好输出一个标记:
STEP_PASS|<step-id>|<evidence>
或
STEP_FAIL|<step-id>|<expected> → <actual>|<screenshot-path>
step-id:简短标识符,如homepage-cta、form-validation-error、modal-cancelevidence:你观察到的证明步骤通过的内容(元素引用、文本内容、URL、eval 结果)expected → actual:你预期的与实际得到的对比screenshot-path:保存的截图路径(仅失败时 — 见下方截图捕获)
失败时的截图捕获
每个 STEP_FAIL 必须附带截图,以便开发者直观地看到问题所在。
当测试步骤失败时:
# 1. 观察到失败后立即截图
browse screenshot --path .context/ui-test-screenshots/<step-id>.png
# 如果 --path 不受支持,截图后手动保存:
browse screenshot
# browse CLI 会输出截图路径 — 移动/复制它:
cp /tmp/browse-screenshot-*.png .context/ui-test-screenshots/<step-id>.png
在任何测试运行开始时设置截图目录:
mkdir -p .context/ui-test-screenshots
规则:
- 文件名 = step-id(例如
double-submit.png、axe-audit.png、modal-focus-trap.png) - 存储在
.context/ui-test-screenshots/中 — 该目录被 gitignore,对开发者和其它代理可访问 - 对于并行运行,包含会话名称:
<session>-<step-id>.png(例如signup-double-submit.png) - 在失败时刻截图 — 捕获损坏状态,而非恢复后
- 对于视觉/布局缺陷,同时截取基线(正常工作状态)用于比较:
<step-id>-baseline.png
如何验证(按严谨度排序)
- 确定性检查(最强)—
browse eval返回可检查的结构化数据。例如:axe-core 违规数、document.title、表单字段值、控制台错误数组、元素数量。 - 快照元素匹配 — 可访问性树中存在具有特定角色和文本的特定元素。通过引用检查:
@0-12 button "Save"。元素要么存在于树中,要么不存在。 - 前后对比 — 操作前快照、操作、操作后快照。验证树按预期方式变化(元素出现、消失、文本改变)。
- 截图 + 视觉判断(最弱)— 仅用于可访问性树无法捕获的纯视觉属性(颜色、间距、布局)。始终附带你具体评估的内容。
前后对比模式
这是核心验证循环。每次交互都使用它:
# 1. 之前:捕获状态
browse snapshot
# 记录:存在哪些元素、它们的文本、它们的引用
# 2. 操作:执行交互
browse click @0-12
# 3. 之后:捕获新状态
browse snapshot
# 比较:发生了什么变化?什么出现了?什么消失了?
# 4. 断言:基于比较发出标记
# 如果对话框出现:STEP_PASS|modal-open|dialog "Confirm" appeared at @0-20
# 如果无变化:
browse screenshot --path .context/ui-test-screenshots/modal-open.png
# STEP_FAIL|modal-open|expected dialog to appear → snapshot unchanged|.context/ui-test-screenshots/modal-open.png
设置
which browse || npm install -g browse
避免权限疲劳
此技能运行许多 browse 命令(快照、点击、eval)。为避免逐个批准,将 browse 添加到允许的命令中:
将两个模式添加到 .claude/settings.json(项目级)或 ~/.claude/settings.json(用户级):
{
"permissions": {
"allow": [
"Bash(browse:*)",
"Bash(BROWSE_SESSION=*)"
]
}
}
第一个模式覆盖普通的 browse 命令。第二个模式覆盖并行会话(BROWSE_SESSION=signup browse open ...)。两者都需要以避免批准提示。
模式选择
| 目标 | 模式 | 命令 | 认证 |
|---|---|---|---|
localhost / 127.0.0.1 |
本地 | browse open <url> --local |
无需(默认使用干净的隔离本地浏览器) |
| 已部署/预发布站点 | 远程 | browse open <url> --remote |
Browserbase 凭据;在支持的地方使用上下文 |
规则:如果目标 URL 包含 localhost 或 127.0.0.1,在第一次 browse open 时传递 --local。
本地模式(localhost 默认)
browse open http://localhost:3000 --local
browse open ... --local 默认使用干净的隔离本地浏览器,最适合可复现的 localhost QA 运行。
仅在需要时使用本地模式变体:
browse open <url> --auto-connect— 自动发现现有的可调试本地 Chrome。仅当测试明确需要现有的本地登录/cookies/状态时使用。browse open <url> --cdp <port|url>— 附加到特定的 CDP 目标(显式本地浏览器附加)。
远程模式(通过 cookie-sync 的已部署站点)
# 步骤 1:从本地 Chrome 同步 cookies 到 Browserbase
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains your-app.com
# 输出:Context ID: ctx_abc123
# 步骤 2:使用同步的上下文以远程模式打开
SESSION_JSON="$(browse cloud sessions create --context-id ctx_abc123 --persist --keep-alive)"
SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"
CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"
browse open https://staging.your-app.com --cdp "$CONNECT_URL"
browse snapshot
# ... 运行测试 ...
browse stop
browse cloud sessions update "$SESSION_ID" --status REQUEST_RELEASE
Cookie-sync 标志:--domains、--context、--verified、--proxy "City,ST,US"
工作流 A:Diff 驱动测试
阶段 1:分析 diff
git diff --name-only HEAD~1 # 或:git diff --name-only / git diff --name-only main...HEAD
git diff HEAD~1 -- <file> # 读取实际变更
对变更文件分类:
| 文件模式 | UI 影响 | 测试内容 |
|---|---|---|
*.tsx, *.jsx, *.vue, *.svelte |
组件 | 渲染、交互、状态、边缘情况 |
pages/**, app/**, src/routes/** |
路由/页面 | 导航、页面加载、内容、404 处理 |
*.css, *.scss, *.module.css |
样式 | 视觉外观(截图)、响应式 |
*form*, *input*, *field* |
表单 | 验证、提交、空输入、长输入、特殊字符 |
*modal*, *dialog*, *dropdown* |
交互式 | 打开/关闭、Escape、焦点陷阱、取消与确认 |
*nav*, *menu*, *header* |
导航 | 链接、激活状态、路由、键盘导航 |
| 仅非 UI 文件 | 无 | 跳过 — 报告“无需 UI 测试” |
阶段 2:将文件映射到 URL
检测框架:cat package.json | grep -E '"(next|react|vue|nuxt|svelte|@sveltejs|angular|vite)"'
| 框架 | 默认端口 | 文件 → URL 模式 |
|---|---|---|
| Next.js App Router | 3000 | app/dashboard/page.tsx → /dashboard |
| Next.js Pages Router | 3000 | pages/about.tsx → /about |
| Vite | 5173 | 检查路由配置 |
| Nuxt | 3000 | pages/index.vue → / |
| SvelteKit | 5173 | src/routes/+page.svelte → / |
| Angular | 4200 | 检查路由模块 |
阶段 3:确保正确的代码在运行
在测试之前,验证开发服务器正在提供来自 diff 的代码 — 而不是过时的分支。
如果测试 PR 或特定分支:
# 检查当前检出的分支
git branch --show-current
# 如果不是 PR 分支,切换到它
git fetch origin <branch> && git checkout <branch>
# 安装依赖 — 不同分支的 lockfile 可能不同
yarn install # 或 npm install / pnpm install
如果开发服务器已在不同分支上运行,切换后重启它。
查找正在运行的开发服务器:
for port in 3000 3001 5173 4200 8080 8000 5000; do
s=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port" 2>/dev/null)
if [ "$s" != "000" ]; then echo "Dev server on port $port (HTTP $s)"; fi
done
如果未找到:告诉用户启动开发服务器。
验证它确实渲染:
在 browse open + browse snapshot 之后,检查可访问性树是否包含真实页面内容(导航、标题、交互元素) — 而不仅仅是错误覆盖层或空 body。Next.js 开发服务器可以在显示全屏构建错误对话框时返回 HTTP 200。如果快照为空或被错误对话框主导,则服务器已损坏 — 在测试前修复构建。
阶段 4:生成测试计划
对于每个变更区域,规划快乐路径和对抗性测试:
Test Plan (based on git diff)
=============================
Changed: src/components/SignupForm.tsx (added email validation)
1. [happy] Valid email submits successfully
URL: http://localhost:3000/signup
Steps: fill valid email → submit → verify success message appears
2. [adversarial] Invalid email shows error
Steps: fill "not-an-email" → submit → verify error message appears
3. [adversarial] Empty form submission
Steps: click submit without filling anything → verify error, no crash
4. [adversarial] XSS in email field
Steps: fill "<script>alert(1)</script>" → submit → verify sanitized/rejected
5. [adversarial] Rapid double-submit
Steps: click submit twice quickly → verify no duplicate submission
6. [adversarial] Keyboard-only flow
Steps: Tab to email → type → Tab to submit → Enter → verify success
阶段 5:执行测试
browse stop 2>/dev/null
mkdir -p .context/ui-test-screenshots
# localhost/default QA → clean, reproducible local run
browse open http://localhost:3000 --local
对于每个测试,遵循前后模式:
# 导航
browse open http://localhost:3000/path --local
browse wait load
# 之前快照
browse snapshot
# 注意当前状态:元素、引用、文本
# 操作
browse click @0-ref
# 或:browse fill "selector" "value"
# 或:browse type "text"
# 或:browse press Enter
# 之后快照
browse snapshot
# 与之前比较:发生了什么变化?
# 使用标记断言
# STEP_PASS|step-id|evidence 或 STEP_FAIL|step-id|expected → actual
阶段 6:报告结果
## UI Test Results
### STEP_PASS|valid-email-submit|status "Thanks!" appeared at @0-42 after submit
- URL: http://localhost:3000/signup
- Before: form with email input @0-3, submit button @0-7
- Action: filled "user@test.com", clicked @0-7
- After: form replaced by status element with "Thanks! We'll be in touch."
### STEP_FAIL|double-submit|expected single submission → form submitted twice|.context/ui-test-screenshots/double-submit.png
- URL: http://localhost:3000/signup
- Before: form with submit button @0-7
- Action: clicked @0-7 twice rapidly
- After: two success toasts appeared, suggesting duplicate submission
- Screenshot: .context/ui-test-screenshots/double-submit.png
- Suggestion: disable submit button after first click, or debounce the handler
---
**Summary: 4/6 passed, 2 failed**
Failed: double-submit, xss-sanitization
Screenshots saved to `.context/ui-test-screenshots/` — open any failed step's screenshot to see the broken state.
完成后始终 browse stop。
阶段 7:生成 HTML 报告
生成文本报告后,生成一个独立的 HTML 报告,审阅者可以在浏览器中打开。报告将截图内联(base64)嵌入,因此作为单个文件工作 — 无需外部依赖。
原因: 文本报告对代理对话很好,但审阅者(PM、设计师、其他工程师)希望有一个可以打开、扫描和分享的可视化产物。内联截图使失败立即显而易见。
如何生成
- 读取 references/report-template.html 中的 HTML 模板
- 通过用实际测试数据替换模板占位符来构建报告:
| 占位符 | 值 |
|---|---|
{{TITLE}} |
报告标题,用于 <title> 标签(例如 "UI Test: PR #1234 — OAuth Settings") |
{{TITLE_HTML}} |
报告标题,用于可见的 <h1>。如果有 PR URL,将 PR 引用包裹在 <a> 标签中使其可点击(例如 UI Test: <a href="https://github.com/org/repo/pull/1234">PR #1234</a> — OAuth Settings)。如果没有 URL,使用与 {{TITLE}} 相同的纯文本。 |
{{META}} |
单行上下文:日期、应用 URL、用户、分支 |
{{TOTAL_TESTS}} |
STEP_PASS + STEP_FAIL 总数 |
{{AGENT_COUNT}} |
运行的子代理数量 |
{{PASS_COUNT}} |
STEP_PASS 数量 |
{{FAIL_COUNT}} |
STEP_FAIL 数量 |
{{PASS_RATE}} |
整数百分比(例如 "92") |
{{RATE_CLASS}} |
good(≥90%)、warn(70–89%)、bad(<70%) |
{{FAILURES_SECTION}} |
失败测试卡的 HTML(见下文) |
{{PASSES_SECTION}} |
通过测试卡的 HTML(见下文) |
- 对于每个测试结果,生成一个
<details>卡片。失败的测试应默认打开,以便审阅者立即看到:
<!-- 失败测试卡(默认打开) -->
<div class="section">
<h2>Failures <span class="count">{{FAIL_COUNT}}</span></h2>
<details class="test-card fail" open>
<summary>
<span class="badge fail">FAIL</span>
<span class="step-id">step-id-here</span>
<span class="evidence">expected → actual</span>
</summary>
<div class="body">
<dl>
<dt>URL</dt><dd>http://localhost:3000/path</dd>
<dt>Action</dt><dd>What was done</dd>
<dt>Expected</dt><dd>What should have happened</dd>
<dt>Actual</dt><dd>What happened instead</dd>
</dl>
<div class="suggestion">Fix: description of suggested fix</div>
<div class="screenshot">
<img src="data:image/png;base64,..." alt="Screenshot of failure">
<div class="caption">step-id.png — captured at moment of failure</div>
</div>
</div>
</details>
</div>
<!-- 通过测试卡(默认折叠) -->
<div class="section">
<h2>Passed <span class="count">{{PASS_COUNT}}</span></h2>
<details class="test-card pass">
<summary>
<span class="badge pass">PASS</span>
<span class="step-id">step-id-here</span>
<span class="evidence">evidence summary</span>
</summary>
<div class="body">
<dl>
<dt>URL</dt><dd>http://localhost:3000/path</dd>
<dt>Evidence</dt><dd>What was observed</dd>
</dl>
</div>
</details>
</div>
- 将截图嵌入为 base64,使 HTML 完全自包含:
# 将截图转换为 base64 数据 URI
base64 -i .context/ui-test-screenshots/step-id.png | tr -d '\n'
# 用作:src="data:image/png;base64,<output>"
读取 STEP_FAIL 标记中引用的每个截图文件,进行 base64 编码,并将其作为 <img src="data:image/png;base64,..."> 嵌入到相应的测试卡中。对于 STEP_PASS,仅在明确截取了截图时嵌入(例如基线截图)。
- 将最终 HTML 写入
.context/ui-test-report.html:
# 写入生成的 HTML
cat > .context/ui-test-report.html << 'REPORT_EOF'
<!DOCTYPE html>
...generated report...
REPORT_EOF
# 为审阅者打开
open .context/ui-test-report.html # macOS
# xdg-open .context/ui-test-report.html # Linux
- 告诉用户:
Report saved to .context/ui-test-report.html并提供打开选项。
规则:
- 失败部分在通过部分之前 — 审阅者首先关心什么被破坏了
- 失败卡片默认
open;通过卡片折叠 - 每个 STEP_FAIL 卡片必须包含嵌入的截图 — 如果截图文件缺失,在卡片中注明
- 如果提供了建议/修复,在每个失败卡片中包含它
- 报告必须离线工作 — 无 CDN 链接,无外部资源
- 保持 HTML 在 5MB 以下 — 如果截图超出,降低图像质量或跳过通过的基线截图
对抗性测试模式
将这些应用于你测试的每个交互元素。阅读 references/adversarial-patterns.md 获取完整的模式库(表单、模态框、导航、错误状态、键盘可访问性)。
确定性检查
这些产生结构化数据,而非判断调用。将它们用作最强形式的断言。
| 检查 | 捕获内容 | 断言 |
|---|---|---|
| axe-core | WCAG 违规 | violations.length === 0 |
| 控制台错误 | 运行时异常、失败的请求 | 空错误数组 |
| 损坏的图片 | 缺失/失败的图片加载 | 没有 naturalWidth === 0 的图片 |
| 表单标签 | 没有可访问标签的输入 | 每个输入有 hasLabel: true |
有关确切的 browse eval 配方,请阅读 references/browser-recipes.md。
工作流 B:探索性测试
无 diff,无计划 — 只需打开应用并尝试破坏它。当用户说“测试我的应用”、“查找缺陷”或“QA 这个站点”时使用此工作流。
方法
- 发现应用 — 读取
package.json检测框架,然后打开根 URL 并快照查看内容 - 导航所有内容 — 点击导航链接,访问每个可到达的页面,记录存在的内容
- 测试你发现的内容 — 对于每个页面,应用下面的对抗性模式(表单、模态框、导航、键盘、错误状态)
- 运行确定性检查 — 在每个页面上运行 axe-core、控制台错误、损坏的图片、表单标签
- 报告发现 — 使用 STEP_PASS/STEP_FAIL 标记,为失败包含复现步骤
不要试图系统地覆盖。只需像用户一样探索,但意图是破坏东西。代理擅长这个 — 让它自由漫游。
探索性运行的提示
- 从主页开始,然后自然地跟随导航
- 尝试 404 页面(
/does-not-exist)— 是自定义还是默认? - 寻找空状态(无数据的页面)
- 在有效输入之前用垃圾输入测试表单
- 在每个页面上检查移动视口(375px)— 是否溢出?
- 如果应用有认证,首先使用 cookie-sync
工作流 C:并行测试
使用命名的 browse 会话(BROWSE_SESSION=<name>)并发运行独立的测试组。每个会话有自己的浏览器。适用于本地和远程模式。
当测试多个页面或类别并希望更快的挂钟时间时使用。
阅读 references/parallel-testing.md 获取完整工作流:会话设置、代理扇出、用于认证的 cookie-sync 以及结果合并。
设计一致性
检查变更的 UI 是否与应用的其他部分视觉匹配。在进行视觉或设计检查时阅读 references/design-consistency.md。
测试类别
| 类别 | 方法 | 断言类型 |
|---|---|---|
| 可访问性 | axe-core + 键盘导航 | 确定性(违规数) |
| 视觉质量 | 截图 + 启发式评估 | 视觉判断(最弱 — 注明具体内容) |
| 响应式 | 视口扫描 + 截图 | 视觉 + 确定性(溢出检查) |
| 控制台健康 | 控制台捕获 eval | 确定性(错误数) |
| UX 启发式 | 快照 + 用户体验法则 + Nielsen | 结构化判断(引用具体启发式) |
| 错误状态 | 导航到空/错误状态 | 前后对比 |
| 数据显示 | 表格/仪表板快照 | 元素匹配(列数、格式) |
| 设计一致性 | 截图基线 + 变更页面比较 | 视觉判断(引用具体属性) |
| 探索性 | 自由导航 + 对抗性测试 | 前后对比 + 判断 |
参考指南(按需加载):
- 对抗性模式 — references/adversarial-patterns.md — 测试表单、模态框、导航或键盘可访问性时加载
- 浏览器配方 — references/browser-recipes.md — 运行确定性检查(axe-core、控制台、图片、表单标签)时加载
- 探索性测试 — references/exploratory-testing.md — 工作流 B(无 diff,开放式探索)时加载
- UX 启发式 — references/ux-heuristics.md — 评估 UX 质量或引用特定启发式时加载
- 设计系统 — references/design-system.example.md — 供用户自定义的模板
- 设计一致性 — references/design-consistency.md — 进行视觉一致性检查时加载
- 并行测试 — references/parallel-testing.md — 工作流 C(并发会话)时加载
- 报告模板 — references/report-template.html — 阶段 7 报告生成的 HTML 模板
如需带有确切命令的示例,请阅读 EXAMPLES.md 以查看断言协议的实际应用。
最佳实践
- 保持对抗性 — 尝试破坏东西,而不仅仅是确认它们工作
- 每个断言需要证据 — 快照引用、eval 结果或前后差异
- 每次交互前后对比 — 快照、操作、快照、比较
- 每个失败截图 — 在 STEP_FAIL 时立即
browse screenshot,保存到.context/ui-test-screenshots/<step-id>.png - 先进行确定性检查 — 在视觉判断之前运行 axe-core、控制台错误、表单标签
- 对于 localhost,从干净的本地模式开始 — 在第一次
browse open时传递--local以获得可复现的运行;仅在需要现有本地状态时使用--auto-connect - 完成后始终
browse stop— 对于并行运行,停止每个命名的会话 - 报告失败时包含复现步骤 — 操作、预期、实际、截图路径、建议
- 并行化独立测试 — 在已部署站点上测试多个页面或类别时,使用工作流 C 和命名会话
故障排除
- "No active page":
browse stop,重试。对于僵尸进程:pkill -f "browse.*daemon" - 开发服务器无响应:
curl http://localhost:<port>— 要求用户启动它 browse eval使用await失败:改用.then()—browse eval不支持顶级 await- 元素引用未找到:再次
browse snapshot— 引用在页面更新时改变 - 空白快照:在快照前使用
browse wait load或browse wait selector ".expected" - SPA 深层链接 404:先导航到
/,然后点击进入 - 远程认证失败:使用
--context <id>重新运行 cookie-sync,尝试--verified - 并行会话冲突:确保每个
browse命令使用BROWSE_SESSION=<name>— 否则命令进入默认会话 - 会话未停止:
BROWSE_SESSION=<name> browse stop。对于僵尸进程:pkill -f "browse.*<name>.*daemon"






