rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
94 lines (93 loc) • 3.77 kB
JavaScript
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createScrollRestoration, HISTORY_STATE_SCROLL_KEY, } from "./scrollRestoration";
describe("scrollRestoration", () => {
let historyState;
beforeEach(() => {
historyState = {};
vi.stubGlobal("window", {
location: { href: "http://localhost/" },
scrollX: 0,
scrollY: 0,
scrollTo: vi.fn(),
history: {
scrollRestoration: "auto",
get state() {
return historyState;
},
pushState: vi.fn((state) => {
historyState = state;
}),
replaceState: vi.fn((state) => {
historyState = state;
}),
},
});
});
it("restores scroll from history state after a reload", () => {
historyState = {
[HISTORY_STATE_SCROLL_KEY]: "entry:1",
scrollX: 12,
scrollY: 345,
};
const scrollRestoration = createScrollRestoration();
scrollRestoration.initialize();
scrollRestoration.applyPendingScroll();
expect(window.scrollTo).toHaveBeenCalledWith({
left: 12,
top: 345,
behavior: "instant",
});
});
it("migrates legacy scrollX/scrollY history state on boot", () => {
historyState = { scrollX: 7, scrollY: 222 };
const scrollRestoration = createScrollRestoration();
scrollRestoration.initialize();
scrollRestoration.applyPendingScroll();
expect(window.history.replaceState).toHaveBeenCalledWith(expect.objectContaining({
[HISTORY_STATE_SCROLL_KEY]: expect.any(String),
scrollX: 7,
scrollY: 222,
}), "", "http://localhost/");
expect(window.scrollTo).toHaveBeenCalledWith({
left: 7,
top: 222,
behavior: "instant",
});
});
it("restores back/forward positions from the in-memory key map", () => {
const scrollRestoration = createScrollRestoration();
scrollRestoration.initialize();
const firstEntryState = historyState;
window.scrollY = 500;
scrollRestoration.recordCurrentPosition(0, 500);
scrollRestoration.pushEntry("/next", new URL("/next", "http://localhost/"), { x: 0, y: 0 });
historyState = firstEntryState;
scrollRestoration.restorePopStateScroll();
scrollRestoration.applyPendingScroll();
expect(window.scrollTo).toHaveBeenCalledWith({
left: 0,
top: 500,
behavior: "instant",
});
});
it("does not write to history state while recording scroll", () => {
const scrollRestoration = createScrollRestoration();
scrollRestoration.initialize();
vi.mocked(window.history.replaceState).mockClear();
scrollRestoration.recordCurrentPosition(0, 100);
scrollRestoration.recordCurrentPosition(0, 200);
expect(window.history.replaceState).not.toHaveBeenCalled();
});
it("flushes the latest scroll position to history state on lifecycle boundaries", () => {
const scrollRestoration = createScrollRestoration();
scrollRestoration.initialize();
vi.mocked(window.history.replaceState).mockClear();
scrollRestoration.recordCurrentPosition(4, 400);
scrollRestoration.flushCurrentPositionToHistoryState(4, 400);
expect(window.history.replaceState).toHaveBeenCalledWith(expect.objectContaining({
[HISTORY_STATE_SCROLL_KEY]: expect.any(String),
scrollX: 4,
scrollY: 400,
}), "", "http://localhost/");
});
});