feat: route-map navigation, persisted progress with a resume point, Alt+arrow exit keys and a trip log
This commit is contained in:
65
tests/unit/route-memory.test.ts
Normal file
65
tests/unit/route-memory.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
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<SectionId>(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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user