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,
IWebhookDescription,
Workflow,
INodeConnections,
} from 'n8n-workflow';
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
import { useCanvasOperations } from '@/composables/useCanvasOperations';
@ -2049,36 +2050,129 @@ describe('useCanvasOperations', () => {
expect(workflowsStore.setNodes).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', () => {
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,
},
],
describe('filterConnectionsByNodes', () => {
it('should return filtered connections when all 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>(['node1', 'node2', 'node3']);
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({
nodes: [createTestNode()],
connections: {},
const { filterConnectionsByNodes } = useCanvasOperations({ router });
const result = filterConnectionsByNodes(connections, includeNodeNames);
expect(result).toEqual({
[NodeConnectionType.Main]: [[], []],
});
});
const { initializeWorkspace } = useCanvasOperations({ router });
initializeWorkspace(workflow);
it('should return partially filtered connections when some 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>(['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(
connections: Record<string, IConnection[][]>,
connections: INodeConnections,
includeNodeNames: Set<string>,
): INodeConnections {
const filteredConnections: INodeConnections = {};
for (const [type, typeConnections] of Object.entries(connections)) {
const validConnections = typeConnections
.map((sourceConnections) =>
sourceConnections.filter((connection) => includeNodeNames.has(connection.node)),
)
.filter((sourceConnections) => sourceConnections.length > 0);
const validConnections = typeConnections.map((sourceConnections) =>
sourceConnections.filter((connection) => includeNodeNames.has(connection.node)),
);
if (validConnections.length) {
filteredConnections[type] = validConnections;
@ -1888,6 +1886,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
revertDeleteConnection,
deleteConnectionsByNodeId,
isConnectionAllowed,
filterConnectionsByNodes,
importWorkflowData,
fetchWorkflowDataFromUrl,
resetWorkspace,