|
|
|
@ -595,6 +595,39 @@ describe("Undo/Redo integration", () => { |
|
|
|
expect(Object.keys(store.groups).length).toBe(0); |
|
|
|
}); |
|
|
|
|
|
|
|
it("redoes creating a group", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 0, y: 0 }); |
|
|
|
store.addNode("imageInput", { x: 100, y: 0 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const nodeIds = store.nodes.map((n) => n.id); |
|
|
|
let groupId = ""; |
|
|
|
|
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(nodeIds); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId]).toBeUndefined(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId]).toBeDefined(); |
|
|
|
expect(store.nodes.some((node) => node.id === `group-node-${groupId}`)).toBe(true); |
|
|
|
expect(store.nodes.filter((node) => node.groupId === groupId)).toHaveLength(2); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes deleting a group", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
@ -626,6 +659,283 @@ describe("Undo/Redo integration", () => { |
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(Object.keys(store.groups).length).toBe(1); |
|
|
|
}); |
|
|
|
|
|
|
|
it("redoes deleting a group", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 0, y: 0 }); |
|
|
|
}); |
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const nodeIds = store.nodes.map((n) => n.id); |
|
|
|
|
|
|
|
let groupId = ""; |
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(nodeIds); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.deleteGroup(groupId); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId]).toBeDefined(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId]).toBeUndefined(); |
|
|
|
expect(store.nodes.some((node) => node.id === `group-node-${groupId}`)).toBe(false); |
|
|
|
expect(store.nodes.every((node) => node.groupId !== groupId && node.parentId !== `group-node-${groupId}`)).toBe(true); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes and redoes removing nodes from a group", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 0, y: 0 }); |
|
|
|
store.addNode("imageInput", { x: 100, y: 0 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const nodeIds = store.nodes.map((n) => n.id); |
|
|
|
const nodeIdToRemove = nodeIds[0]; |
|
|
|
let groupId = ""; |
|
|
|
|
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(nodeIds); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.removeNodesFromGroup([nodeIdToRemove]); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === nodeIdToRemove)?.groupId).toBeUndefined(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === nodeIdToRemove)?.groupId).toBe(groupId); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === nodeIdToRemove)?.groupId).toBeUndefined(); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes and redoes adding nodes to a group", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 0, y: 0 }); |
|
|
|
store.addNode("imageInput", { x: 300, y: 0 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const [firstNodeId, secondNodeId] = store.nodes.map((n) => n.id); |
|
|
|
let groupId = ""; |
|
|
|
|
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup([firstNodeId]); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNodesToGroup([secondNodeId], groupId); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === secondNodeId)?.groupId).toBe(groupId); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === secondNodeId)?.groupId).toBeUndefined(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.nodes.find((node) => node.id === secondNodeId)?.groupId).toBe(groupId); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes and redoes group metadata changes", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 0, y: 0 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
let groupId = ""; |
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(store.nodes.map((n) => n.id)); |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.updateGroup(groupId, { name: "Updated group", color: "purple", locked: true }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].name).toBe("Updated group"); |
|
|
|
expect(store.groups[groupId].color).toBe("purple"); |
|
|
|
expect(store.groups[groupId].locked).toBe(true); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].name).not.toBe("Updated group"); |
|
|
|
expect(store.groups[groupId].color).not.toBe("purple"); |
|
|
|
expect(store.groups[groupId].locked).toBeUndefined(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].name).toBe("Updated group"); |
|
|
|
expect(store.groups[groupId].color).toBe("purple"); |
|
|
|
expect(store.groups[groupId].locked).toBe(true); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes and redoes moving a group through the store API", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 50, y: 80 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
let groupId = ""; |
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(store.nodes.map((n) => n.id)); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const originalPosition = store.groups[groupId].position; |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.moveGroupNodes(groupId, { x: 40, y: 25 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].position).toEqual({ |
|
|
|
x: originalPosition.x + 40, |
|
|
|
y: originalPosition.y + 25, |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].position).toEqual(originalPosition); |
|
|
|
expect(store.nodes.find((node) => node.id === `group-node-${groupId}`)?.position).toEqual(originalPosition); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].position).toEqual({ |
|
|
|
x: originalPosition.x + 40, |
|
|
|
y: originalPosition.y + 25, |
|
|
|
}); |
|
|
|
expect(store.nodes.find((node) => node.id === `group-node-${groupId}`)?.position).toEqual({ |
|
|
|
x: originalPosition.x + 40, |
|
|
|
y: originalPosition.y + 25, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
it("undoes and redoes resizing a group from React Flow dimensions changes", () => { |
|
|
|
let store = useWorkflowStore.getState(); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.addNode("prompt", { x: 50, y: 80 }); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
let groupId = ""; |
|
|
|
act(() => { |
|
|
|
groupId = store.createGroup(store.nodes.map((n) => n.id)); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
const groupNodeId = `group-node-${groupId}`; |
|
|
|
const originalSize = store.groups[groupId].size; |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.onNodesChange([ |
|
|
|
{ |
|
|
|
id: groupNodeId, |
|
|
|
type: "dimensions", |
|
|
|
dimensions: { |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}, |
|
|
|
resizing: true, |
|
|
|
setAttributes: true, |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: groupNodeId, |
|
|
|
type: "dimensions", |
|
|
|
dimensions: { |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}, |
|
|
|
resizing: false, |
|
|
|
setAttributes: true, |
|
|
|
}, |
|
|
|
]); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].size).toEqual({ |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}); |
|
|
|
expect(store.nodes.find((node) => node.id === groupNodeId)?.style).toMatchObject({ |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.undo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].size).toEqual(originalSize); |
|
|
|
expect(store.nodes.find((node) => node.id === groupNodeId)?.style).toMatchObject(originalSize); |
|
|
|
|
|
|
|
act(() => { |
|
|
|
store.redo(); |
|
|
|
}); |
|
|
|
|
|
|
|
store = useWorkflowStore.getState(); |
|
|
|
expect(store.groups[groupId].size).toEqual({ |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}); |
|
|
|
expect(store.nodes.find((node) => node.id === groupNodeId)?.style).toMatchObject({ |
|
|
|
width: originalSize.width + 80, |
|
|
|
height: originalSize.height + 40, |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
describe("hasUnsavedChanges is set on undo/redo", () => { |
|
|
|
|