import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { SiteOutlineController } from "@/client/sites/islands/site-outline-controller";
let host: HTMLElement;
let root: Root;
beforeEach(() => {
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
vi.useFakeTimers();
vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
return window.setTimeout(() => cb(performance.now()), 0) as unknown as number;
});
vi.spyOn(window, "cancelAnimationFrame").mockImplementation((id) => {
window.clearTimeout(id);
});
document.body.innerHTML = `
Intro
Second
`;
const intro = document.getElementById("intro")!;
const second = document.getElementById("second")!;
Object.defineProperty(intro, "offsetParent", { configurable: true, get: () => document.body });
Object.defineProperty(second, "offsetParent", { configurable: true, get: () => document.body });
intro.getBoundingClientRect = () => ({ top: 40, bottom: 80 }) as DOMRect;
second.getBoundingClientRect = () => ({ top: 520, bottom: 560 }) as DOMRect;
host = document.getElementById("rail-host")!;
root = createRoot(host);
act(() => {
root.render(
,
);
});
});
afterEach(() => {
act(() => {
root.unmount();
});
document.body.innerHTML = "";
vi.useRealTimers();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
describe("SiteOutlineController", () => {
it("updates every duplicate outline link on simulated viewport changes", () => {
expect(document.querySelectorAll("[data-outline-id='intro'][data-active='true']").length).toBeGreaterThanOrEqual(2);
expect(
document.querySelectorAll("[data-outline-id='intro'][aria-current='location']").length,
).toBeGreaterThanOrEqual(2);
const intro = document.getElementById("intro")!;
const second = document.getElementById("second")!;
intro.getBoundingClientRect = () => ({ top: -140, bottom: -80 }) as DOMRect;
second.getBoundingClientRect = () => ({ top: 180, bottom: 220 }) as DOMRect;
act(() => {
window.dispatchEvent(new Event("scroll"));
vi.runOnlyPendingTimers();
});
expect(document.querySelectorAll("[data-outline-id='second'][data-active='true']").length).toBeGreaterThanOrEqual(
2,
);
expect(document.querySelectorAll("[data-outline-id='intro'][aria-current='location']")).toHaveLength(0);
});
});