fix(editor): Fix copy/paste issue when switch node is in workflow (#8103)

## Summary
Fix bug where copy/pasting a node breaks the view. Issue seems to be
that copied workflow does not have Switch node when workflow is
rerendering..

<img width="1942" alt="Screenshot 2023-12-19 at 18 16 23"
src="https://github.com/n8n-io/n8n/assets/4711238/ad53b8ae-3693-4733-8f6b-7bc9e7b9d216">




## Related tickets and issues
[Linear ticket
ADO-1504](https://linear.app/n8n/issue/ADO-1504/unwanted-nodes-connections-after-copypaste-of-n8n-form-trigger)


## Review / Merge checklist
- [X] PR title and summary are descriptive. **Remember, the title
automatically goes into the changelog. Use `(no-changelog)` otherwise.**
([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md))
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up
ticket created.
- [x] Tests included.
> A bug is not considered fixed, unless a test is added to prevent it
from happening again.
   > A feature is not complete without tests.
This commit is contained in:
Mutasem Aldmour 2023-12-20 10:13:59 +01:00 committed by GitHub
parent 19b7f1ffb1
commit 4b86926752
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 6 deletions

View file

@ -325,4 +325,33 @@ describe('Undo/Redo', () => {
WorkflowPage.getters.nodeConnections().should('have.length', 2); WorkflowPage.getters.nodeConnections().should('have.length', 2);
}); });
}); });
it('should be able to copy and paste pinned data nodes in workflows with dynamic Switch node', () => {
cy.fixture('Test_workflow_form_switch.json').then((data) => {
cy.get('body').paste(JSON.stringify(data));
});
WorkflowPage.actions.zoomToFit();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1);
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch'))
.should('have.css', 'left', `637px`)
.should('have.css', 'top', `501px`);
cy.fixture('Test_workflow_form_switch.json').then((data) => {
cy.get('body').paste(JSON.stringify(data));
});
WorkflowPage.getters.canvasNodes().should('have.length', 4);
WorkflowPage.getters.nodeConnections().should('have.length', 2);
WorkflowPage.actions.hitUndo();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1);
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch'))
.should('have.css', 'left', `637px`)
.should('have.css', 'top', `501px`);
});
}); });

View file

@ -0,0 +1,78 @@
{
"name": "My workflow 8",
"nodes": [
{
"parameters": {
"path": "d1cba915-ca18-4425-bcfb-133205fc815a",
"formTitle": "test",
"formFields": {
"values": [
{
"fieldLabel": "test"
}
]
},
"options": {}
},
"id": "9e685367-fb94-4376-a9a4-7f311d9f7e2d",
"name": "n8n Form Trigger",
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2,
"position": [
620,
580
],
"webhookId": "d1cba915-ca18-4425-bcfb-133205fc815a"
},
{
"parameters": {},
"id": "0f4dfe66-51c0-4378-9eab-680f8140a572",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
800,
580
]
}
],
"pinData": {
"n8n Form Trigger": [
{
"json": {
"name": "First item",
"code": 1
}
},
{
"json": {
"name": "Second item",
"code": 2
}
}
]
},
"connections": {
"n8n Form Trigger": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "d6c14bc8-a69f-47bb-b5ba-fe6e9db0a3a4",
"id": "UQSimcMQJGbTeTLG",
"meta": {
"instanceId": "a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0"
},
"tags": []
}

View file

@ -351,9 +351,6 @@ export class WorkflowPage extends BasePage {
hitCopy: () => { hitCopy: () => {
cy.get('body').type(META_KEY, { delay: 500, release: false }).type('c'); cy.get('body').type(META_KEY, { delay: 500, release: false }).type('c');
}, },
hitPaste: () => {
cy.get('body').type(META_KEY, { delay: 500, release: false }).type('P');
},
hitPinNodeShortcut: () => { hitPinNodeShortcut: () => {
cy.get('body').type('p'); cy.get('body').type('p');
}, },

View file

@ -377,9 +377,10 @@ export default defineComponent({
styles['--configurable-node-input-count'] = nonMainInputs.length + spacerCount; styles['--configurable-node-input-count'] = nonMainInputs.length + spacerCount;
} }
const outputs = let outputs = [] as Array<ConnectionTypes | INodeOutputConfiguration>;
NodeHelpers.getNodeOutputs(this.workflow, this.node, this.nodeType) || if (this.workflow.nodes[this.node.name]) {
([] as Array<ConnectionTypes | INodeOutputConfiguration>); outputs = NodeHelpers.getNodeOutputs(this.workflow, this.node, this.nodeType);
}
const outputTypes = NodeHelpers.getConnectionTypes(outputs); const outputTypes = NodeHelpers.getConnectionTypes(outputs);

View file

@ -96,6 +96,9 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
}, },
isConfigNode() { isConfigNode() {
return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => { return (workflow: Workflow, node: INode, nodeTypeName: string): boolean => {
if (!workflow.nodes[node.name]) {
return false;
}
const nodeType = this.getNodeType(nodeTypeName); const nodeType = this.getNodeType(nodeTypeName);
if (!nodeType) { if (!nodeType) {
return false; return false;