planmymeal/app/lib/exportPlan.test.ts

51 lines
2 KiB
TypeScript
Raw Normal View History

import { describe, expect, test } from "bun:test";
import { jsPDF } from "jspdf";
import { buildPlanModel, pdfSafe, renderPlanPdf } from "./exportPlan";
import { RECIPES } from "./recipes";
const breakfast = RECIPES.find((r) => r.kind === "breakfast")!;
const dinner = RECIPES.find((r) => r.kind === "main")!;
const state = {
daysCount: 2,
mealsPerDay: 3,
householdSize: 4,
// Monday breakfast + dinner, Tuesday dinner; "6-3" is outside daysCount and must not export.
slots: { "0-1": breakfast.id, "0-3": dinner.id, "1-3": dinner.id, "6-3": dinner.id },
};
describe("plan export", () => {
const model = buildPlanModel(state);
test("organizes meals under each planned day, in meal order", () => {
expect(model.days.map((d) => d.name)).toEqual(["Monday", "Tuesday"]);
const [monday, tuesday] = model.days;
expect(monday.meals.map((m) => ("empty" in m ? `${m.label}: -` : `${m.label}: ${m.title}`))).toEqual([
`Breakfast: ${breakfast.title}`,
"Lunch: -",
`Dinner: ${dinner.title}`,
]);
expect(tuesday.meals.filter((m) => !("empty" in m))).toHaveLength(1);
});
test("each meal carries its full recipe, scaled to the household", () => {
const meal = model.days[0].meals[0];
if ("empty" in meal) throw new Error("expected a planned meal");
expect(meal.steps).toEqual(breakfast.steps);
expect(meal.ingredients).toHaveLength(breakfast.ingredients.length);
expect(meal.meta).toContain("serves 4");
});
test("text is made printable in the base PDF font", () => {
expect(pdfSafe("1⅓ cup flour")).toBe("1 1/3 cup flour");
expect(pdfSafe("⅛ tsp salt")).toBe("1/8 tsp salt");
expect(pdfSafe("½ cup · 350°F — Rakı")).toBe("½ cup · 350°F — Raki");
});
test("renders a real multi-page PDF (one day per page)", () => {
const bytes = new Uint8Array(renderPlanPdf(model, jsPDF).output("arraybuffer"));
expect(new TextDecoder().decode(bytes.slice(0, 5))).toBe("%PDF-");
expect(renderPlanPdf(model, jsPDF).getNumberOfPages()).toBeGreaterThanOrEqual(2);
});
});