test(canvas): expand reconciliation helper coverage

This commit is contained in:
2026-04-03 20:51:21 +02:00
parent 297816c5ca
commit f2f22b66a7
2 changed files with 194 additions and 1 deletions

View File

@@ -8,6 +8,7 @@ import {
} from "@/components/canvas/canvas-flow-reconciliation-helpers"; } from "@/components/canvas/canvas-flow-reconciliation-helpers";
const asNodeId = (id: string): Id<"nodes"> => id as Id<"nodes">; const asNodeId = (id: string): Id<"nodes"> => id as Id<"nodes">;
const asEdgeId = (id: string) => id as Id<"edges">;
describe("canvas flow reconciliation helpers", () => { describe("canvas flow reconciliation helpers", () => {
it("carries an optimistic edge while a pending connection create awaits convex edge sync", () => { it("carries an optimistic edge while a pending connection create awaits convex edge sync", () => {
@@ -81,6 +82,146 @@ describe("canvas flow reconciliation helpers", () => {
]); ]);
}); });
it("suppresses carried optimistic edges when convex already has the same connection signature", () => {
const result = reconcileCanvasFlowEdges({
previousEdges: [
{
id: "optimistic_edge_req-3",
source: "optimistic_req-3",
target: "node-target",
},
],
convexEdges: [
{
_id: asEdgeId("edge-1"),
sourceNodeId: asNodeId("node-real"),
targetNodeId: asNodeId("node-target"),
sourceHandle: undefined,
targetHandle: undefined,
},
],
convexNodes: [
{ _id: asNodeId("node-real"), type: "image" },
{ _id: asNodeId("node-target"), type: "prompt" },
],
previousConvexNodeIdsSnapshot: new Set(["node-real", "node-target"]),
pendingRemovedEdgeIds: new Set(),
pendingConnectionCreateIds: new Set(),
resolvedRealIdByClientRequest: new Map([["req-3", asNodeId("node-real")]]),
localNodeIds: new Set(),
isAnyNodeDragging: false,
colorMode: "light",
});
expect(result.edges).toHaveLength(1);
expect(result.edges[0]).toMatchObject({
id: "edge-1",
source: "node-real",
target: "node-target",
});
});
it("preserves temp edges while filtering pending removed convex edges", () => {
const result = reconcileCanvasFlowEdges({
previousEdges: [
{
id: "temp-edge-1",
source: "node-a",
target: "node-b",
className: "temp",
},
],
convexEdges: [
{
_id: asEdgeId("edge-removed"),
sourceNodeId: asNodeId("node-a"),
targetNodeId: asNodeId("node-b"),
sourceHandle: undefined,
targetHandle: undefined,
},
],
convexNodes: [
{ _id: asNodeId("node-a"), type: "image" },
{ _id: asNodeId("node-b"), type: "prompt" },
],
previousConvexNodeIdsSnapshot: new Set(["node-a", "node-b"]),
pendingRemovedEdgeIds: new Set(["edge-removed"]),
pendingConnectionCreateIds: new Set(),
resolvedRealIdByClientRequest: new Map(),
localNodeIds: new Set(),
isAnyNodeDragging: false,
colorMode: "light",
});
expect(result.edges).toEqual([
{
id: "temp-edge-1",
source: "node-a",
target: "node-b",
className: "temp",
},
]);
});
it("keeps optimistic endpoints for local dragging nodes and still carries the edge", () => {
const result = reconcileCanvasFlowEdges({
previousEdges: [
{
id: "optimistic_edge_req-drag",
source: "node-source",
target: "optimistic_req-drag",
},
],
convexEdges: [],
convexNodes: [{ _id: asNodeId("node-source"), type: "image" }],
previousConvexNodeIdsSnapshot: new Set(["node-source"]),
pendingRemovedEdgeIds: new Set(),
pendingConnectionCreateIds: new Set(["req-drag"]),
resolvedRealIdByClientRequest: new Map([["req-drag", asNodeId("node-real")]]),
localNodeIds: new Set(["optimistic_req-drag"]),
isAnyNodeDragging: true,
colorMode: "light",
});
expect(result.edges).toEqual([
{
id: "optimistic_edge_req-drag",
source: "node-source",
target: "optimistic_req-drag",
},
]);
});
it("reports settled pending connection creates once convex has the real node and edge", () => {
const result = reconcileCanvasFlowEdges({
previousEdges: [],
convexEdges: [
{
_id: asEdgeId("edge-settled"),
sourceNodeId: asNodeId("node-source"),
targetNodeId: asNodeId("node-real"),
sourceHandle: undefined,
targetHandle: undefined,
},
],
convexNodes: [
{ _id: asNodeId("node-source"), type: "image" },
{ _id: asNodeId("node-real"), type: "prompt" },
],
previousConvexNodeIdsSnapshot: new Set(["node-source", "node-real"]),
pendingRemovedEdgeIds: new Set(),
pendingConnectionCreateIds: new Set(["req-settled"]),
resolvedRealIdByClientRequest: new Map([
["req-settled", asNodeId("node-real")],
]),
localNodeIds: new Set(),
isAnyNodeDragging: false,
colorMode: "light",
});
expect(result.settledPendingConnectionCreateIds).toEqual(["req-settled"]);
});
it("cleans up matched local position pins once convex catches up", () => { it("cleans up matched local position pins once convex catches up", () => {
const previousNodes: RFNode[] = [ const previousNodes: RFNode[] = [
{ {
@@ -114,4 +255,56 @@ describe("canvas flow reconciliation helpers", () => {
expect(result.nodes).toEqual(incomingNodes); expect(result.nodes).toEqual(incomingNodes);
expect(result.nextPendingLocalPositionPins.size).toBe(0); expect(result.nextPendingLocalPositionPins.size).toBe(0);
}); });
it("filters deleting nodes from incoming reconciliation results", () => {
const result = reconcileCanvasFlowNodes({
previousNodes: [
{
id: "node-keep",
type: "image",
position: { x: 0, y: 0 },
data: {},
},
{
id: "node-delete",
type: "image",
position: { x: 40, y: 40 },
data: {},
},
],
incomingNodes: [
{
id: "node-keep",
type: "image",
position: { x: 0, y: 0 },
data: {},
},
{
id: "node-delete",
type: "image",
position: { x: 40, y: 40 },
data: {},
},
],
convexNodes: [
{ _id: asNodeId("node-keep"), type: "image" },
{ _id: asNodeId("node-delete"), type: "image" },
],
deletingNodeIds: new Set(["node-delete"]),
resolvedRealIdByClientRequest: new Map(),
pendingConnectionCreateIds: new Set(),
preferLocalPositionNodeIds: new Set(),
pendingLocalPositionPins: new Map(),
pendingMovePins: new Map(),
});
expect(result.nodes).toEqual([
{
id: "node-keep",
type: "image",
position: { x: 0, y: 0 },
data: {},
},
]);
});
}); });

View File

@@ -11,7 +11,7 @@ export default defineConfig({
environment: "node", environment: "node",
include: [ include: [
"tests/**/*.test.ts", "tests/**/*.test.ts",
"components/**/__tests__/*.test.ts", "components/canvas/__tests__/canvas-flow-reconciliation-helpers.test.ts",
], ],
}, },
}); });