import { describe, expect, it } from "vitest"; import { normalizeProgress } from "./progressNormalization"; const isGrade = (value: unknown): value is "correct" | "missed" => value === "correct" || value === "missed"; function normalize(order: string[], currentIndex: number, liveIds: string[]) { return normalizeProgress({ orderJson: JSON.stringify(order), currentIndex, dataJson: JSON.stringify({ a: "correct", deleted: "missed" }), liveIds, isValidValue: isGrade, }); } describe("normalizeProgress", () => { it("preserves the logical current card when a prior card was deleted", () => { expect(normalize(["a", "b", "c"], 1, ["b", "c"]).currentIndex).toBe(0); }); it("uses the next survivor when the current card was deleted", () => { expect(normalize(["a", "b", "c"], 1, ["a", "c"]).currentIndex).toBe(1); }); it("uses the previous survivor when no later card remains", () => { expect(normalize(["a", "b", "c"], 2, ["a", "b"]).currentIndex).toBe(1); }); it("preserves completed state after stale IDs are removed", () => { const result = normalize(["a", "b", "c"], 3, ["a", "c"]); expect(result.currentIndex).toBe(2); expect(result.completed).toBe(true); }); it("recovers malformed JSON and filters stale result keys", () => { const result = normalizeProgress({ orderJson: "not-json", currentIndex: -1, dataJson: '{"a":"correct","b":"invalid"}', liveIds: ["a", "b"], isValidValue: isGrade, }); expect(result).toMatchObject({ order: ["a", "b"], currentIndex: 0, data: { a: "correct" }, completed: false, wasRecovered: true, }); }); it("returns a clear completed state when all saved cards were deleted", () => { const result = normalize(["deleted"], 0, []); expect(result).toMatchObject({ order: [], currentIndex: 0, completed: true }); }); it("lets content-specific validation reject a value owned by another item", () => { const result = normalizeProgress({ orderJson: '["q1","q2"]', currentIndex: 0, dataJson: '{"q1":["q2-option"]}', liveIds: ["q1", "q2"], isValidValue: (value, id): value is string[] => Array.isArray(value) && value.every((optionId) => optionId === `${id}-option`), }); expect(result.data).toEqual({}); expect(result.wasRecovered).toBe(true); }); });