test(web): MSW harness with typed handlers, fixtures, and client tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 22:35:55 +02:00
parent dcfddc88c7
commit 66d0624279
10 changed files with 140 additions and 1 deletions
+34
View File
@@ -0,0 +1,34 @@
import type { components } from "../api/schema";
export type AdminObjectView = components["schemas"]["AdminObjectView"];
export type AdminObjectPage = components["schemas"]["AdminObjectPage"];
export const amphora: AdminObjectView = {
id: "11111111-1111-1111-1111-111111111111",
object_number: "LM-0042",
object_name: "Amphora",
number_of_objects: 1,
brief_description: "Storage jar",
current_location: "Vault 3",
current_owner: null,
recorder: null,
recording_date: null,
visibility: "public",
fields: {},
};
export const fibula: AdminObjectView = {
...amphora,
id: "22222222-2222-2222-2222-222222222222",
object_number: "LM-0043",
object_name: "Bronze fibula",
visibility: "internal",
fields: {},
};
export const objectsPage: AdminObjectPage = {
items: [amphora, fibula],
total: 2,
limit: 50,
offset: 0,
};
+10
View File
@@ -0,0 +1,10 @@
import { expect, test } from "vitest";
import { api } from "../api/client";
test("default handler serves the objects page", async () => {
const { data } = await api.GET("/api/admin/objects", { params: { query: {} } });
expect(data?.total).toBe(2);
expect(data?.items[0].object_number).toBe("LM-0042");
});
+35
View File
@@ -0,0 +1,35 @@
import { http, HttpResponse } from "msw";
import { amphora, fibula, objectsPage } from "./fixtures";
export const handlers = [
http.get("/api/admin/me", () =>
HttpResponse.json({ id: "u1", email: "editor@example.com", role: "editor" }),
),
http.get("/api/admin/objects", () => HttpResponse.json(objectsPage)),
http.get("/api/admin/objects/:id", ({ params }) => {
const found = [amphora, fibula].find((o) => o.id === params.id);
return found ? HttpResponse.json(found) : new HttpResponse(null, { status: 404 });
}),
http.get("/api/admin/field-definitions", () =>
HttpResponse.json([
{
key: "material",
data_type: "term",
vocabulary_id: "v1",
authority_kind: null,
required: false,
group: null,
labels: [{ lang: "en", label: "Material" }],
},
]),
),
http.post("/api/admin/login", () => new HttpResponse(null, { status: 204 })),
http.post("/api/admin/logout", () => new HttpResponse(null, { status: 204 })),
];
+5
View File
@@ -0,0 +1,5 @@
import { setupServer } from "msw/node";
import { handlers } from "./handlers";
export const server = setupServer(...handlers);
+10
View File
@@ -1 +1,11 @@
import "@testing-library/jest-dom/vitest";
import { afterAll, afterEach } from "vitest";
import { server } from "./server";
// Start MSW at module level so its fetch patch is in place before any test
// module captures globalThis.fetch via openapi-fetch's createClient().
server.listen({ onUnhandledRequest: "error" });
afterEach(() => server.resetHandlers());
afterAll(() => server.close());