first commit

This commit is contained in:
2026-07-21 16:50:58 +02:00
commit 256599626e
407 changed files with 30489 additions and 0 deletions
@@ -0,0 +1,82 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { fetchRecipesIndex } from "./catalog";
const emptyText = { fr: "", en: "", ko: "", zh: "" };
const minimalBase = {
id: "base:x",
flavor: "savory" as const,
name: emptyText,
history: emptyText,
ingredients: [],
steps: []
};
const minimalVariant = {
id: "var:y",
baseIds: ["base:x"],
name: emptyText,
story: emptyText,
ingredients: [],
steps: []
};
describe("fetchRecipesIndex", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("loads and freezes catalog JSON", async () => {
const payload = { bases: [minimalBase], variants: [minimalVariant] };
vi.stubGlobal(
"fetch",
vi.fn(async () =>
Promise.resolve({
ok: true,
json: async () => payload
})
)
);
const index = await fetchRecipesIndex("/custom/catalog.json");
expect(index.bases).toHaveLength(1);
expect(index.variants).toHaveLength(1);
expect(fetch).toHaveBeenCalledWith("/custom/catalog.json", {
headers: { Accept: "application/json" }
});
});
it("throws when response is not ok", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () => Promise.resolve({ ok: false, status: 503 }))
);
await expect(fetchRecipesIndex()).rejects.toThrow("503");
});
it("throws when payload is not an object", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () =>
Promise.resolve({
ok: true,
json: async () => null
})
)
);
await expect(fetchRecipesIndex()).rejects.toThrow("Invalid recipe catalog payload");
});
it("throws when bases or variants are not arrays", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () =>
Promise.resolve({
ok: true,
json: async () => ({ bases: [], variants: null })
})
)
);
await expect(fetchRecipesIndex()).rejects.toThrow("Invalid recipe catalog shape");
});
});