perf(canvas): reduce Convex hot-path query load
This commit is contained in:
115
tests/use-canvas-data.test.ts
Normal file
115
tests/use-canvas-data.test.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import React, { act, useEffect } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const useQueryMock = vi.hoisted(() => vi.fn());
|
||||
const resolveStorageUrlsForCanvasMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("@/convex/_generated/api", () => ({
|
||||
api: {
|
||||
canvasGraph: { get: "canvasGraph.get" },
|
||||
canvases: { get: "canvases.get" },
|
||||
storage: { batchGetUrlsForCanvas: "storage.batchGetUrlsForCanvas" },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("convex/react", () => ({
|
||||
useConvexAuth: () => ({ isLoading: false, isAuthenticated: true }),
|
||||
useMutation: () => resolveStorageUrlsForCanvasMock,
|
||||
useQuery: useQueryMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/auth-client", () => ({
|
||||
authClient: {
|
||||
useSession: () => ({
|
||||
data: { user: { email: "user@example.com" } },
|
||||
isPending: false,
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
import { useCanvasData } from "@/components/canvas/use-canvas-data";
|
||||
|
||||
const latestHookValue: {
|
||||
current: ReturnType<typeof useCanvasData> | null;
|
||||
} = { current: null };
|
||||
|
||||
function HookHarness() {
|
||||
const value = useCanvasData({ canvasId: "canvas_1" as never });
|
||||
|
||||
useEffect(() => {
|
||||
latestHookValue.current = value;
|
||||
return () => {
|
||||
latestHookValue.current = null;
|
||||
};
|
||||
}, [value]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
describe("useCanvasData", () => {
|
||||
let container: HTMLDivElement | null = null;
|
||||
let root: Root | null = null;
|
||||
|
||||
afterEach(async () => {
|
||||
if (root) {
|
||||
await act(async () => {
|
||||
root?.unmount();
|
||||
});
|
||||
}
|
||||
container?.remove();
|
||||
container = null;
|
||||
root = null;
|
||||
latestHookValue.current = null;
|
||||
useQueryMock.mockReset();
|
||||
resolveStorageUrlsForCanvasMock.mockReset();
|
||||
});
|
||||
|
||||
it("subscribes to the shared graph query and derives nodes and edges from it", async () => {
|
||||
const graph = {
|
||||
nodes: [
|
||||
{
|
||||
_id: "node_1",
|
||||
canvasId: "canvas_1",
|
||||
data: { storageId: "storage_1" },
|
||||
},
|
||||
],
|
||||
edges: [{ _id: "edge_1", canvasId: "canvas_1" }],
|
||||
};
|
||||
|
||||
useQueryMock.mockImplementation((query: string) => {
|
||||
if (query === "canvasGraph.get") {
|
||||
return graph;
|
||||
}
|
||||
if (query === "canvases.get") {
|
||||
return { _id: "canvas_1" };
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
resolveStorageUrlsForCanvasMock.mockResolvedValue({ storage_1: "https://cdn.example.com/1" });
|
||||
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await act(async () => {
|
||||
root?.render(React.createElement(HookHarness));
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(useQueryMock).toHaveBeenCalledWith("canvasGraph.get", { canvasId: "canvas_1" });
|
||||
expect(latestHookValue.current?.convexNodes).toEqual(graph.nodes);
|
||||
expect(latestHookValue.current?.convexEdges).toEqual(graph.edges);
|
||||
expect(resolveStorageUrlsForCanvasMock).toHaveBeenCalledWith({
|
||||
canvasId: "canvas_1",
|
||||
storageIds: ["storage_1"],
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user