import { describe, it, expect, beforeEach } from "vitest"; import { ScrollMemory, classifyNavigation } from "./scrollRestore"; describe("classifyNavigation", () => { it("treats the initial page load as an entry", () => { expect(classifyNavigation({ type: "enter" })).toBe("enter"); }); it("treats back/forward gestures as a popstate", () => { expect(classifyNavigation({ type: "popstate" })).toBe("popstate"); }); it("treats link and goto navigations as forward moves", () => { expect(classifyNavigation({ type: "link" })).toBe("forward"); expect(classifyNavigation({ type: "goto" })).toBe("forward"); expect(classifyNavigation({ type: "form" })).toBe("forward"); }); }); describe("ScrollMemory", () => { let memory: ScrollMemory; beforeEach(() => { memory = new ScrollMemory(); }); // The bug: a scroll container that lives in a persistent layout keeps its // offset across a forward navigation, so a page opened from a scrolled list // starts part-way down. A forward move must always land at the top. it("resets to the top on a forward navigation, even from a scrolled page", () => { memory.save("/library", 1200); expect(memory.decide("/library/abc123", "forward")).toEqual({ kind: "reset" }); }); it("resets to the top when navigating forward to a page seen before", () => { memory.save("/library", 1200); memory.save("/search", 340); // Re-entering /library by tapping a nav link is a fresh visit, not a Back. expect(memory.decide("/library", "forward")).toEqual({ kind: "reset" }); }); it("restores the saved offset on Back", () => { memory.save("/library", 1200); expect(memory.decide("/library", "popstate")).toEqual({ kind: "restore", top: 1200 }); }); it("restores the top when Back targets a page with no saved offset", () => { expect(memory.decide("/library", "popstate")).toEqual({ kind: "restore", top: 0 }); }); it("keeps offsets per route rather than sharing one across pages", () => { memory.save("/library", 1200); memory.save("/search", 340); expect(memory.decide("/library", "popstate")).toEqual({ kind: "restore", top: 1200 }); expect(memory.decide("/search", "popstate")).toEqual({ kind: "restore", top: 340 }); }); it("leaves the container alone on the initial load", () => { expect(memory.decide("/", "enter")).toEqual({ kind: "none" }); }); it("overwrites a stale offset when the same route is saved again", () => { memory.save("/library", 1200); memory.save("/library", 80); expect(memory.decide("/library", "popstate")).toEqual({ kind: "restore", top: 80 }); }); it("forgets nothing on decide, so a repeated Back still restores", () => { memory.save("/library", 1200); memory.decide("/library", "popstate"); expect(memory.decide("/library", "popstate")).toEqual({ kind: "restore", top: 1200 }); }); });