为一个不是我写的系统做 QA:Saleor

我围绕一个本地固定版本的 Saleor 环境搭建了独立、基于风险的 QA 框架,记录 4 个缺陷或候选问题,并将 VFX 经验中的感知差分方法用于跨浏览器视觉回归。

独立 QA 案例 · Saleor 3.23(本地固定版本)· 特定时点结果 · 发现已记录,尚未向上游提交

测试/QA探索性测试PlaywrightpytestGraphQL视觉回归WCAGi18n

自动化检查

32 · 30 通过

23 项 GraphQL(pytest)+ 9 项仪表板(Playwright),含 2 个记录缺陷的 xfail——顺序无关,一份合并的 Allure 报告,经 CI 验证

已记录发现

4 个发现

针对本地固定环境记录 4 个缺陷或候选问题;其中 2 个有严格 xfail 回归检查支撑;均未向上游提交

恶意输入下的 API

0 服务器错误

8 例恶意输入从不 500;查询成本与批量限制均生效——如实报告为确实健壮

视觉基线

40

36 条覆盖 chromium/firefox/webkit + 4 条 chromium 专属 i18n 基线;重跑差异为 0 像素

植入回归

在 0.01 处抓到

0.2px 字体度量漂移触发门禁——5,366 个差异像素,比率 0.01 超过 0.005 上限

发现的 i18n 缺口

1 处真实缺口

“Catalog”/“Fulfillment” 在 zh-Hans / zh-Hant / ko 下均以英文显示

我做了什么

我拿 Saleor——一个我并没有参与开发的大型开源电商平台(Python/Django/GraphQL)——从外部为它搭起了一套独立 QA 框架。测试通过 GraphQL API 和管理后台驱动运行中的应用,全程不复制、不修改 Saleor 自身代码。

它分两块。第一块是一套以风险为先的功能测试框架——32 项自动化检查覆盖 API 健壮性、访问控制和结账,并在本地固定环境中记录了 4 个缺陷或候选问题。第二块是跨浏览器视觉回归工具,它抓到了一处植入回归,并发现了一处本地化缺口。

针对本地 Saleor 3.23(固定的预构建镜像)从外部驱动。两条通道——pytest(GraphQL)与 Playwright(仪表板 + axe)——在 CI 中输出一份合并的 Allure 报告;视觉套件使用独立配置运行。以下每一项都对应仓库中的一件工件:基于风险的测试计划、可追溯性与覆盖图、4 份起草的缺陷报告,以及感知差分方法记录。

测试框架 + 已记录发现

我从一份以风险为先的计划入手:给 14 个领域按“出问题的可能性”和“出了会有多严重”打分。资金流转和访问控制排在最前,而 i18n、视觉回归和无障碍则被当作一流的重点来对待。整套功能测试的分量,压在四项探针上。

健壮性测试套件——以及一个诚实的正向结论

一组 8 例恶意输入(非法 id 与枚举、first 参数的极端值、深度嵌套与别名查询)从不返回 500——每个失败都是一个带类型的 GraphQL 错误。查询成本与批量大小限制均生效。Saleor 健壮之处,我如实报告为健壮,并将其固化为通过的测试,而非编造缺陷。

通过权限受限的 App 令牌做 RBAC

一个仅具 MANAGE_ORDERS 的 App 令牌证明了权限边界——它能读取订单,却不能创建产品——无需脆弱的员工邀请流程。匿名与员工的门控,以及 tokenCreate(有效与错误凭据)也一并覆盖。

把结账作为 GraphQL 流程

saleor-platform 不含店面,因此客户旅程通过 API 演练:checkoutCreate → 添加行 → 地址 → 配送方式 → 合计,每个结账、产品与受限 App 都在各自的测试内创建并拆除。

以真实系统而非假设为准

预期结果来自运行中的应用。一项负向输入测试断言真实的 HTTP 400 校验契约——上游研究预测为 200;运行中的应用并不一致,把测试建立在系统之上正好抓到了这处差异。

