Files
buzz-sheet/lib/image-export/client.test.ts
T

74 lines
2.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { exportFixtures } from "./fixtures";
import type { WorkerRequest, WorkerResult } from "./types";
class TestWorker {
static instances: TestWorker[] = [];
requests: WorkerRequest[] = [];
onmessage?: (event: { data: WorkerResult }) => void;
onerror?: () => void;
terminate = vi.fn();
constructor() {
TestWorker.instances.push(this);
}
postMessage(request: WorkerRequest) {
this.requests.push(request);
}
}
beforeEach(() => {
vi.resetModules();
TestWorker.instances = [];
vi.stubGlobal("Worker", TestWorker);
vi.stubGlobal("window", { location: { origin: "http://export.test" } });
});
afterEach(() => vi.unstubAllGlobals());
describe("export worker client", () => {
it("reuses one lazy worker and matches replies to concurrent requests", async () => {
const { renderImage } = await import("./client");
expect(TestWorker.instances).toHaveLength(0);
const first = renderImage(exportFixtures[0]);
const second = renderImage({ ...exportFixtures[0], format: "webp" });
expect(TestWorker.instances).toHaveLength(1);
const worker = TestWorker.instances[0];
const [a, b] = worker.requests;
worker.onmessage!({
data: { id: b.id, bytes: new Uint8Array([2]).buffer },
});
worker.onmessage!({
data: { id: a.id, bytes: new Uint8Array([1]).buffer },
});
expect((await first).type).toBe("image/png");
expect((await second).type).toBe("image/webp");
});
it("rejects a failed job and permits retry", async () => {
const { renderImage } = await import("./client");
const failed = renderImage(exportFixtures[0]);
const worker = TestWorker.instances[0];
const rejection = expect(failed).rejects.toThrow("Missing artwork");
worker.onmessage!({
data: { id: worker.requests[0].id, error: "Missing artwork" },
});
await rejection;
const retried = renderImage(exportFixtures[0]);
worker.onmessage!({
data: { id: worker.requests[1].id, bytes: new ArrayBuffer(1) },
});
expect(await retried).toBeInstanceOf(Blob);
});
it("clears pending jobs and recreates a crashed worker", async () => {
const { renderImage } = await import("./client");
const failed = renderImage(exportFixtures[0]);
const rejection = expect(failed).rejects.toThrow("Image renderer failed");
TestWorker.instances[0].onerror!();
await rejection;
expect(TestWorker.instances[0].terminate).toHaveBeenCalledOnce();
const retried = renderImage(exportFixtures[0]);
const worker = TestWorker.instances[1];
worker.onmessage!({
data: { id: worker.requests[0].id, bytes: new ArrayBuffer(1) },
});
expect(await retried).toBeInstanceOf(Blob);
});
});