test.social/core/renderers/stage.renderer.test.ts
test.social/core/renderers/stage.renderer.test.ts is a file in Codebase Testing. 125 lines of code and 0 definitions.
import {
ANIMATION_FAILED,
MISSING_LAYER,
NEEDS_WEBGPU,
} from "@banes-lab/social-share/configuration/strings/card.strings.ts";
import type { Anchor, CardInput } from "@banes-lab/social-share/types/card.types.ts";
import { describe, expect, it, vi } from "vitest";
import { createAnimationPass } from "@banes-lab/social-share/core/renderers/image.renderer.ts";
import { createCard } from "@banes-lab/social-share/core/factories/card.factory.ts";
import { createShaderPass } from "@banes-lab/social-share/core/renderers/shader.renderer.ts";
import { mountCard } from "@banes-lab/social-share/core/renderers/stage.renderer.ts";
import { resolveCard } from "@banes-lab/social-share/core/evaluators/card.evaluator.ts";
const PROFILE = { height: 630, id: "og", width: 1200 };
const isForgedAnchor = function isForgedAnchor(name: string): name is Anchor {
return name.length > 0;
};
const input: CardInput = {
alt: "A demo card.",
id: "demo",
layers: [
{ className: "demo-ground", id: "ground", kind: "box", placement: { height: 1, width: 1, x: 0, y: 0 } },
{
alt: "Mark",
id: "mark",
kind: "image",
placement: { anchor: "center", x: 0.5, y: 0.5 },
source: "/mark.png",
},
{
id: "title",
kind: "text",
placement: { x: 0.1, y: 0.2 },
style: { fontSize: "40px" },
text: (frame) => `frame ${String(frame.frame)}`,
},
],
page: "home",
stylesheet: ".card-demo { color: red; }",
timeline: { fps: 10, frames: 10 },
tone: "page-gold",
};
const spec = createCard(input);
describe("mountCard", () => {
it("builds one element per layer inside a sized card root with the card stylesheet", () => {
const host = document.createElement("main");
const mounted = mountCard(host, resolveCard(spec, PROFILE, 0));
expect(mounted.element.classList.contains("card-demo")).toBe(true);
expect(mounted.element.classList.contains("page-gold")).toBe(true);
expect(mounted.element.querySelector("style")?.textContent).toBe(".card-demo { color: red; }");
expect(mounted.element.querySelector("img")?.getAttribute("src")).toBe("/mark.png");
expect(mounted.element.querySelectorAll(".card-layer")).toHaveLength(3);
expect(host.contains(mounted.element)).toBe(true);
});
it("repaints text for each frame and resolves once nothing is pending", async () => {
const mounted = mountCard(document.createElement("main"), resolveCard(spec, PROFILE, 0));
await mounted.ready();
await mounted.paint(resolveCard(spec, PROFILE, 4));
expect(mounted.element.textContent).toContain("frame 4");
});
it("throws when a frame names a layer the card never mounted", async () => {
const mounted = mountCard(document.createElement("main"), resolveCard(spec, PROFILE, 0));
const grown = createCard({
...input,
layers: [...input.layers, { id: "late", kind: "box", placement: { x: 0, y: 0 } }],
});
await expect(mounted.paint(resolveCard(grown, PROFILE, 0))).rejects.toThrow(MISSING_LAYER);
});
it("throws on a placement anchor outside the vocabulary", async () => {
const [odd] = ["middle"]
.filter(isForgedAnchor)
.map((anchor) =>
createCard({ ...input, layers: [{ id: "odd", kind: "box", placement: { anchor, x: 0, y: 0 } }] }),
);
if (odd === undefined) {
throw new Error("the forged anchor was filtered out");
}
const mounted = mountCard(document.createElement("main"), resolveCard(odd, PROFILE, 0));
await expect(mounted.paint(resolveCard(odd, PROFILE, 0))).rejects.toThrow("middle");
});
it("shows and logs a reported failure on the card", () => {
const logged = vi.spyOn(console, "error").mockReturnValue();
const mounted = mountCard(document.createElement("main"), resolveCard(spec, PROFILE, 0));
expect(mounted.report(new Error("broken"))).toBe("demo broken");
expect(mounted.element.querySelector(".stage-findings")?.textContent).toBe("demo broken");
expect(logged).toHaveBeenCalledWith("demo broken");
logged.mockRestore();
});
it("shows and logs one failure once however often it is reported", () => {
const logged = vi.spyOn(console, "error").mockReturnValue();
const mounted = mountCard(document.createElement("main"), resolveCard(spec, PROFILE, 0));
mounted.report(new Error("broken"));
mounted.report(new Error("broken"));
expect(mounted.element.querySelectorAll(".stage-findings")).toHaveLength(1);
expect(logged).toHaveBeenCalledTimes(1);
logged.mockRestore();
});
});
describe("createAnimationPass", () => {
it("refuses loudly when the canvas offers no drawing context", async () => {
await expect(createAnimationPass(document.createElement("canvas"), "/mark.gif")).rejects.toThrow(
ANIMATION_FAILED,
);
});
});
describe("createShaderPass", () => {
it("refuses loudly when the browser provides no WebGPU", async () => {
await expect(createShaderPass(document.createElement("canvas"), "")).rejects.toThrow(NEEDS_WEBGPU);
});
it("surfaces the missing GPU through a mounted shader card's readiness", async () => {
const shaded = createCard({
...input,
layers: [
{
id: "field",
kind: "shader",
placement: { height: 1, width: 1, x: 0, y: 0 },
shader: "",
uniforms: {},
},
],
});
const mounted = mountCard(document.createElement("main"), resolveCard(shaded, PROFILE, 0));
await expect(mounted.ready()).rejects.toThrow(NEEDS_WEBGPU);
});
});