fix(editor): Fix reordered switch connections when copying nodes on new canvas (#11788)

This commit is contained in:
Alex Grozav 2024-11-19 14:13:19 +02:00 committed by GitHub
parent de0e86150f
commit 6c2dad7914
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 121 additions and 28 deletions

View file

@ -4,6 +4,7 @@ import type {
INodeTypeDescription, INodeTypeDescription,
IWebhookDescription, IWebhookDescription,
Workflow, Workflow,
INodeConnections,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow'; import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
import { useCanvasOperations } from '@/composables/useCanvasOperations'; import { useCanvasOperations } from '@/composables/useCanvasOperations';
@ -2049,36 +2050,129 @@ describe('useCanvasOperations', () => {
expect(workflowsStore.setNodes).toHaveBeenCalled(); expect(workflowsStore.setNodes).toHaveBeenCalled();
expect(workflowsStore.setConnections).toHaveBeenCalled(); expect(workflowsStore.setConnections).toHaveBeenCalled();
}); });
it('should initialize node data from node type description', () => {
const nodeTypesStore = mockedStore(useNodeTypesStore);
const type = SET_NODE_TYPE;
const version = 1;
const expectedDescription = mockNodeTypeDescription({
name: type,
version,
properties: [
{
displayName: 'Value',
name: 'value',
type: 'boolean',
default: true,
},
],
});
nodeTypesStore.nodeTypes = { [type]: { [version]: expectedDescription } };
const workflow = createTestWorkflow({
nodes: [createTestNode()],
connections: {},
});
const { initializeWorkspace } = useCanvasOperations({ router });
initializeWorkspace(workflow);
expect(workflow.nodes[0].parameters).toEqual({ value: true });
});
}); });
it('should initialize node data from node type description', () => { describe('filterConnectionsByNodes', () => {
const nodeTypesStore = mockedStore(useNodeTypesStore); it('should return filtered connections when all nodes are included', () => {
const type = SET_NODE_TYPE; const connections: INodeConnections = {
const version = 1; [NodeConnectionType.Main]: [
const expectedDescription = mockNodeTypeDescription({ [
name: type, { node: 'node1', type: NodeConnectionType.Main, index: 0 },
version, { node: 'node2', type: NodeConnectionType.Main, index: 0 },
properties: [ ],
{ [{ node: 'node3', type: NodeConnectionType.Main, index: 0 }],
displayName: 'Value', ],
name: 'value', };
type: 'boolean', const includeNodeNames = new Set<string>(['node1', 'node2', 'node3']);
default: true,
}, const { filterConnectionsByNodes } = useCanvasOperations({ router });
], const result = filterConnectionsByNodes(connections, includeNodeNames);
expect(result).toEqual(connections);
}); });
nodeTypesStore.nodeTypes = { [type]: { [version]: expectedDescription } }; it('should return empty connections when no nodes are included', () => {
const connections: INodeConnections = {
[NodeConnectionType.Main]: [
[
{ node: 'node1', type: NodeConnectionType.Main, index: 0 },
{ node: 'node2', type: NodeConnectionType.Main, index: 0 },
],
[{ node: 'node3', type: NodeConnectionType.Main, index: 0 }],
],
};
const includeNodeNames = new Set<string>();
const workflow = createTestWorkflow({ const { filterConnectionsByNodes } = useCanvasOperations({ router });
nodes: [createTestNode()], const result = filterConnectionsByNodes(connections, includeNodeNames);
connections: {},
expect(result).toEqual({
[NodeConnectionType.Main]: [[], []],
});
}); });
const { initializeWorkspace } = useCanvasOperations({ router }); it('should return partially filtered connections when some nodes are included', () => {
initializeWorkspace(workflow); const connections: INodeConnections = {
[NodeConnectionType.Main]: [
[
{ node: 'node1', type: NodeConnectionType.Main, index: 0 },
{ node: 'node2', type: NodeConnectionType.Main, index: 0 },
],
[{ node: 'node3', type: NodeConnectionType.Main, index: 0 }],
],
};
const includeNodeNames = new Set<string>(['node1']);
expect(workflow.nodes[0].parameters).toEqual({ value: true }); const { filterConnectionsByNodes } = useCanvasOperations({ router });
const result = filterConnectionsByNodes(connections, includeNodeNames);
expect(result).toEqual({
[NodeConnectionType.Main]: [
[{ node: 'node1', type: NodeConnectionType.Main, index: 0 }],
[],
],
});
});
it('should handle empty connections input', () => {
const connections: INodeConnections = {};
const includeNodeNames = new Set<string>(['node1']);
const { filterConnectionsByNodes } = useCanvasOperations({ router });
const result = filterConnectionsByNodes(connections, includeNodeNames);
expect(result).toEqual({});
});
it('should handle connections with no valid nodes', () => {
const connections: INodeConnections = {
[NodeConnectionType.Main]: [
[
{ node: 'node4', type: NodeConnectionType.Main, index: 0 },
{ node: 'node5', type: NodeConnectionType.Main, index: 0 },
],
[{ node: 'node6', type: NodeConnectionType.Main, index: 0 }],
],
};
const includeNodeNames = new Set<string>(['node1', 'node2', 'node3']);
const { filterConnectionsByNodes } = useCanvasOperations({ router });
const result = filterConnectionsByNodes(connections, includeNodeNames);
expect(result).toEqual({
[NodeConnectionType.Main]: [[], []],
});
});
}); });
}); });

View file

@ -1809,17 +1809,15 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
} }
function filterConnectionsByNodes( function filterConnectionsByNodes(
connections: Record<string, IConnection[][]>, connections: INodeConnections,
includeNodeNames: Set<string>, includeNodeNames: Set<string>,
): INodeConnections { ): INodeConnections {
const filteredConnections: INodeConnections = {}; const filteredConnections: INodeConnections = {};
for (const [type, typeConnections] of Object.entries(connections)) { for (const [type, typeConnections] of Object.entries(connections)) {
const validConnections = typeConnections const validConnections = typeConnections.map((sourceConnections) =>
.map((sourceConnections) => sourceConnections.filter((connection) => includeNodeNames.has(connection.node)),
sourceConnections.filter((connection) => includeNodeNames.has(connection.node)), );
)
.filter((sourceConnections) => sourceConnections.length > 0);
if (validConnections.length) { if (validConnections.length) {
filteredConnections[type] = validConnections; filteredConnections[type] = validConnections;
@ -1888,6 +1886,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
revertDeleteConnection, revertDeleteConnection,
deleteConnectionsByNodeId, deleteConnectionsByNodeId,
isConnectionAllowed, isConnectionAllowed,
filterConnectionsByNodes,
importWorkflowData, importWorkflowData,
fetchWorkflowDataFromUrl, fetchWorkflowDataFromUrl,
resetWorkspace, resetWorkspace,