落地页标题换了一行,截图差异很大;付款按钮的点击区域被透明层挡住,截图可能完全一样。视觉回归有用,但它检测的是像素变化,不能包办交互质量。
对一人维护的前端项目,我会把自动比较作为定位入口,让失败能说清楚“哪里变了”,再由行为断言判断关键任务是否仍可完成。
先稳定环境,再讨论阈值
Playwright视觉比较文档指出,操作系统、浏览器、字体等环境差异会影响截图。基线与回归应使用一致环境;动画、当前时间、随机头像等内容也需控制。
不要一遇到失败就放宽阈值。阈值容忍的是不影响判断的噪声,不应吞掉真实布局缺陷。基线更新是一次代码审阅:先看差异图,再看页面行为,最后决定是否接受。
图中的三类结果不能互相覆盖。像素一致但行为失败,仍然需要阻断;行为通过但视觉有意变更,需要人工接受新基线。
把发布决策从比较器里拿出来
下面用模拟比较结果演示门禁,不运行浏览器,也不读取真实截图。
const assert = require('node:assert/strict'); function releaseGate({ behavior, pixels, visualApproved }) { if (!['pass', 'fail', 'not_run'].includes(behavior) || !['same', 'changed', 'not_run'].includes(pixels)) throw new TypeError('invalid result'); if (behavior !== 'pass' || pixels === 'not_run') return 'blocked'; if (pixels === 'changed' && visualApproved !== true) return 'review'; return 'ready'; } assert.equal(releaseGate({ behavior: 'fail', pixels: 'same' }), 'blocked'); assert.equal(releaseGate({ behavior: 'pass', pixels: 'changed' }), 'review'); assert.equal(releaseGate({ behavior: 'pass', pixels: 'changed', visualApproved: true }), 'ready'); assert.equal(releaseGate({ behavior: 'not_run', pixels: 'same' }), 'blocked'); console.log('visual-gate: 4 assertions passed');保存为visual-gate.cjs,运行node visual-gate.cjs。结果ready只表示本例两个条件满足,不能扩大解释成整个产品可发布。真实项目仍有安全、数据迁移、兼容性等其他门禁。
选少量关键状态,别只截首页
一个设置对话框至少有默认、输入错误、保存中、失败保留输入、成功关闭等状态。再加上窄屏、长文本和键盘焦点,覆盖面已经比单纯增加十张首页截图更有价值。
失败报告最好同时提供原图、新图、差异图及访问路径。若接入模型解释差异,模型结论只能辅助定位,不能自动把差异标为无害。本文没有安装Playwright,没有运行真实浏览器或模型;本地验证仅覆盖门禁决策,不能报告界面测试通过率。