// @vitest-environment jsdom // // A loading state fades in only after a delay, and loading states that hand over to each other share // ONE clock. The bug it replaced: every LoadingState restarted its own 300 ms CSS delay, so a slow // first load went spinner → blank → spinner when the page chunk's Suspense fallback handed over to // the page's own "fetching" state. The clock is module state, so each test runs at its own time. import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { render, cleanup } from "@testing-library/react"; import { LoadingState } from "./QueryState"; let now = 0; beforeEach(() => { vi.spyOn(performance, "now").mockImplementation(() => now); }); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); /** The reveal delay the rendered loading state waits, in ms (0 = shown at once). */ function revealDelay(container: HTMLElement): number { const el = container.querySelector("[role=status]")!; const m = /ease (\d+)ms/.exec(el.style.animation); return m ? Number(m[1]) : 0; } describe("LoadingState reveal", () => { it("a loading state on its own waits the full delay", () => { now = 10_000; const { container } = render(); expect(revealDelay(container)).toBe(300); }); it("one that takes over from a loading state on screen keeps that one's clock", () => { now = 20_000; const { container, rerender } = render(); now = 20_200; rerender(); // a new element: the fallback leaves as it mounts expect(revealDelay(container)).toBe(100); }); it("once the shared clock has run out, the next one in the hand-off shows at once", () => { now = 30_000; const { container, rerender } = render(); now = 30_400; rerender(); expect(revealDelay(container)).toBe(0); }); it("a loading state after a pause starts a new clock", () => { now = 40_000; const first = render(); first.unmount(); now = 40_500; const { container } = render(); expect(revealDelay(container)).toBe(300); }); it("`since` counts the wait from an earlier start (the boot screen: navigation start)", () => { now = 50_250; const { container } = render(); expect(revealDelay(container)).toBe(150); }); });