Files
EpicNext-Cms/e2e/imaging/cms-imaging.spec.ts
T
openhands 640e5a5dd0
CI / check (push) Successful in 32s
CI / tests-imaging-browsers (push) Successful in 1m33s
CI / tests-integration (push) Successful in 2m5s
CI / tests-unit (push) Successful in 2m7s
CI / tests-ui (push) Successful in 2m47s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 3m44s
test(imaging): broaden coverage across contract, transcode, routing and pixels
Environment-independent contract tests (cache headers + source, CORS, OPTIONS
preflight, negative figure/badge tables, concurrency burst) run in both the
deploy gate and the cross-browser job.

CMS-only /imaging assertions (webp/avif transcode, animated-APNG passthrough,
s-maxage edge caching) live in cms-imaging.spec.ts and are skipped behind the
edge proxy; the public topology has its own public-routing.spec.ts smoke that
fails if the edge ever stops fronting the imager or starts shadowing the CMS
endpoints. A committed per-engine visual baseline pins the static render.

The cross-browser imaging job is now blocking (IMAGING_EDGE=1), so avatar
imaging on chromium/firefox/webkit is a required signal.
2026-10-14 21:19:18 +02:00

79 lines
3.2 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { countBytes, FIGURE, IS_EDGE, looksLikeImage } from "./imaging-target";
// These assertions only hold when the CMS itself answers `/imaging`. In
// production the edge proxy fronts bare `/imaging` (and `/imaging/*`) to the
// imager container, so the CMS route is shadowed; set `IMAGING_EDGE=1` to skip.
test.skip(
IS_EDGE,
"The CMS /imaging route is shadowed by the edge proxy in production",
);
const imagerPath = (params: Record<string, string> = {}) =>
`/imaging?${new URLSearchParams({ figure: FIGURE, img_format: "png", ...params })}`;
const PNG_MAGIC = Buffer.from("89504e470d0a1a0a", "hex");
test("serves the default render as a PNG", async ({ request }) => {
const response = await request.get(imagerPath());
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("image/png");
const body = await response.body();
expect(body.subarray(0, 8).equals(PNG_MAGIC)).toBe(true);
expect(looksLikeImage(body)).toBe(true);
});
test("transcodes to WebP on request", async ({ request }) => {
const response = await request.get(imagerPath({ format: "webp" }));
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("image/webp");
const body = await response.body();
expect(body.subarray(0, 4).toString("ascii")).toBe("RIFF");
expect(body.subarray(8, 12).toString("ascii")).toBe("WEBP");
});
test("transcodes to AVIF on request", async ({ request }) => {
const response = await request.get(imagerPath({ format: "avif" }));
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("image/avif");
const body = await response.body();
expect(body.subarray(4, 12).toString("ascii")).toBe("ftypavif");
});
test("leaves animated APNG renders untranscoded", async ({ request }) => {
// The renderer emits effected avatars as APNG; libvips here reads only the
// first frame, so the animated original must be served as-is even when a
// transcode is requested — otherwise the avatar would stop animating.
const response = await request.get(
imagerPath({ img_format: "apng", effect: "14", format: "webp" }),
);
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("image/png");
const body = await response.body();
expect(body.includes(Buffer.from("acTL"))).toBe(true);
expect(countBytes(body, Buffer.from("fcTL"))).toBeGreaterThan(1);
});
test("advertises long-lived edge caching", async ({ request }) => {
const response = await request.get(imagerPath());
expect(response.status()).toBe(200);
const cacheControl = response.headers()["cache-control"] ?? "";
expect(cacheControl).toContain("public");
expect(cacheControl).toContain("s-maxage");
expect(cacheControl).toContain("stale-while-revalidate");
});
test("rejects a malformed figure with 400", async ({ request }) => {
const response = await request.get(
`/imaging?figure=${encodeURIComponent("../bad")}`,
);
expect(response.status()).toBe(400);
});
test("the imager preflight advertises GET", async ({ request }) => {
const response = await request.fetch("/imaging", { method: "OPTIONS" });
expect(response.status()).toBe(204);
expect(response.headers()["access-control-allow-methods"]).toContain("GET");
});