4 个已记录的缺陷或候选问题

一次有结构的探索性测试,针对本地固定版本 Saleor 3.23 产出了 4 份有证据支撑的发现报告。它们是作品集中的缺陷或候选问题,不是上游已确认缺陷,且均未提交上游;提交前需先去重并遵循 Saleor 的 CLA 或安全披露流程。

结账元数据无上限——且可匿名写入

Checkout 上的公开 updateMetadata 映射为 no_permissions,因此一个未认证的客户端接受了 1 MB 的值、2,000 个键和一个 100,000 字符的键,且无任何错误——一个低成本的存储/DoS 向量。一个严格 xfail 回归断言期望的 100 项上限,一旦限制落地便会告警。

关联 Saleor #19210 · 已复现(完整)· 严格 xfail

结账行列表无上限

checkoutLinesAdd 在一次调用中接受了 73 个不同的变体行(仅受种子目录数量限制),且一个 CheckoutLine 接受了 1 MB 的元数据值——而每行数量被正确地限制在 50。第二个严格 xfail 固化了期望的列表长度上限。

关联 Saleor #19209 · 已复现(完整)· 严格 xfail

仪表板 WCAG 障碍

axe-core 门禁在产品创建表单上发现 12 个无可读名称的图标按钮、缺失的文档 lang、被禁用的双指缩放,以及 5 个低于 AA 的对比度节点。高置信度项优先呈现;框架生成的 ARIA role 节点在提交前标注为需人工甄别。

新发现 · axe 证据 · 提交前先去重

X-Forwarded-For 信任 → 登录限流绕过

get_client_ip 信任最左侧、由客户端提供的 X-Forwarded-For 值,因此按 IP 的登录限流键由攻击者掌控、可轮换。已对照 3.23.5 源码核实并在线上确认限流;一次干净的端到端伪造需要带代理的部署,因此这是部分(源码核实)复现,而非完整的本地利用。

关联 Saleor #19203 · 源码核实 · 安全披露

一个有意思的副作用:我自己反复的 tokenCreate 探测触发了 Saleor 按 IP 的登录限流(LOGIN_ATTEMPT_DELAYED)。我没有硬扛,而是让 E2E 套件只登录一次进入共享 storageState、并让 API 夹具对限流做重试——一个真实的可测试性约束,塑造了框架的认证复用架构。

视觉回归 + 感知方法论

截图是最容易的部分。一套视觉回归工具真正值钱的地方,在于它是不是只在“人看了也会说这是回归”的时候才报错,而不会被渲染噪声、字体抖动或重新画出来的时间戳带偏。真正的交付物是这份判断力,基线和那张被抓到的回归截图,都是从它衍生出来的。

跨浏览器 / 主题 / 视口矩阵

40 条已提交基线:三个高密度管理界面(产品编辑器、配置、订单详情)× 桌面 1280×800 + 移动 375×812 × 浅色 + 深色 × chromium/firefox/webkit,外加一次 4 语言的 i18n 遍历。它填补了 saleor-dashboard 自带设置留下的空白——仅 Chrome、无基线、无 Firefox/WebKit、无移动端。

确定性杠杆

固定 deviceScaleFactor 与视口,关闭动画与光标,等待 document.fonts.ready,将时区/语言锁定为 UTC/en-US,在首次导航前于 localStorage 设置主题与 UI 语言,并遮罩动态区域(id、日期、金额、地址)。回报是:连续两次干净运行差异为 0 像素——这正是门禁得以收紧的前提。

按失败模式选择度量

pixelmatch 是零依赖默认项;若出现确定性杠杆无法消除的伽马/抗锯齿漂移,SSIM 是记录在案的升级路径;当套件运行时长要紧时可换用 odiff;而接入托管服务(Argos / Percy / Lost Pixel)做并排 PR 评审只是更换报告器,而非重写。

源自 VFX 合成

