Files
next-tjwater-frontend/src/shared/ui/deferred-render.test.tsx
T
jiang a617aa95e9 fix(workbench): stabilize investigation presentation rendering
Keep investigation graphs stable during updates, defer expensive chart rendering, and prevent stale results when switching tasks. Adopt reference annotations and consolidate frontend documentation.
2026-11-11 19:10:41 +08:00

62 lines
2.6 KiB
TypeScript

import { act, cleanup, render, screen } from "@testing-library/react";
import { afterEach, beforeEach, expect, it, vi } from "vitest";
import { DeferredRender } from "./deferred-render";
let sequence = 0;
const frames = new Map<number, FrameRequestCallback>();
const tick = () => act(() => {
const callbacks = [...frames.values()];
frames.clear();
callbacks.forEach((callback) => callback(0));
});
beforeEach(() => {
vi.stubGlobal("IntersectionObserver", undefined);
vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => { frames.set(++sequence, callback); return sequence; });
vi.stubGlobal("cancelAnimationFrame", (id: number) => frames.delete(id));
});
afterEach(() => { cleanup(); frames.clear(); vi.unstubAllGlobals(); });
it("paints loading placeholders before mounting visible drawings one at a time", () => {
render(<><DeferredRender label="正在加载甲图"><p>甲图已就绪</p></DeferredRender>
<DeferredRender label="正在加载乙图"><p>乙图已就绪</p></DeferredRender></>);
expect(screen.getByRole("status", { name: "正在加载甲图" })).toHaveAttribute("aria-busy", "true");
tick();
expect(screen.queryByText("甲图已就绪")).toBeNull();
tick();
expect(screen.getByText("甲图已就绪")).toBeInTheDocument();
expect(screen.queryByText("乙图已就绪")).toBeNull();
tick(); tick();
expect(screen.getByText("乙图已就绪")).toBeInTheDocument();
expect(screen.queryByRole("status")).toBeNull();
});
it("cancels queued drawings when the task is removed", () => {
const Drawing = vi.fn(() => <p>已过期图表</p>);
const { unmount } = render(<DeferredRender label="加载中"><Drawing /></DeferredRender>);
tick();
unmount();
expect(frames.size).toBe(0);
tick(); tick();
expect(Drawing).not.toHaveBeenCalled();
expect(frames.size).toBe(0);
});
it("defers offscreen content until its own viewport intersection", () => {
let intersect!: IntersectionObserverCallback;
const disconnect = vi.fn();
vi.stubGlobal("IntersectionObserver", class {
constructor(callback: IntersectionObserverCallback) { intersect = callback; }
observe() {}
disconnect = disconnect;
});
render(<DeferredRender label="等待显示"><p>可见图表</p></DeferredRender>);
expect(frames.size).toBe(0);
act(() => intersect([{ isIntersecting: false }] as IntersectionObserverEntry[], {} as IntersectionObserver));
expect(frames.size).toBe(0);
act(() => intersect([{ isIntersecting: true }] as IntersectionObserverEntry[], {} as IntersectionObserver));
tick(); tick();
expect(screen.getByText("可见图表")).toBeInTheDocument();
expect(disconnect).toHaveBeenCalled();
});