关于 visual-regression-testing
用于检查原生 Canvas 渲染与序列化 foreignObject 渲染之间像素一致性的测试技能,帮助开发者编写针对性 CSS 回归测试并排查预览差异。
主要功能
- 01画布间直接比较
- 02基线 PNG 快照检查
- 03可配置像素差异阈值
- 04最小化 CSS 复现测试
- 05原生 API 可用性保护
适用场景
- 01检查 CSS 功能的渲染一致性
- 02排查预览缩略图差异
- 03验证序列化器更改
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ editframe/ skills --skill visual-regression-testing安装后在 Claude Code 中输入/visual-regression-testing确认已加载。
详细介绍
视觉回归测试
两条渲染路径必须生成完全相同的像素。原生画布路径(通过 drawElementImage 调用 renderToImageNative)直接通过 Blink 渲染实时 DOM,是基准结果。foreignObject 路径(captureTimelineToDataUri)会将 DOM 序列化为 SVG <foreignObject> 中的 XHTML。两者之间的任何差异都是序列化器缺陷。
工具函数
elements/packages/elements/test/visualRegressionUtils.ts 提供了测试 API。主要函数:
// Compare two canvases directly — returns diffPercentage, no baseline file needed
compareTwoCanvases(canvas1, canvas2, testName, comparisonName, options)
expectCanvasesToMatch(canvas1, canvas2, testName, comparisonName, options)
// Compare a canvas against a stored baseline PNG
assertCanvasSnapshot(canvas, testName, snapshotName, options)
expectCanvasToMatchSnapshot(source, testName, snapshotName, options)
options.acceptableDiffPercentage(默认值为 1.0)用于控制测试通过或失败。进行严格的对齐检查时,可设为 0 到 0.5。
标准比较模式
import { captureTimelineToDataUri } from "./rendering/serializeTimelineDirect.js";
import { loadImageFromDataUri } from "./rendering/loadImage.js";
import { renderToImageNative } from "./rendering/renderToImageNative.js";
import { isNativeCanvasApiAvailable } from "./previewSettings.js";
import { expectCanvasesToMatch } from "../../test/visualRegressionUtils.js";
async function captureForComparison(tg: EFTimegroup, W: number, H: number) {
// foreignObject path
const dataUri = await captureTimelineToDataUri(tg, W, H, { canvasScale: 1, timeMs: 0 });
const img = await loadImageFromDataUri(dataUri);
const fc = document.createElement("canvas");
fc.width = W; fc.height = H;
fc.getContext("2d")!.drawImage(img, 0, 0);
// native path (ground truth — Blink renders the live DOM)
const nc = await renderToImageNative(tg, W, H, { skipDprScaling: true });
return { foreignCanvas: fc, nativeCanvas: nc };
}
原生路径测试必须始终使用 isNativeCanvasApiAvailable() 进行保护,因为 WICG drawElementImage API 仅适用于 Chromium。
编写最小复现测试
最小复现应隔离单个 CSS 属性。测试应当在修复前失败,修复后通过。
it("text-shadow from inline style", async () => {
if (!isNativeCanvasApiAvailable()) return;
const tg = document.createElement("ef-timegroup") as EFTimegroup;
tg.style.cssText = "width:400px;height:200px;background:#000;position:relative;";
const el = document.createElement("div");
el.style.cssText = "position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);"
+ "font-size:60px;font-weight:900;color:white;text-shadow:0 0 40px red;";
el.textContent = "GLOW";
tg.appendChild(el);
document.body.appendChild(tg);
await tg.updateComplete;
const { foreignCanvas, nativeCanvas } = await captureForComparison(tg, 400, 200);
await expectCanvasesToMatch(foreignCanvas, nativeCanvas, "canvasDomParity", "text-shadow-inline", {
acceptableDiffPercentage: 0.5,
});
tg.remove();
});
快照文件
差异 PNG 文件会写入 elements/test-assets/test/__snapshots__/<testName>/。打开文件即可直观检查哪些像素存在差异。
序列化器属性
foreignObject 序列化器会从 elements/packages/elements/src/preview/rendering/serializeTimelineDirect.ts 中的 SERIALIZED_STYLE_PROPERTIES 获取计算样式。如果此处缺少某个属性,那么在通过 animation:none 冻结动画时,该属性就会丢失。当某项 CSS 功能出现对齐差异时,请先检查此列表。
何时使用此技能
- 为 CSS 功能差异编写新的对齐测试
- 调查预览缩略图与 DOM 实际显示内容不一致的报告
- 修改
serializeTimelineDirect.ts后,验证是否引入了新的回归问题