mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
fix(cli): contrast gate judges only readable content; examples pass check
Three filters keep the escalated contrast gate honest, each surfaced by running check across the registry examples: - data-layout-ignore (the layout audit's existing decorative opt-out) now also excludes set-dressing text from the contrast audit — one vocabulary for 'not copy a viewer must read'. - Text that has (nearly) left the canvas is skipped: sampling a clamped off-canvas box reads border pixels and produced the classic false white-on-white (a cursor exiting the frame). - Contrast failures follow the same persistence rule as layout findings: observed at a single sample of a multi-sample sweep demotes to warning; held failures gate. Example fixes the sweep exposed: motion-blur's 21 deliberately-dim rail labels are marked decorative (its vivid labels pass on their own); nyt-graph's subtitle and source-note adopt the gate's suggested compliant gray; decision-tree's declared duration drops 15s to 10s — its content ends at ~9.5s and every render shipped a blank white tail.
This commit is contained in:
@@ -1112,6 +1112,52 @@ describe("check pipeline", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("contrast persistence", () => {
|
||||
it("demotes a single-sample contrast failure to warning but gates held failures", async () => {
|
||||
const driver = fakeDriver({
|
||||
collectContrast: vi.fn(async (time: number) => ({
|
||||
entries: [
|
||||
// #hero fails at every sample: held, stays an error.
|
||||
contrastEntry({ time, selector: "#hero" }),
|
||||
// #entrance fails only at the first sample (mid-entrance): demoted.
|
||||
...(time < 1
|
||||
? [contrastEntry({ time, selector: "#entrance", text: "Fading in" })]
|
||||
: [
|
||||
contrastEntry({
|
||||
time,
|
||||
selector: "#entrance",
|
||||
text: "Fading in",
|
||||
ratio: 8,
|
||||
wcagAA: true,
|
||||
}),
|
||||
]),
|
||||
],
|
||||
pngBase64: PNG_BASE64,
|
||||
})),
|
||||
});
|
||||
const { report } = await runScenario(driver);
|
||||
|
||||
const bySelector = new Map(
|
||||
report.contrast.findings.map((finding) => [finding.selector, finding.severity]),
|
||||
);
|
||||
expect(bySelector.get("#hero")).toBe("error");
|
||||
expect(bySelector.get("#entrance")).toBe("warning");
|
||||
expect(checkExitCode(report)).toBe(1);
|
||||
});
|
||||
|
||||
it("keeps full severity when only one sample time exists", async () => {
|
||||
const driver = fakeDriver({
|
||||
collectContrast: vi.fn(async (time: number) => ({
|
||||
entries: [contrastEntry({ time })],
|
||||
pngBase64: PNG_BASE64,
|
||||
})),
|
||||
});
|
||||
const { report } = await runScenario(driver, { samples: 1, at: [2] });
|
||||
|
||||
expect(report.contrast.findings[0]?.severity).toBe("error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("check report telemetry", () => {
|
||||
it("reports one clean run with every gate and sampled-point count", async () => {
|
||||
const motion: MotionSpecResolution = {
|
||||
|
||||
Reference in New Issue
Block a user