import { mount } from "@vue/test-utils"; import { afterEach, describe, expect, it } from "vitest"; import { defineComponent, h, nextTick, ref } from "vue"; import type { SectionId } from "@/content/types"; import { useRouteMemory, type RouteMemory } from "@/composables/useRouteMemory"; const ids = ["start", "profile", "experience", "contact"] as unknown as readonly SectionId[]; const harness = (initial: SectionId) => { const active = ref(initial); let memory: RouteMemory | undefined; const wrapper = mount(defineComponent({ setup() { memory = useRouteMemory(ids, () => active.value); return () => h("div"); }, })); return { active, memory: memory!, wrapper }; }; afterEach(() => { window.localStorage.clear(); window.location.hash = ""; }); describe("route memory", () => { it("records passed exits and offers the last stop as a resume point", async () => { const first = harness("start" as SectionId); first.active.value = "profile" as SectionId; await nextTick(); first.active.value = "experience" as SectionId; await nextTick(); expect([...first.memory.visited.value]).toEqual(["start", "profile", "experience"]); first.wrapper.unmount(); const second = harness("start" as SectionId); await nextTick(); expect(second.memory.resumeFrom.value).toBe("experience"); expect(second.memory.visited.value.has("profile" as SectionId)).toBe(true); // Idling at the on-ramp keeps the resume point for the next visit. second.wrapper.unmount(); expect(harness("start" as SectionId).memory.resumeFrom.value).toBe("experience"); }); it("drops the offer once the visitor drives on and ignores corrupt storage", async () => { window.localStorage.setItem("nightdrive:route", JSON.stringify({ visited: ["profile", "nowhere"], last: "profile" })); const run = harness("start" as SectionId); expect(run.memory.resumeFrom.value).toBe("profile"); expect([...run.memory.visited.value]).toEqual(["profile", "start"]); run.active.value = "experience" as SectionId; await nextTick(); expect(run.memory.resumeFrom.value).toBeNull(); window.localStorage.setItem("nightdrive:route", "{not json"); expect(harness("start" as SectionId).memory.resumeFrom.value).toBeNull(); }); it("logs the trip once the destination is reached", async () => { const run = harness("start" as SectionId); expect(run.memory.tripMinutes.value).toBeNull(); run.active.value = "contact" as SectionId; await nextTick(); expect(run.memory.tripMinutes.value).toBe(1); }); });