import { expect, test, type Page } from "@playwright/test"; import { expectToolcraftCanvasBackingPixelsForRenderScale } from "./browser-render-scale-evidence"; import { createToolcraftPerformanceCanvasQualityGuard } from "./performance-canvas-quality-guard"; const canvasSelector = "[data-quality-guard-canvas]"; async function installCanvas(page: Page): Promise { await page.setContent(` `); await setBackingScale(page, 2); } async function setBackingScale(page: Page, scale: number): Promise { await page.locator(canvasSelector).evaluate((canvas, nextScale) => { if (!(canvas instanceof HTMLCanvasElement)) { throw new Error("Quality guard fixture must target a canvas."); } const rect = canvas.getBoundingClientRect(); canvas.width = Math.round(rect.width * window.devicePixelRatio * nextScale); canvas.height = Math.round(rect.height * window.devicePixelRatio * nextScale); }, scale); } async function createGuard( page: Page, cssSizePolicy: "fixed" | "viewport-driven" = "fixed", ) { const baselineCssSize = await expectToolcraftCanvasBackingPixelsForRenderScale( page, canvasSelector, 2, { state: "performance-baseline" }, ); return createToolcraftPerformanceCanvasQualityGuard(page, { baselineCssSize, canvasSelector, cssSizePolicy, pathId: "performance-path:quality-guard", }); } test("quality guard rejects a transient same-document 1x clamp", async ({ page, }) => { await installCanvas(page); const guard = await createGuard(page); try { await expect( guard.runPhase("cold", async () => { await setBackingScale(page, 1); await page.evaluate( () => new Promise((resolve) => requestAnimationFrame(() => resolve())), ); await setBackingScale(page, 2); }), ).rejects.toThrow(/cold.*backing width/iu); } finally { await guard.dispose(); } }); test("quality guard rejects an immediate 1x clamp followed by canvas replacement", async ({ page, }) => { await installCanvas(page); const guard = await createGuard(page); try { await expect( guard.runPhase("cold", async () => { await page.locator(canvasSelector).evaluate((canvas) => { if (!(canvas instanceof HTMLCanvasElement)) { throw new Error("Quality guard fixture must target a canvas."); } const rect = canvas.getBoundingClientRect(); canvas.width = Math.round(rect.width * window.devicePixelRatio); canvas.height = Math.round(rect.height * window.devicePixelRatio); const replacement = canvas.cloneNode(false) as HTMLCanvasElement; replacement.width = Math.round( rect.width * window.devicePixelRatio * 2, ); replacement.height = Math.round( rect.height * window.devicePixelRatio * 2, ); canvas.replaceWith(replacement); }); }), ).rejects.toThrow(/cold.*backing width/iu); } finally { await guard.dispose(); } }); test("quality guard rejects a one-frame CSS transform on a deep ancestor", async ({ page, }) => { await installCanvas(page); await page.locator(canvasSelector).evaluate((canvas) => { let child: Element = canvas; for (let depth = 0; depth < 8; depth += 1) { const wrapper = document.createElement("div"); wrapper.dataset.qualityGuardDepth = String(depth); child.replaceWith(wrapper); wrapper.append(child); child = wrapper; } }); const guard = await createGuard(page); try { await expect( guard.runPhase("warm", async () => { await page.locator(canvasSelector).evaluate(async (canvas) => { if (!(canvas instanceof HTMLCanvasElement)) { throw new Error("Quality guard fixture must target a canvas."); } const ancestor = document.querySelector( '[data-quality-guard-depth="7"]', ); if (!(ancestor instanceof HTMLElement)) { throw new Error("Quality guard fixture requires a canvas ancestor."); } ancestor.style.transform = "scale(.5)"; await new Promise((resolve) => requestAnimationFrame(() => resolve()), ); ancestor.style.transform = ""; }); }), ).rejects.toThrow(/warm.*preserve.*CSS width/iu); } finally { await guard.dispose(); } }); test("quality guard rejects a transient navigation-time 1x clamp", async ({ page, }) => { await installCanvas(page); const guard = await createGuard(page); const destination = `data:text/html,${encodeURIComponent(`