test(imaging): add endpoint contract e2e and cross-browser render checks
CI / check (push) Successful in 40s
CI / tests-imaging-browsers (push) Failing after 1m20s
CI / tests-unit (push) Successful in 2m12s
CI / tests-integration (push) Successful in 2m24s
CI / tests-ui (push) Successful in 2m55s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m38s

The deploy gate only installs chromium, so imaging had no browser-level
coverage. Add two Playwright specs:

- e2e/imaging/avatar-endpoint.spec.ts: HTTP contract for the avatar
  (/api/imaging/avatar), default (/imaging) and badge endpoints — animated
  APNG for a figure with an effect, the trailing-`.0` regression figure,
  ETag/304 revalidation, and a 400 for a malformed figure. Runs in the
  deploy gate automatically via the default Playwright config.
- e2e/imaging/avatar-render.spec.ts: loads the avatar in a real browser and
  asserts it decodes at a non-zero size and animates. Navigates to the image
  URL directly so production CSP `upgrade-insecure-requests` cannot rewrite
  the request over a plain-HTTP host.

Add playwright.imaging.config.ts running both across chromium, firefox and
webkit, a `test:imaging` script, and a non-blocking CI job that installs all
three engines and checks the live site.
This commit is contained in:
openhands committed 2026-10-14 21:00:27 +02:00
1 parent f3407ebfdf
commit 888765c47f
5 files changed
+204

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
import { expect, test } from "@playwright/test";
// Same production figure as the endpoint spec: includes the trailing `.0`
// segment the client sends and the renderer ignores.
const FIGURE =
"hr-11782-40-40.hd-180-7-14.ch-11592-66.lg-10726-79-1408.sh-11764-1408.ha-11958-70-1408.wa-2007-0.0";
const AVATAR_PATH = `/api/imaging/avatar?${new URLSearchParams({
figure: FIGURE,
img_format: "apng",
effect: "14",
})}`;
/**
* Cross-browser check that a real browser (chromium/firefox/webkit) can decode
* the avatar the imager produces and that it actually animates. The endpoint
* spec covers the HTTP contract; this one covers the client-side decode, where
* an unsupported image format would surface as a blank/broken avatar.
*
* Navigates straight to the image URL (a top-level navigation, which CSP
* `upgrade-insecure-requests` does not rewrite) rather than embedding it in a
* page: over a plain-HTTP host the upgrade directive would otherwise send the
* subresource request to https and break the load, without exercising anything
* about the image itself.
*
* Run across engines via `pnpm test:imaging` (playwright.imaging.config.ts).
*/
test("the avatar decodes and animates in the browser", async ({ page }) => {
const response = await page.goto(AVATAR_PATH);
expect(response?.status()).toBe(200);
const image = page.locator("img");
await expect(image).toBeVisible();
const decoded = await image.evaluate((element: HTMLImageElement) => ({
width: element.naturalWidth,
height: element.naturalHeight,
}));
expect(decoded.width).toBeGreaterThan(0);
expect(decoded.height).toBeGreaterThan(0);
// An animated APNG keeps producing new frames; a static/undecodable one
// yields the same pixels every time. Sampling screenshots over a second is
// enough to tell them apart without depending on exact frame timing.
const frames = new Set<string>();
for (let attempt = 0; attempt < 8; attempt += 1) {
frames.add((await image.screenshot()).toString("base64"));
await page.waitForTimeout(180);
}
expect(frames.size).toBeGreaterThan(1);
});