Enhance canvas functionality by adding media preview capabilities and image upload handling. Introduce compressed image previews during uploads, improve media library integration, and implement retry logic for bridge edge creation. Update dashboard to display media previews and optimize image node handling.
This commit is contained in:
133
components/canvas/__tests__/canvas-delete-handlers.test.tsx
Normal file
133
components/canvas/__tests__/canvas-delete-handlers.test.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import React, { act, useEffect, useRef, useState } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import type { Edge as RFEdge, Node as RFNode } from "@xyflow/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { Id } from "@/convex/_generated/dataModel";
|
||||
|
||||
const toastInfoMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("@/lib/toast", () => ({
|
||||
toast: {
|
||||
warning: vi.fn(),
|
||||
info: toastInfoMock,
|
||||
},
|
||||
}));
|
||||
|
||||
import { useCanvasDeleteHandlers } from "@/components/canvas/canvas-delete-handlers";
|
||||
|
||||
const asCanvasId = (id: string): Id<"canvases"> => id as Id<"canvases">;
|
||||
|
||||
const latestHandlersRef: {
|
||||
current: ReturnType<typeof useCanvasDeleteHandlers> | null;
|
||||
} = { current: null };
|
||||
|
||||
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
type HookHarnessProps = {
|
||||
runBatchRemoveNodesMutation: ReturnType<typeof vi.fn>;
|
||||
runCreateEdgeMutation: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
function HookHarness({
|
||||
runBatchRemoveNodesMutation,
|
||||
runCreateEdgeMutation,
|
||||
}: HookHarnessProps) {
|
||||
const [nodes] = useState<RFNode[]>([
|
||||
{ id: "node-source", type: "image", position: { x: 0, y: 0 }, data: {} },
|
||||
{ id: "node-middle", type: "note", position: { x: 120, y: 0 }, data: {} },
|
||||
{ id: "node-target", type: "text", position: { x: 240, y: 0 }, data: {} },
|
||||
]);
|
||||
const [edges] = useState<RFEdge[]>([
|
||||
{ id: "edge-source-middle", source: "node-source", target: "node-middle" },
|
||||
{ id: "edge-middle-target", source: "node-middle", target: "node-target" },
|
||||
]);
|
||||
const nodesRef = useRef(nodes);
|
||||
const edgesRef = useRef(edges);
|
||||
const deletingNodeIds = useRef(new Set<string>());
|
||||
const [, setAssetBrowserTargetNodeId] = useState<string | null>(null);
|
||||
|
||||
const handlers = useCanvasDeleteHandlers({
|
||||
t: ((key: string) => key) as never,
|
||||
canvasId: asCanvasId("canvas-1"),
|
||||
nodes,
|
||||
edges,
|
||||
nodesRef,
|
||||
edgesRef,
|
||||
deletingNodeIds,
|
||||
setAssetBrowserTargetNodeId,
|
||||
runBatchRemoveNodesMutation,
|
||||
runCreateEdgeMutation,
|
||||
runRemoveEdgeMutation: vi.fn(async () => undefined),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
latestHandlersRef.current = handlers;
|
||||
}, [handlers]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
describe("useCanvasDeleteHandlers", () => {
|
||||
let container: HTMLDivElement | null = null;
|
||||
let root: Root | null = null;
|
||||
|
||||
afterEach(async () => {
|
||||
latestHandlersRef.current = null;
|
||||
vi.useRealTimers();
|
||||
vi.clearAllMocks();
|
||||
if (root) {
|
||||
await act(async () => {
|
||||
root?.unmount();
|
||||
});
|
||||
}
|
||||
container?.remove();
|
||||
root = null;
|
||||
container = null;
|
||||
});
|
||||
|
||||
it("retries bridge edge creation when the first create fails", async () => {
|
||||
vi.useFakeTimers();
|
||||
const runBatchRemoveNodesMutation = vi.fn(async () => undefined);
|
||||
const runCreateEdgeMutation = vi
|
||||
.fn(async () => undefined)
|
||||
.mockRejectedValueOnce(new Error("incoming limit reached"));
|
||||
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await act(async () => {
|
||||
root?.render(
|
||||
<HookHarness
|
||||
runBatchRemoveNodesMutation={runBatchRemoveNodesMutation}
|
||||
runCreateEdgeMutation={runCreateEdgeMutation}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
latestHandlersRef.current?.onNodesDelete([
|
||||
{
|
||||
id: "node-middle",
|
||||
type: "note",
|
||||
position: { x: 120, y: 0 },
|
||||
data: {},
|
||||
},
|
||||
]);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
vi.runAllTimers();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(runBatchRemoveNodesMutation).toHaveBeenCalledTimes(1);
|
||||
expect(runCreateEdgeMutation).toHaveBeenCalledTimes(2);
|
||||
expect(toastInfoMock).toHaveBeenCalledWith("canvas.nodesRemoved");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user