diff --git a/packages/cli/src/commands/check.test.ts b/packages/cli/src/commands/check.test.ts
index 1cbf482ea2..970fdb65a1 100644
--- a/packages/cli/src/commands/check.test.ts
+++ b/packages/cli/src/commands/check.test.ts
@@ -2841,6 +2841,20 @@ describe("check pipeline", () => {
expect(checkExitCode(strict.report)).toBe(0);
});
+ it("seeks each craft frame with WebGL draws on, so a redrawn canvas reads as moving", async () => {
+ const driver = craftDriver();
+ await runScenario(driver);
+ const painted = new Set(
+ vi
+ .mocked(driver.seekGeometry)
+ .mock.calls.flatMap(([time, seek]) => (seek?.draws ? [time] : [])),
+ );
+ const sampled = vi.mocked(driver.collectCraftFrame!).mock.calls.map(([time]) => time);
+
+ expect(sampled.length).toBeGreaterThan(0);
+ expect(sampled.every((time) => painted.has(time))).toBe(true);
+ });
+
it("re-measures a short line between dense samples before judging it", async () => {
const driver = craftDriver();
const { report } = await runScenario(driver);
diff --git a/packages/cli/src/utils/checkBrowser.chromium.test.ts b/packages/cli/src/utils/checkBrowser.chromium.test.ts
index b218ea1648..144380970b 100644
--- a/packages/cli/src/utils/checkBrowser.chromium.test.ts
+++ b/packages/cli/src/utils/checkBrowser.chromium.test.ts
@@ -3,7 +3,7 @@ import puppeteer, { type Browser, type Page } from "puppeteer-core";
import { createRequire } from "node:module";
import { resolve } from "node:path";
import { measuredInMotion, readContrastMotion } from "./contrastMotion.js";
-import { collectSeekClock } from "./checkBrowser.js";
+import { collectSeekClock, createPageDriver } from "./checkBrowser.js";
const executablePath = process.env.PUPPETEER_EXECUTABLE_PATH;
@@ -119,6 +119,24 @@ describe.runIf(executablePath)("collectSeekClock in Chromium", () => {
await page.close();
});
+ it("skips a seek's WebGL draws unless the caller asks to paint them", async () => {
+ page = await browser.newPage();
+ await page.setContent(``);
+ const driver = createPageDriver(page);
+ const drawn = () => page.evaluate(() => Reflect.get(window, "draws"));
+
+ await driver.seekGeometry(1);
+ expect(await drawn()).toBe(0);
+ await driver.seekGeometry(2, { draws: true });
+ expect(await drawn()).toBe(1);
+ await page.close();
+ });
+
it("reports nothing on a page with no animations", async () => {
page = await browser.newPage();
await page.setContent("
Hello
");
diff --git a/packages/cli/src/utils/checkBrowser.ts b/packages/cli/src/utils/checkBrowser.ts
index 924a976c8e..98ee3b7adc 100644
--- a/packages/cli/src/utils/checkBrowser.ts
+++ b/packages/cli/src/utils/checkBrowser.ts
@@ -969,9 +969,12 @@ export function createPageDriver(
screenshotFollows ? SCREENSHOT_SEEK_OPTIONS : AUDIT_SEEK_OPTIONS,
);
},
- seekGeometry: async (time) => {
+ seekGeometry: async (time, { draws = false } = {}) => {
clock.at(time);
- await seekCompositionTimeline(page, time, DENSE_GEOMETRY_SEEK_OPTIONS);
+ const options = draws
+ ? { ...DENSE_GEOMETRY_SEEK_OPTIONS, skipWebglDraws: false }
+ : DENSE_GEOMETRY_SEEK_OPTIONS;
+ await seekCompositionTimeline(page, time, options);
},
collectLayout: (time, tolerance, layout) => collectLayout(page, time, tolerance, layout),
collectOverlap: (time) => collectOverlap(page, time),
diff --git a/packages/cli/src/utils/checkPipeline.ts b/packages/cli/src/utils/checkPipeline.ts
index 1730884676..7ee085a0b2 100644
--- a/packages/cli/src/utils/checkPipeline.ts
+++ b/packages/cli/src/utils/checkPipeline.ts
@@ -759,7 +759,7 @@ async function collectMotionOverlapSamples(
const craftTimes = buildCraftSampleTimes(grid.duration).filter((time) => time < grid.duration);
for (const [index, time] of craftTimes.entries()) {
progress(`craft frame at ${time}s`);
- await driver.seekGeometry(time);
+ await driver.seekGeometry(time, { draws: true });
collected.craftFrames.push(...(await collectCraftFrame(driver, time)));
progress();
report("craft", index + 1, craftTimes.length);
@@ -1583,7 +1583,7 @@ export async function runAuditGrid(
noTimeline || sweepFindings.length > 0 || wholeCompositionKeepsMoving(plan.assertions),
},
async (time) => {
- await driver.seekGeometry(time);
+ await driver.seekGeometry(time, { draws: true });
if (!driver.collectCraftFrame) throw new Error("craft sampling unavailable");
return driver.collectCraftFrame(time);
},
diff --git a/packages/cli/src/utils/checkTypes.ts b/packages/cli/src/utils/checkTypes.ts
index a01956b3b3..33ef1c8141 100644
--- a/packages/cli/src/utils/checkTypes.ts
+++ b/packages/cli/src/utils/checkTypes.ts
@@ -262,8 +262,8 @@ export interface CheckAuditDriver {
getCanvas(): Promise