在电影 VFX 工作中,我负责像素级 QC——判断哪一处像素差异是缺陷,哪一处只是媒介的噪声底。这套工具正是把那套判断自动化,用于 Web UI。

阈值与比率的陷阱

Playwright 的 toHaveScreenshot 暴露两个相互独立的容差,而它们的盲点恰好相反。一次覆盖整页的 3° 色相旋转在阈值 0.15 下通过——每个像素的 YIQ 差都低于逐像素阈值,于是 pixelmatch 计得零个不同像素。仅移动标题 1px 也通过——像素太少,跨不过比率。可靠触发失败的是 0.2px 的字距漂移:它移动了每个字形边缘,点亮了文本密集屏幕约 1% 的像素(比率 0.01,超过 0.005 门禁)。要抓色彩漂移,你必须调低阈值;要抓布局位移,你必须调低比率并优先选择文本密集目标。这一相互作用正是核心的方法论要点。

证据——被抓到的植入回归

为证明门禁仍然咬得住,我向最密集的界面——产品编辑器——注入一处细微的字体度量 + 色彩 + 1px 漂移,然后重跑套件。它如设计般在 5,366 个差异像素处失败(比率 0.01,超过 0.005 门禁),并标记出表单上每一个位移的标签。

预期——已提交基线
Saleor 产品创建编辑器,已提交的干净基线:清晰的黑色表单标签与控件。
Saleor 产品创建编辑器上已提交的干净基线——视觉套件设卡的最密集界面,也是每次运行据以做差分的参照。自托管;采集于本地 Saleor 3.23,2026-07-04。

这里展示的是干净基线,而非原始的 pixelmatch 差分图。差分帧是一张只标出变化像素的暗化伪彩叠加图,脱离左右对照便读作视觉噪声——因此真正承载证据的是门禁据以判定的数字,即上文那次 5,366 像素(比率 0.01)的失败,而不是那张图。正是它让构建变红。

一处真实的 i18n 缺口,以视觉方式暴露

同一次语言遍历带出了一个真实发现:“Catalog” 与 “Fulfillment” 在 zh-Hans、zh-Hant 与 ko 下均以英文显示,“Translations” 在 zh-Hans 与 ko 下也保持英文——以逐语言基线与一份记录的标签报告捕获。在这些宽度下无裁切或溢出。这是仪表板翻译完整度的缺口(提交前先在上游去重),而非布局缺陷——是一处 QA 一致性发现,而非编造的缺陷。

诚实的说明

该框架驱动的是本地固定版本 Saleor 3.23 环境,因此每项结果都是针对该构建的特定时点结论。4 个缺陷或候选问题均已记录为草报告,但未提交、也未获上游确认;提交前需先去重,并遵循 Saleor 的 CLA 或安全披露流程。BR-004 是源码审查与限流现象确认,而非完整的本地利用。在一组恶意输入测试下,API 未出现 500,成本与批量限制均生效,这是一项如实记录的正向结果。

最稳妥的解读:这是围绕一个陌生生产代码库搭建的独立、基于风险的 QA——一套可用框架、4 个已记录的缺陷或候选问题,以及能抓到植入回归的感知差分工具——而非安全审计或上游贡献记录。

下一步

去重并按 Saleor 的流程提交起草的报告(X-Forwarded-For 发现走安全披露路径);把 API 通道扩展到优惠券与支付并发以及订单履约状态机;在固定的 Playwright 镜像内引导生成 Linux CI 基线,并随确定性得到验证把门禁向每屏 0.1 / 0.001 收紧;并植入真实翻译,把 i18n 探针升级为完整的语言矩阵。

定位为对陌生系统的外部、基于风险的 QA——ISTQB 风格的测试设计、贴近 ITIL 的缺陷报告,以及自 VFX 合成沿用而来的感知差分方法。

查看 / 运行

基于风险的计划、可追溯性与覆盖图、四份起草的缺陷报告、合并的 Allure 报告,以及带植入回归证据的视觉回归工具,都在仓库中。欢迎索取讲解或对报告的只读查看。

索取讲解