init
This commit is contained in:
commit
c19b910c36
80 changed files with 16134 additions and 0 deletions
93
app/lib/plan.test.ts
Normal file
93
app/lib/plan.test.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import type { MealPlanState } from "@/app/types";
|
||||
import { activeRecipeIds, mealKindFor, mealLabel, visibleMeals } from "./plan";
|
||||
import { DEFAULT_STATE, migrateV1Slots, sanitizeState } from "./sanitize";
|
||||
import { RECIPES } from "./recipes";
|
||||
import { planReducer } from "./usePlanState";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Meal slots. Regression tests for the "change meals per day and your
|
||||
// breakfast turns into lunch" bug. Meal numbers are fixed:
|
||||
// 1 = Breakfast, 2 = Lunch, 3 = Dinner
|
||||
// and "meals per day" only decides which ones you can see.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Any real recipes will do; names are just for readability.
|
||||
const PANCAKES = RECIPES.find((r) => r.kind === "breakfast")!.id;
|
||||
const [SANDWICH, STEW] = RECIPES.filter((r) => r.kind === "main").map((r) => r.id);
|
||||
|
||||
/** Monday with all three meals planned. */
|
||||
const fullMonday: MealPlanState = {
|
||||
...DEFAULT_STATE,
|
||||
daysCount: 1,
|
||||
mealsPerDay: 3,
|
||||
slots: { "0-1": PANCAKES, "0-2": SANDWICH, "0-3": STEW },
|
||||
};
|
||||
|
||||
/** What the grid shows, as "Label: recipe". */
|
||||
function visibleGrid(state: MealPlanState): string[] {
|
||||
return visibleMeals(state.mealsPerDay).map((meal) => `${mealLabel(meal)}: ${state.slots[`0-${meal}`] ?? "—"}`);
|
||||
}
|
||||
|
||||
describe("visible meals", () => {
|
||||
test("fewer meals hides from the front of the day", () => {
|
||||
expect(visibleMeals(3).map(mealLabel)).toEqual(["Breakfast", "Lunch", "Dinner"]);
|
||||
expect(visibleMeals(2).map(mealLabel)).toEqual(["Lunch", "Dinner"]);
|
||||
expect(visibleMeals(1).map(mealLabel)).toEqual(["Dinner"]);
|
||||
});
|
||||
|
||||
test("only meal 1 is breakfast", () => {
|
||||
expect([1, 2, 3].map(mealKindFor)).toEqual(["breakfast", "main", "main"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("changing meals per day", () => {
|
||||
test("3 → 2 keeps lunch as lunch and dinner as dinner", () => {
|
||||
const state = planReducer(fullMonday, { type: "setMealsPerDay", value: 2 });
|
||||
expect(visibleGrid(state)).toEqual([`Lunch: ${SANDWICH}`, `Dinner: ${STEW}`]);
|
||||
});
|
||||
|
||||
test("3 → 1 → 3 brings breakfast and lunch back (nothing deleted)", () => {
|
||||
const down = planReducer(fullMonday, { type: "setMealsPerDay", value: 1 });
|
||||
expect(visibleGrid(down)).toEqual([`Dinner: ${STEW}`]);
|
||||
|
||||
const backUp = planReducer(down, { type: "setMealsPerDay", value: 3 });
|
||||
expect(visibleGrid(backUp)).toEqual([`Breakfast: ${PANCAKES}`, `Lunch: ${SANDWICH}`, `Dinner: ${STEW}`]);
|
||||
});
|
||||
|
||||
test("hidden meals don't count toward the cart", () => {
|
||||
expect(activeRecipeIds({ ...fullMonday, mealsPerDay: 1 })).toEqual([STEW]);
|
||||
expect(activeRecipeIds({ ...fullMonday, mealsPerDay: 2 })).toEqual([SANDWICH, STEW]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Clear", () => {
|
||||
test("empties the visible grid but keeps hidden meals", () => {
|
||||
const lunchAndDinner = { ...fullMonday, mealsPerDay: 2 };
|
||||
const cleared = planReducer(lunchAndDinner, { type: "clearSlots" });
|
||||
|
||||
expect(visibleGrid(cleared)).toEqual(["Lunch: —", "Dinner: —"]);
|
||||
expect(cleared.slots).toEqual({ "0-1": PANCAKES }); // breakfast was hidden, so untouched
|
||||
});
|
||||
});
|
||||
|
||||
describe("migrating plans saved before meal numbers were fixed", () => {
|
||||
test("2 meals/day: old 1/2 (Lunch/Dinner) become 2/3", () => {
|
||||
const old = { mealsPerDay: 2, slots: { "0-1": SANDWICH, "0-2": STEW } };
|
||||
expect((migrateV1Slots(old) as MealPlanState).slots).toEqual({ "0-2": SANDWICH, "0-3": STEW });
|
||||
});
|
||||
|
||||
test("1 meal/day: old 1 (Dinner) becomes 3, stale hidden keys are dropped", () => {
|
||||
const old = { mealsPerDay: 1, slots: { "0-1": STEW, "0-2": SANDWICH } };
|
||||
expect((migrateV1Slots(old) as MealPlanState).slots).toEqual({ "0-3": STEW });
|
||||
});
|
||||
|
||||
test("3 meals/day: nothing moves", () => {
|
||||
const old = { mealsPerDay: 3, slots: { "0-1": PANCAKES, "0-3": STEW } };
|
||||
expect((migrateV1Slots(old) as MealPlanState).slots).toEqual(old.slots);
|
||||
});
|
||||
|
||||
test("migrated plans still pass sanitizeState", () => {
|
||||
expect(() => sanitizeState(migrateV1Slots({ mealsPerDay: 2, slots: { "0-1": "x" } }))).not.toThrow();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue