mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-02 08:27:29 -08:00
fix(editor): Fix reordered switch connections when copying nodes on new canvas (#11788)
This commit is contained in:
parent
de0e86150f
commit
6c2dad7914
|
@ -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]: [[], []],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue