视觉回归 · 跨浏览器欧盟托管 · 遵循 GDPR

上线您设计的那版 UI而不是悄悄溜过 CI 的那一版

ScanU 是面向专业 Web 团队的视觉回归与跨浏览器截图测试平台。它会对您关心的每一个页面,按真实用户实际使用的浏览器和视口,生成像素级稳定的渲染;并把单元测试和端到端测试在沉默中漏掉的布局变化暴露出来。

浏览器
Chromium · Gecko · WebKit
视口
桌面 · 平板 · 移动端
托管
欧盟 · 遵循 GDPR
集成
CI/CD 就绪

ScanU 是什么

一个视觉 QA 平台,而不只是截图采集器

ScanU 把发布质量的最后一公里 — 也就是用户真正看到的部分 — 变成团队可以据此行动的信号。它会对您指定的页面生成像素级稳定的截图,记录一份显式 baseline,此后每一次运行都会在那些真正重要的浏览器与设备上,与该 baseline 做比对。

大多数团队已经跑着 linter、类型检查、单元测试和端到端流程。 但这些都可能保持绿色 — 而与此同时,一个按钮却悄悄下移了 十二个像素,一个弹层丢了 padding,一个菜单在 Safari 上 错乱,或者一个移动端断点让 hero 区无声地重排。功能测试 验证的是行为,它们不会验证 页面长成什么样。这正是截图测试工具存在的意义, 也正是 ScanU 在没有任何仪式感的情况下所要做的事。

在底层,ScanU 驱动真实的渲染引擎 — Chromium、Gecko 和 WebKit — 访问您指定的 URL 或组件预览。每一次运行都会在 每对(浏览器,视口)配置上产生一张确定性的截图。第一次被 接受的运行会成为 baseline。从此,每一个 pull request、 每一次部署、每一次定时检查,都是针对这个 baseline 的 diff: 任何一处视觉回归都会以可审阅的变更形式被突显出来 — 就像 code review 会把一行代码的改动凸显出来一样。

完整的 比对流水线 负责处理那些让截图 diff 在生产中真正可用的细节: anti-aliasing 容差、动画稳定、字体加载、动态内容遮罩、 滚动截取以及感知 diff 阈值 — 一句话,不能让 sub-pixel 级 的 font hinting 变化淹没掉一处真正的布局回归。

ScanU 面向那些把 UI 视为与用户之间契约的团队。它既是视觉 回归测试工具,也是跨浏览器截图比对工具,又是 diff 审阅 界面 — 这些都集中在一个平台里 — 坐落于您的 build 与 release gate 之间。

像素级稳定的截图

确定性渲染,等待字体加载、固定动画、遮罩动态内容 — 让 diff 反映真实回归,而不是噪声。

跨浏览器矩阵

Chromium、Gecko 与 WebKit 并行驱动,覆盖您的用户真正使用的桌面、平板与移动端视口。

可审阅的 diff

baseline、当前截图、高亮 diff 三者并排。接受、拒绝或更新 baseline — 始终是显式动作,从不静默发生。

CI/CD 原生

与您现有的流水线同跑。Status check、PR 评论与产物链接,出现在工程师本来就在做 code review 的地方。

对设计系统友好

把 ScanU 对准 Storybook、组件沙箱或线上 URL。Token 变动与组件重构在发布前就能获得一次诚实的视觉审计。

欧盟托管

截图与元数据保存在欧洲基础设施上,数据处理遵循 GDPR — 适合对数据驻留有严格要求的团队。

为什么这件事重要

视觉回归就是那类真正触达用户的 bug

视觉 bug 的成本很少用开发时间来衡量。它衡量的是一种信任 — 当用户打开您的产品,看到某处微妙的不对劲,心里默默判定:这个团队不在意细节。这种信任很难再建,而几乎总是预防 bug 的成本更低。

功能测试能很好地回答一个问题:「点击按钮时,它做了正确的事吗?」 但对于点击之前发生的一切 — 按钮颜色是否正确、尺寸是否正确、 位置是否正确、在 360 像素屏幕上是否仍然可读、在最近一次 CSS 重构之后是否还真的可见 — 它们是沉默的。绝大多数发布 当天的事故,就住在这条缝隙里。

我们观察到的、也是 ScanU 专门为之设计的真实回归,会围绕 少数几种可预测的触发源聚集:

  • 一次 CSS 重构,让 flex 容器在某一个断点上的 gap 塌陷。桌面端看起来一切如常;到了 768 像素 的平板上,两张卡片已经开始重叠。
  • 一次 design token 调整,让每一个 spacing 值挪动两像素。 没有一处会失败,但一打精心平衡的版式,悄无声息地失去了 平衡。
  • 一个第三方组件 — cookie 横幅、在线客服、广告位 — 发了一次 静默更新,first paint 被挪了位置。Core Web Vitals 一夜之间 恶化,而您这边没有改动过一行代码。
  • 框架的一次升级,更换了默认的 font-feature 栈。长文页面上 的正文重新回流了半行。hero 上的 CTA 忽然沉到了首屏之下。
  • 一次 feature flag 灰度发布,把 DOM 顺序改了。在 Safari 上, 一个弹层的 stacking context 变了,关闭按钮变得无法点击。 在 Chrome 上,一切看起来正常。

这些变化没有一个会让 assertion 变红。但每一个都在折损用户 体验。ScanU 的工作,就是让这第二类故障和一个失败的单元 测试一样容易被看见、被审阅、被拦下。当 diff 出现在 pull request 上,设计师就能像 reviewer 在函数签名上发表意见 一样介入 — 在合入之前,而不是等到它上线。

这正是关键的一步转变 — 从「希望有人在 staging 上会注意到」 走向「像审阅代码变更那样审阅视觉变更」— 也正是视觉回归 测试工具在现代发布流水线中的核心价值。

跨浏览器测试

您发布的 Web,并不是您测过的那张 Web

跨浏览器测试并非 IE6 时代遗留下来的一道怀旧税。现代引擎 — Chromium、Gecko 与 WebKit — 仍然在细节上存在分歧,而您的布局正默默地依赖这些细节;许多线上事故,归根结底都能追溯到一处没人审计过的渲染差异。

当一处回归只在某一个引擎上出现,几乎总是意味着以下三种 之一:用到了某个特性,它在部分引擎中上线得比其他晚;某块 布局依赖于一个按平台变化的度量(scrollbar 宽度、字体 baseline、平滑算法);或者某个依赖 user-agent 的 polyfill 在线上的表现和在开发机上略有不同。三种情况对一套与引擎 无关的测试套件都是不可见的。

跨浏览器审计中常见的例子:

  • 表单控件 — 尤其是 datedatetime-localselect — 在 WebKit 上渲染出的控件外观与 Chromium 上明显不同,偶尔会把相邻元素 挤开几像素。
  • 滚动条空间计算:WebKit 在 macOS 上默认隐藏滚动条,Chromium 在 Windows 上则为它保留宽度。一份像素完美的布局在一边 是完美的,到了另一边就会被裁掉或多出间隙。
  • 字体的 sub-pixel hinting 在不同引擎之间存在差异。某一 line-height 在 Chromium 上很平衡,到了 Gecko 可能就把 CTA 顶到首屏之下。
  • flex 容器里的 CSS gapaspect-ratio 以及 container queries,在各引擎中落地的时间略有 差异。如果您的 build 以较旧的 baseline 为目标,fallback 路径的渲染效果可能与主路径并不相同。
  • 系统级偏好 — prefers-color-schemeaccent-color 以及 Windows 上的 forced-colors — 会以不同方式在各平台上透到 DOM,在本地开发环境中很容易被忽略。

ScanU 会在您配置的所有引擎与视口上,用同样的 fixture 和 timing hook 在同一次运行中捕获每一个页面,从而让您看到的 diff 是真实的布局差异 — 而不是「每个浏览器跑在不同 harness 里」的副作用。完整的 跨浏览器能力 可以在产品页查看:您配置的是哪张矩阵,每次运行捕获的 就是哪张矩阵。

对于交付设计系统的团队,或任何把视觉识别视为品牌一部分 的产品,这张矩阵都不是锦上添花。它是唯一能够保证您的客户 在 Safari 上看到的就是您在 Chrome 上签收的那个状态的方法。

示例:ScanU 对某营销首页的一次跨浏览器运行
浏览器视口状态
Chromium 1241440 × 900与 baseline 一致
Firefox 1261440 × 900与 baseline 一致
WebKit (Safari 17)1440 × 900视觉 diff · 待审阅
Chromium 124768 × 1024与 baseline 一致
WebKit (Safari 17)768 × 1024检测到布局偏移
Chromium 124390 × 844新 baseline 待确认

截图比对是如何进行的

确定性的捕获、诚实的 diff、显式的审阅

一个视觉 diff 工具有多有用,取决于它产出的 diff 在信号与噪声之间的比值。ScanU 的捕获流水线刻意如此设计:两次运行之间唯一改变的,就是您真正改过的东西 — 而不是它周围那一圈字体、动画、时间戳和广告位布景。

每一次运行都是一条三步流水线。首先,ScanU 会在每一个引擎中 打开待测页面或组件,等待一组已配置的 readiness 信号 — 字体已加载、图片已解码、网络已安静、存在一个 data-ready 属性,或者自定义的 JavaScript 探针 成功 — 然后把所有 CSS 动画固定在最终帧。第二步,它在同一 次运行中按每一个已配置的视口捕获页面;对长页面使用 scroll-stitching,这样您拿到的是完整的产物,而不是首屏。 最后一步,它把捕获结果和已接受的 baseline 做比对,并为每 一对(浏览器,视口)给出结果:完全一致、在容差范围内, 或存在显著差异。

比对本身并不是一个朴素的像素 diff。ScanU 采用了一种感知 模型,它理解 anti-aliasing、sub-pixel 渲染以及色彩空间的 细微漂移,因此 WebKit 里的一处 kerning 小怪癖不会被当成 bug。它也支持对那些您明知每次加载都会变化的区域设置显式 遮罩 — 实时数据、相对时间戳、随机化内容、走马灯 — 这些 区域对 diff 的贡献会是零噪声。

当真正的改动出现时,平台会呈现三个同步的面板:baseline、 当前截图和一个高亮出位移的 diff overlay。每一次改动都会 记录一个可审阅的决定 — 接受、拒绝或更新 baseline — 这个 决定随后会成为项目历史的一部分。没有静默的漂移,也没有 自动接受:baseline 只有在有权限的人明确表态时才会移动。

同一条流水线可以从三种入口驱动:针对线上 URL(生产、 staging、PR 预览)、针对一份静态站点或构建产物包、针对 Storybook 风格沙箱中的组件预览。多数团队会用第一种作为 release gate,第二种用于部署产物,第三种用于设计系统工作 — 它们共同喂养同一份 baseline 存储。

Baseline · 当前 · Diff — 审阅界面的三个面板
Baseline

该页面在这个浏览器、这个视口上最近一次被接受的渲染。在 reviewer 显式更新之前保持冻结。

当前

本次运行捕获到的截图。相同的 fixture、相同的 timing hook、相同的视口 — 只有被测代码发生了变化。

Diff overlay

以琥珀色高亮的区域标示出当前渲染在哪些地方超出了配置的容差,与 baseline 不一致。

ScanU 适合谁

把 UI 视为发布门关的团队

ScanU 为那些以「用户看到了什么」为评判标准的团队而生。这个群体比听起来更广 — 不止是设计师与前端工程师,还包括任何发布决策依赖于 UI 在特定浏览器、特定视口、在特定一天正确呈现的人。

前端与全栈工程师把 ScanU 用作 pull request 流水线的最后一道:build 是绿的,单元测试通过,端到端套件 也满意 — 然后由一次视觉 diff 要么确认改动与设计师意图一致, 要么暴露出一条光看 patch 根本发现不了的回归。结果是: 上线后救火的时间变少,用在真正推动产品前进的工作上的时间 更多。

QA 工程师得到的是一层无需重写选择器就能 扩展的测试。把一个新页面纳入视觉套件,只需要一个 URL 加 一份 baseline — 不再是一周脆弱的 XPath。捕获 UI 回归的 那一层,成为测试金字塔的一等成员,而不再是上线之后由用户 通过 Slack 发来的一条事后消息。

设计系统与平台团队对库里每一个组件沙箱 跑 ScanU。一旦某个 token 变更,依赖它的每一个组件都会自动 得到一次视觉审计。一旦某个组件被重构,下游消费者会看到 一份可审阅的 diff — 早于新版本的落地。这正是一个设计系统 避免在岁月里悄悄积累回归的方式。

产品与工程负责人把审阅界面当作证据来使用。 Release notes 可以附上每次发布对应的已接受视觉 diff 链接。 事故回顾可以引用某条回归到底是被拦住、被漏过,还是被 明确接受。久而久之,平台变成了一份记录 — 记下 UI 如何 演进,以及谁在每一步签了字。

使用场景

从代运营交付到设计系统治理

同一套平台可以服务形态非常不同的工作流。共同的那条线索是:这些团队都按照他们的客户真正在意的节奏在交付 UI,也都需要一种方式 — 一眼就能看出 UI 是否依旧是它应有的样子。

不管团队形态如何,ScanU 通常会因为下面五种原因之一被引入: 一次最近发生的线上事故,而视觉回归本能拦下;一次设计系统 迁移,其影响面无法靠人工审计;一条 CI/CD 工作流需要比 「测试通过」更丰富的 release gate;一份面向客户的交付物 需要「前后对比」的证据;又或是一种合规立场,倾向于使用 欧盟托管的测试工具。共用的一套 CI/CD 集成 承载着这五种场景 — 无论触发来自 PR、merge 还是夜间定时, 同一条流水线都会完成工作。

数字代理商

每周向客户交付一份按站点的视觉差异报告。在每一个 sprint、每一个合同约定的浏览器上,一份报告就能给出「前后对比」证据。

QA 与发布工程

为测试金字塔补上视觉这一层。新页面通过一个 URL 加一份 baseline 加入套件 — 没有脆弱的选择器,也没有需要维护的自制截图脚本。

前端团队

截图 diff 内联出现在 pull request 上。布局回归在 code review 里被拦下,不再落到线上,也不再变成客户在 Slack 上的一条反馈。

初创与成长期团队

在不弄坏首页的前提下快速迭代。一张确定性的安全网,在改版、A/B 测试和 token 迁移之中保住营销站点的像素稳定。

设计系统

对每一个组件沙箱进行视觉覆盖。token 变更、主题切换与组件重构,都会在库发布之前自动过一轮审计。

响应式设计

在同一次运行中验证移动端、平板与桌面端的断点。响应式测试不再是抓截图的苦差,而成为一个审阅步骤。

CI/CD 与发布信心

一个您的流水线已经会读懂的视觉信号

发布信心并不是一种感觉,它是一条如实讲述即将上线之物的流水线 — 包括那些您的 test runner 根本无从置喙的部分。ScanU 以 status check、PR 评论和可存档产物三种身份,接入这条流水线,任何一次事故回顾都能随手引用。

集成模型刻意做得无聊:ScanU 的捕获步骤,和您现有的测试跑 在同一个 job 里;它把 pass、review-needed 或 fail 报到 PR 上;这个状态被当作任何其他必需检查来对待。团队本来就知道 如何从流水线里读出绿、黄、红 — 把一个视觉回归检查织进 这套节奏,在不要求新的思维模型的前提下,补上一层安全感。

在底层,ScanU 针对现代 CI/CD 的现实而设计:

  • 确定性运行。同一个页面,在同一个浏览器、 同一个视口上,配上同一组稳定化 hook,产出的字节是相同的。 这正是 diffing 能说明问题的前提。
  • 并行。浏览器与视口并行运行,大矩阵会分摊 到多个 runner 上。一个 60 页站点在三种引擎、三种视口上 跑,是几分钟的检查,而不是一场通宵 batch。
  • 诚实的状态。新页面会进入「baseline 待确认」 状态,而不是 fail。已知会抖动的区域会被遮罩,而不是忽略。 一处真正的回归,是一条带链接的审阅请求 — 不会是一整片 像素噪声墙。
  • 对预览环境友好。PR 预览会拥有自己的临时 baseline,只在 merge 并被接受之后才晋升为主 baseline。 不同分支之间不会互相渗透。
  • 以产物为先。每一次运行都会以稳定的 URL 保存 baseline、当前截图与 diff 的完整图像集合。Release notes、事故报告与审计,都能精确地引用当时上线的视觉状态。

分步的 CI/CD 集成指南 给出了 GitHub Actions、GitLab CI、Bitbucket Pipelines 以及 自建 runner 的具体接线方式。常规场景是多加一个 job;进阶 场景则是一套环境矩阵,让 baseline 在分阶段发布中逐步 向前推进。

GDPR · 欧盟托管 · 隐私

一个尊重您数据所在之地的测试平台

测试产物本身就是数据。截图可能在不经意间把页面上的个人信息一并保留下来 — 导航栏里的姓名、个人菜单里的邮箱、确认页面上的订单号。一个负责任的视觉测试平台,会用对待生产数据同等的谨慎对待这些产物。

ScanU 在欧盟构建并托管,存储与处理都位于欧盟运营的基础 设施之上。对那些在数据驻留方面有承诺的团队 — 受监管行业、 公共部门客户、对隐私敏感的 B2B 客户 — 这通常是采购的硬 要求,而不是偏好。ScanU 的设计目标,正是在不牺牲产品 能力的前提下满足这一要求。

由这一立场出发,衍生出几条刻意的设计选择:

  • 数据最小化。平台只保存截图,以及为 生成 diff 所需的元数据;不会吸走对 diff 没有贡献的 页面源码、cookie 或请求体。
  • 敏感场景下默认遮罩。已知会承载个人信息 的字段 — 账户头部、邮箱地址、订单号 — 可以声明为遮罩 区域,这样捕获到的图像在写入存储时就已经是脱敏后的 状态。
  • 访问控制。谁能看到一份 diff,本身就是一种 一等权限。Reviewer、贡献者与外部审计员各有清晰的角色, 每一次 baseline 更新都会连同授权人一并记录。
  • 分包处理透明。分包处理方清单是公开且 版本化的。没有任何隐藏的 analytics 流水线能看见您的 截图。
  • 由您决定保留期。baseline 与 diff 产物会 按您配置的时间窗保留;旧的运行会自动到期,而不是悄悄 堆积。

完整的隐私立场 — 分包处理方、区域托管、DPA、保留时间窗 — 请参阅 GDPR 与数据处理文档。对大多数团队而言,简短版已经够用:您的截图留在欧盟, 平台正是为了让它们留在那里而构建的。

差异所在

为什么 ScanU 不是又一个通用测试工具

视觉测试是一个拥挤的品类。让 ScanU 与众不同的,有相当一部分是它拒绝去做的事 — 它不抄的近路、它不愿意往审阅队列里塞的噪声、以及它对一个 diff 应当如何被审阅所持的立场。

通用截图工具往往以「贴在 test runner 上的一个 library」的 形态发布,然后把 timing、字体、动画,以及「一像素的偏移 到底是 bug 还是舍入误差」这道永恒之问,统统留给您去搏斗。 ScanU 的态度更硬气:如果平台无法产出确定性的捕获,那是 平台自己的问题 — 不是您的。所以 readiness 信号、字体加载、 动画固定、scroll-stitching、感知 diff 与遮罩区域,都是 内建能力,而不是事后挂上去的。

定价遵循同样的逻辑。对一个体面的团队 — 几十个页面、三种 引擎、三种视口、一条流水线 — 视觉覆盖的成本不该是要在 预算会上特别辩护的一个条目。完整的 定价页 以清楚的数字写明:每一档包含什么、什么算一次 run、上限 在哪里。对于只想发布出他们设计的那版 UI 的团队,我们 没有定制报价。

有立场的捕获,不只是一个 library

稳定化、字体 readiness、动画固定、scroll-stitching 都是平台能力 — 不是要您在自家代码库里维护的零碎片段。

感知 diff,而不是像素计数器

diff 引擎理解 anti-aliasing 与 sub-pixel 渲染,因此那些不可见的微小偏移不会淹没掉真正重要的回归。

审阅是一等动作

只有有权限的人接受了变更,baseline 才会移动。每一次更新都可被审计。没有静默的漂移。

默认欧盟托管

截图、元数据与审阅历史都生活在欧盟基础设施上。对那些必须坚守这一立场的团队,无需额外开关。

ScanU 能拦下什么

那些悄悄上到生产环境的回归

视觉回归平台所拦下的多数 bug 并不奇异。它们是那些细小的、不戏剧的、容易被忽略的破损 — 因为没有任何 assertion 在专门盯着它们,才会从 reviewer 眼前溜过去。下面是 ScanU 专门为之设计要让其浮出水面的若干类别的样本。

视觉 bug 会聚集在少数几种反复出现的模式中。每一种孤立 来看都显得不起眼;每一种都可能在没有一个红色测试的情况下 上到生产。所有这些,都会在下一次 ScanU 运行中以 diff 的 形式浮现 — 早于 PR 被合入。

  • spacing 偏移。一个 gap utility 从 16 像素 涨到 20 像素;原本舒展的卡片栅格,在移动端突然显得拥挤。 HTML 没变;布局变了。
  • 部署后的版式破裂。build 流水线一次升级, 把负责给 backdrop-filter 自动加前缀的 PostCSS 插件丢掉了。Safari 上,一条原本带模糊的导航栏 突然变得不透明。
  • 由优先级偏移引发的 CSS 回归。Tailwind class 顺序变化、一层新的 utility,或者一次重构过的级联, 会悄悄改变哪一条规则胜出 — 标题在若干页面上突然失去了 它的强调色。
  • 响应式断点失灵。一项新的导航条目在平板 断点处溢出、换到第二行,把 hero 在 iPad 与大屏 Android 上推到首屏之下。
  • 浏览器之间的渲染差异。一处依赖 text-wrap: balance 的设计,在 Chrome 上 干净利落,在尚未发布该特性的引擎上仍然不平衡。没有 跨浏览器检查时,这在开发机上是不可见的。
  • 字体与排印漂移。字体供应商替换了一个 subset,字重整体偏了一百克,营销页面的每一段正文都 重新回流了四分之一行。文本密度变了;仓库里没有任何 diff 能解释它。
  • 第三方组件回归。同意横幅供应商发了一次 更新,在深色主题下让「接受」按钮变得不可见。转化率下降; 而您的测试依然是绿色的。
  • 设计系统漂移。一个按钮组件改动了 focus ring 的偏移。二十个消费者依赖这个组件;二十个页面看上 去都微妙地不同了。没人会手工去审阅二十个页面。
  • 逃到生产的视觉 bug。以上任何一种,乘以 那些在某一周里没人打开的路由与断点,再乘以团队里没人 天天用的浏览器。

功能概览 更详细地讲述检测原语 — 感知容差、遮罩区域、scroll 捕获、baseline 分支 — 并展示每一项原语对应上面哪一类问题。 对大多数团队而言,简短版本是这样:只要视觉上有变化, ScanU 就会注意到;若是预期内,一键接受;若不是,您已经 比其他人都更早地拦下了它。

FAQ

在选用一款视觉测试工具之前值得回答的问题

下一步

在用户察觉之前拦下回归

判断视觉回归测试是否属于您流水线的最快方式,就是拿一个您已经在交付的项目跑一次。ScanU 起步免费,接入到一个已有 CI job 只要几分钟,在下一次提交时就能产出第一份 diff。

多数团队会从一个高流量页面开始,在三种浏览器、三种视口上跑 起来。这已经足够看见平台运转,也足够拦下您当前测试套件本 会漏掉的下一处真正回归。

从那里开始,范围会随着团队信心增长:更多页面、更多断点、 设计系统层面的组件级覆盖、每一个 pull request 上的强制 status check。无需预约通话,无需定制 onboarding,也没有 最低承诺。