Actual Budget 功能型 PR 浏览器测试实战:基于 playwright-cli 的 Netlify 预览验收与标注截图工作流
【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual
功能型(Feature / Enhancement)PR 的浏览器验收与 Bug 型 PR 完全不同:新功能在基线版本上并不存在,因此无需与edge对比,只需在 PR 自带的 Netlify 预览构建上证明"新行为确实按 PR 描述工作",并产出能让下一位审阅者一眼定位新 UI 的标注截图。本文以 Actual Budget 仓库内置的 browser-testing-feature.md 实战手册为主线,结合 SKILL.md、highlight-element.js 以及 preview-builds.md 等仓库证据,完整还原一套可直接复制的"探索计划 → 打开预览 → 逐步走查 → 标注截图 → 给出结论"验收流程,并讲解其底层原理与降级策略。
一、功能型 PR 与 Bug 型 PR 的验收差异
在进入流程之前,先明确这套手册的适用边界。Actual Budget 的 PR 审查 Skill 会将 PR 自动分为三类(见 SKILL.md):
- Bug PR:需要在
edge.actualbudget.org上先复现缺陷,再到 PR 预览上验证修复,遵循 browser-testing-bug.md 手册; - Feature / Enhancement PR:只在
deploy-preview-<num>预览上验证,不打开 edge,遵循本文讲解的 browser-testing-feature.md 手册; - 其他类型(refactor / chore / docs / deps):默认只做代码审查,跳过浏览器测试。
之所以功能型 PR 不做基线对比,原因很直接:新功能在 edge 上根本不存在,对比没有意义。验收的目标只有两个——证明新行为"如广告所述"般工作,以及产出一组让新功能一目了然的截图。手册特别强调:带标注的截图优于裸截图——用红色虚线框圈出新的 chip / 菜单 / 设置项,下一位审阅者无需阅读任何文字说明就能知道该看哪里。
二、前置常量与工具链
手册开头定义了三组贯穿全程的常量:
| 常量 | 值 | 说明 |
|---|---|---|
PREVIEW_URL | https://deploy-preview-<num>.demo.actualbudget.org/ | 以 PR 编号替换<num> |
| 输出目录 | ~/Downloads/pr-review-<num>/ | 一次审查的所有产物集中存放 |
| 标注脚本 | $(git rev-parse --show-toplevel)/.claude/skills/review-actual-pr/references/highlight-element.js | 通过git rev-parse解析仓库根目录,任何贡献者克隆后都能直接使用 |
关于预览 URL 的格式,仓库文档 preview-builds.md 有更完整的说明:每个 PR 会自动部署三个Netlify 预览——Demo(https://deploy-preview-{pr-number}.demo.actualbudget.org/)、Storybook(https://deploy-preview-{pr-number}--actualbudget-storybook.netlify.app/)、Website(https://deploy-preview-{pr-number}.www.actualbudget.org/),且 Netlify bot 会把确切 URL 以评论形式贴到每个 PR 上。本文验证的是其中的Demo 预览(包含完整应用界面)。
工具假设同样重要(见 SKILL.md 的 "Tooling assumptions" 一节):
gh已对actualbudget/actual配置只读访问权限(bot、CI 或私人账号均可);playwright-cli在 PATH 上(通常通过playwright-cliskill 安装)。如果不可用,可回退到npx --no-install playwright-cli;若仍失败,应停止浏览器测试阶段并向用户说明,而不是猜测替代工具;- 输出统一进入
~/Downloads/pr-review-<num>/(macOS / Linux / Windows 均存在该目录),一次运行的所有产物保持在同一目录,让报告与截图始终在一起。
三、Step 1:从 PR 推导探索计划
动手截图前,先基于 PR 标题和正文提炼四件事:
- 新功能是什么(用一句话概括,站在用户视角);
- 它位于 UI 的哪里(哪个页面 / 面板 / 菜单);
- 用户使用它的完整路径(3~8 步操作序列);
- 最终状态是什么(什么结果能证明功能生效)。
这四要素是后续所有走查步骤的蓝图。手册特别给出了一条重要纪律:如果 PR 描述太简略,无法写出清晰计划,先向用户确认,不要测试一个自己都没理解的功能——为一个不理解的功能截图毫无意义。
四、Step 2:打开预览并完成标准演示环境搭建
playwright-cli open $PREVIEW_URL playwright-cli snapshot随后执行标准演示环境搭建(该流程在 AGENTS.md 的 "Testing and previewing the app" 一节中有明确依据,SKILL.md 也将其标准化为三个步骤):
- 点击"Don't use a server"(不连接服务器);
- 点击"View demo"(查看演示数据)。
这一步会加载一个预置的测试预算,内含真实的账户、交易、类别和预算金额,远比从空预算开始测试有用;且 edge 与 preview 的演示数据完全一致,因此 Bug 复现结论可以互相印证。需要注意的是,预览构建没有同步服务器——preview-builds.md 明确提示,如果询问"服务器在哪里",请选择 "Don't use a server";即便要用自建服务器也应谨慎,因为预览构建更可能存在损坏预算数据的 Bug。"Don't use a server" 按钮在源码中确实存在,见 ConfigServer.tsx 与同文件 L550 处的两处实现。
等待预算加载完成后,设置一个得体的视口,保证截图美观:
playwright-cli resize 1440 900如果页面直接显示预算(保留了上次会话状态),直接使用即可;需要全新状态时清空 localStorage 后重载:
playwright-cli localstorage-clear playwright-cli reload五、Step 3:逐步走查功能,拍摄带标注的截图(核心环节)
对探索计划中的每一个有意义的步骤,执行如下循环:
- 操作:按需导航 / 点击 / 填写表单,选择器引用来自
playwright-cli snapshot输出的 refs; - 拍快照找稳定选择器:为 PR 引入的新 UI 元素找一个稳定选择器。优先级依次是:
data-testid(若存在)→ role + accessible name → CSS 选择器;避免脆弱的 nth-child 链式选择器。仓库前端代码中data-testid的使用非常普遍(例如 table.tsx、TransactionsTable.tsx、FilterExpression.tsx 等多处组件),这也印证了手册优先推荐data-testid的原因——它是为测试而生的稳定锚点; - 注入高亮叠加层(脚本路径用
git rev-parse解析,任何贡献者环境都可用):
SKILL_DIR="$(git rev-parse --show-toplevel)/.claude/skills/review-actual-pr" SELECTOR='<your selector>' LABEL='<short label, e.g. "New filter chip">' \ playwright-cli run-code --filename="$SKILL_DIR/references/highlight-element.js"- 截图:
playwright-cli screenshot --filename=$HOME/Downloads/pr-review-<num>/feature-step-N.png- 移除叠加层,避免污染下一张截图:
playwright-cli eval "document.querySelectorAll('[data-pr-highlight]').forEach(n => n.remove())"特别提醒:如果某一步会改变布局(弹窗打开、面板滑出),必须先重新playwright-cli snapshot——布局变化后旧的元素引用会失效。
5.1 标注脚本的底层原理
注入叠加层所调用的 highlight-element.js 值得深入解读,它是"红框标注"能力的真正实现:
- 脚本通过
module.exports = async page => {...}导出,由playwright-cli run-code注入浏览器执行;SELECTOR与LABEL通过环境变量传入(L10-L12),其中SELECTOR为必填,缺失会直接抛错(L14-L16); - 在页面上下文中用
page.evaluate执行(L18-L83):先用document.querySelector(selector)定位元素,未命中返回{ ok: false, reason: 'not-found' };然后调用scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' })将目标滚动到视口中央(L23-L27); - 通过
getBoundingClientRect()获取元素矩形,若宽或高为 0 则判定zero-size并返回失败(L29-L32)——这能有效拦截"选择器命中了但元素不可见/未渲染"的误标注; - 以
PAD = 6为内边距绘制固定定位的虚线框(L34-L50):border: 3px dashed #e3342f(Actual 品牌红色),并叠加boxShadow: 0 0 0 9999px rgba(0,0,0,0.04)形成轻微压暗的"看这里"效果,pointerEvents: 'none'保证标注不会拦截任何用户交互,zIndex: 2147483647确保叠加层永远在最上层; - 若提供了
LABEL,再绘制一个红色底、白色文字的小标签贴在目标上方(L52-L75),让截图自带语义,审阅者无需读任何文字说明; - 所有叠加节点均带
data-pr-highlight属性(box 与 label 两类),这就是"清场命令"能一键移除它们的依据(L7-L8 的注释、L36/L54 的setAttribute); - 返回
{ ok: true, rect }后,脚本再await page.waitForTimeout(50)(L92)——给浏览器一个绘制帧的时间,确保叠加层真正上屏后截图命令才触发,避免"标注没画出来"的空截图。
这套设计解释了手册中所有命令的配合逻辑:标注与清场通过同一个data-pr-highlight标记收敛,滚动定位与 50ms 等待则保证了截图时序的正确性。
六、Step 4:最终干净截图
完成最后一个标注步骤后,再补一张没有任何叠加层的最终状态截图:
playwright-cli screenshot --filename=$HOME/Downloads/pr-review-<num>/feature-final.png这张"干净图"与前面的 QA 证据图用途不同:它是可以直接放进发布说明或 PR 描述里的展示图。手册明确区分了这两类产物的定位——标注图是给审阅者的证据,干净图是给用户的门面。
七、Step 5:结论与报告
验收结束,用平实的语言给出结论:
Behavior matches PR description: yes / no / partially(行为与 PR 描述是否一致);- 对每个步骤给出一行说明:发生了什么、对应哪张截图;
- 如果 PR 描述中的某些内容在构建里根本看不到,必须明确指出——这可能意味着 PR 还需要继续打磨。
结合 SKILL.md 的报告规范,完整的 Testing 段落通常组织为:一段叙述(在什么环境、走查了什么、结果如何)、截图清单(~/Downloads/pr-review-<num>/<file>.png+ 一行说明)、一行 Verdict 结论。整个报告还需包含强制的Head SHA与Reviewed时间戳——这是后续重跑时判断审查是否过期(PR head 是否变更)的依据。此外,Skill 有硬性约束:审查全程不向 GitHub 回帖任何内容,所有建议以本地报告形式输出,报告末尾需保留"未向 GitHub 发布任何评论"的离线审查声明。
一次功能型 PR 审查的产物目录结构如下:
~/Downloads/pr-review-<num>/ ├── pr.json # gh pr view 输出 ├── diff.patch # gh pr diff 输出 ├── review.md # Markdown 审查报告 ├── findings.json # 可选的结构化发现 ├── feature-step-N.png # 功能型 PR:带标注的过程截图 └── feature-final.png # 功能型 PR:最终干净截图八、标注失败时的降级策略
如果找不到新 UI 元素的稳定选择器,按以下顺序降级:
- 先拍一张无标注快照,确认元素确实在屏幕上(排除"根本没渲染"的情况);
- 尝试更粗粒度的选择器,例如父容器;
- 最后手段:该步骤跳过标注,但仍要截图,并在报告中说明哪一步缺少高亮、为什么。
手册对"静默降级"是零容忍的:当用户要求标注截图而你没有提供时,绝不能默默交付无标注的截图,必须把缺口明确标记出来。结合 5.1 节的脚本实现也能看到,not-found与zero-size两个失败分支被显式抛错,本身就是"不让标注静默失败"这一设计意图的代码化体现。
九、需要避免的坑(Things to avoid)
手册最后给出了三条明确的反模式,每一条背后都有实际教训:
- 不要在 edge 上也测一遍。功能在 edge 上不存在,对比无用;
- 不要在步骤之间随意清空 localStorage。除非某一步确实需要全新状态,否则清空会把你登出演示环境,迫使你重做整套设置流程;
- 不要假设动画已经结束。点击打开面板后,先拍 snapshot 再截图,确保捕获的是动画结束后的稳定状态——这正好呼应了 5.1 节中
highlight-element.js里 50ms 等待帧的设计思路:时机不对,截图里的内容就会"差半拍"。
十、与仓库既有测试体系的衔接
这套浏览器验收手册并非孤立存在,它与 Actual Budget 的自动化测试体系互为补充:
- Playwright E2E:仓库在 packages/desktop-client/e2e 下维护了大量端到端测试(
*.test.ts/*.mobile.test.ts),并使用page-models/目录封装可复用的页面交互模型——手册建议的"先 snapshot 再操作、用稳定 refs"正是 E2E 测试的最佳实践在人工验收中的复刻; - 视觉回归测试(VRT):仓库通过
yarn vrt/yarn vrt:docker跑视觉回归,快照存放在各*-snapshots/目录。手册中"统一视口再截图、保证截图可复现"的做法,与 VRT 对"一致环境"的要求如出一辙; - 预览构建基础设施:每个 PR 自动部署的 Netlify 预览(preview-builds.md)正是本手册的测试靶场,也是人工验收能"零本地构建"直接上手的前提。
换言之,这条 feature-PR 浏览器测试流程,是在项目既有的 CI 预览、Playwright 工具链与页面选择器实践之上,补上了"人工验收 + 标注证据"这最后一块拼图——它让审阅者既能信任自动化测试,又能亲眼确认新功能在真实用户路径上的最终表现。
【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考