mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
test(editor): Add canvas dragging tests (no-changelog) (#5406)
* WIP * Fix undo/redo tests and add canvas tests * Cleanup * Cleanup * Use drag and draganddrop commands * Fix hanging CI run * Undo redo spec
This commit is contained in:
@@ -4,9 +4,13 @@ export const DEFAULT_USER_EMAIL = 'nathan@n8n.io';
|
||||
export const DEFAULT_USER_PASSWORD = 'CypressTest123';
|
||||
|
||||
export const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger';
|
||||
export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Execute Workflow"';
|
||||
export const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger';
|
||||
export const CODE_NODE_NAME = 'Code';
|
||||
export const SET_NODE_NAME = 'Set';
|
||||
export const IF_NODE_NAME = 'IF';
|
||||
export const MERGE_NODE_NAME = 'Merge';
|
||||
export const SWITCH_NODE_NAME = 'Switch';
|
||||
export const GMAIL_NODE_NAME = 'Gmail';
|
||||
export const TRELLO_NODE_NAME = 'Trello';
|
||||
export const NOTION_NODE_NAME = 'Notion';
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import {
|
||||
MANUAL_TRIGGER_NODE_NAME,
|
||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||
CODE_NODE_NAME,
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
SET_NODE_NAME,
|
||||
SWITCH_NODE_NAME,
|
||||
IF_NODE_NAME,
|
||||
MERGE_NODE_NAME,
|
||||
HTTP_REQUEST_NODE_NAME,
|
||||
} from './../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
@@ -33,12 +37,125 @@ describe('Canvas Actions', () => {
|
||||
WorkflowPage.getters.executeWorkflowButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should connect and disconnect a simple node', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
|
||||
WorkflowPage.getters.nodeViewBackground().click(600, 200, { force: true });
|
||||
cy.get('.jtk-connector').should('have.length', 1);
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
|
||||
|
||||
// Change connection from Set to Set1
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('input', SET_NODE_NAME),
|
||||
WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`)
|
||||
)
|
||||
|
||||
WorkflowPage.getters.canvasNodeInputEndpointByName(`${SET_NODE_NAME}1`).should('have.class', 'jtk-endpoint-connected');
|
||||
|
||||
cy.get('.jtk-connector').should('have.length', 1);
|
||||
// Disconnect Set1
|
||||
cy.drag(WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`), [-200, 100])
|
||||
cy.get('.jtk-connector').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should add first step', () => {
|
||||
WorkflowPage.getters.canvasPlusButton().should('be.visible');
|
||||
WorkflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should add a node via plus endpoint drag', () => {
|
||||
WorkflowPage.getters.canvasPlusButton().should('be.visible');
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, true);
|
||||
|
||||
cy.drag(WorkflowPage.getters.getEndpointSelector('plus', SCHEDULE_TRIGGER_NODE_NAME), [100, 100])
|
||||
|
||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
WorkflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false);
|
||||
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
||||
});
|
||||
|
||||
it('should add switch node and test connections', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true);
|
||||
|
||||
// Switch has 4 output endpoints
|
||||
for (let i = 0; i < 4; i++) {
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i).click({ force: true })
|
||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME, false);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
}
|
||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||
cy.reload()
|
||||
cy.waitForLoad();
|
||||
// Make sure all connections are there after reload
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const setName = `${SET_NODE_NAME}${i > 0 ? i : ''}`;
|
||||
WorkflowPage.getters.canvasNodeInputEndpointByName(setName).should('have.class', 'jtk-endpoint-connected');
|
||||
}
|
||||
});
|
||||
|
||||
it('should add merge node and test connections', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
|
||||
WorkflowPage.getters.nodeViewBackground().click(600 + i * 100, 200, { force: true });
|
||||
}
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(MERGE_NODE_NAME);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
// Connect manual to Set1
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('output', MANUAL_TRIGGER_NODE_DISPLAY_NAME),
|
||||
WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`)
|
||||
)
|
||||
|
||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 2);
|
||||
|
||||
// Connect Set1 and Set2 to merge
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('plus', SET_NODE_NAME),
|
||||
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 0)
|
||||
)
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('plus', `${SET_NODE_NAME}1`),
|
||||
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1)
|
||||
)
|
||||
|
||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4);
|
||||
|
||||
// Make sure all connections are there after save & reload
|
||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||
cy.reload()
|
||||
cy.waitForLoad();
|
||||
|
||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4);
|
||||
WorkflowPage.actions.executeWorkflow();
|
||||
// If the merged set nodes are connected and executed correctly, there should be 2 items in the output of merge node
|
||||
cy.get('[data-label="2 items"]').should('be.visible');
|
||||
});
|
||||
|
||||
it('should add nodes and check execution success', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
|
||||
}
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.executeWorkflow();
|
||||
|
||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
||||
cy.get('.data-count').should('have.length', 4);
|
||||
cy.get('.plus-draggable-endpoint').should('have.class', 'ep-success');
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
|
||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
||||
cy.get('.jtk-connector').should('have.length', 4);
|
||||
})
|
||||
|
||||
it('should add a connected node using plus endpoint', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
cy.get('.plus-endpoint').should('be.visible').click();
|
||||
@@ -226,7 +343,7 @@ describe('Canvas Actions', () => {
|
||||
it('should delete a connection by moving it away from endpoint', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.drag('.rect-input-endpoint.jtk-endpoint-connected', [0, -100]);
|
||||
cy.drag(WorkflowPage.getters.getEndpointSelector('input', CODE_NODE_NAME), [0, -100]);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,6 +24,18 @@ export class WorkflowPage extends BasePage {
|
||||
canvasNodes: () => cy.getByTestId('canvas-node'),
|
||||
canvasNodeByName: (nodeName: string) =>
|
||||
this.getters.canvasNodes().filter(`:contains("${nodeName}")`),
|
||||
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
|
||||
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`
|
||||
},
|
||||
canvasNodeInputEndpointByName: (nodeName: string, index = 0) => {
|
||||
return cy.get(this.getters.getEndpointSelector('input', nodeName, index));
|
||||
},
|
||||
canvasNodeOutputEndpointByName: (nodeName: string, index = 0) => {
|
||||
return cy.get(this.getters.getEndpointSelector('output', nodeName, index));
|
||||
},
|
||||
canvasNodePlusEndpointByName: (nodeName: string, index = 0) => {
|
||||
return cy.get(this.getters.getEndpointSelector('plus', nodeName, index));
|
||||
},
|
||||
successToast: () => cy.get('.el-notification .el-icon-success').parent(),
|
||||
errorToast: () => cy.get('.el-notification .el-icon-error'),
|
||||
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
|
||||
@@ -122,7 +134,9 @@ export class WorkflowPage extends BasePage {
|
||||
this.getters.workflowMenu().click();
|
||||
},
|
||||
saveWorkflowOnButtonClick: () => {
|
||||
this.getters.saveButton().should('contain', 'Save');
|
||||
this.getters.saveButton().click();
|
||||
this.getters.saveButton().should('contain', 'Saved')
|
||||
},
|
||||
saveWorkflowUsingKeyboardShortcut: () => {
|
||||
cy.get('body').type('{meta}', { release: false }).type('s');
|
||||
|
||||
@@ -187,23 +187,27 @@ Cypress.Commands.add('drag', (selector, pos) => {
|
||||
pageY: originalLocation.top + yDiff,
|
||||
force: true,
|
||||
});
|
||||
element.trigger('mouseup');
|
||||
element.trigger('mouseup', { force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => {
|
||||
cy.get(draggableSelector).should('exist');
|
||||
cy.get(droppableSelector).should('exist');
|
||||
|
||||
const droppableEl = Cypress.$(droppableSelector)[0];
|
||||
const coords = droppableEl.getBoundingClientRect();
|
||||
cy.get(droppableSelector).first().then(([$el]) => {
|
||||
const coords = $el.getBoundingClientRect();
|
||||
|
||||
const pageX = coords.left + coords.width / 2;
|
||||
const pageY = coords.top + coords.height / 2;
|
||||
const pageX = coords.left + coords.width / 2;
|
||||
const pageY = coords.top + coords.height / 2;
|
||||
|
||||
cy.get(draggableSelector).realMouseDown();
|
||||
cy.get(droppableSelector).realMouseMove(pageX, pageY)
|
||||
.realHover()
|
||||
.realMouseUp();
|
||||
// We can't use realMouseDown here because it hangs headless run
|
||||
cy.get(draggableSelector).trigger('mousedown');
|
||||
// We don't chain these commands to make sure cy.get is re-trying correctly
|
||||
cy.get(droppableSelector).realMouseMove(pageX, pageY)
|
||||
cy.get(droppableSelector).realHover()
|
||||
cy.get(droppableSelector).realMouseUp();
|
||||
cy.get(draggableSelector).realMouseUp();
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user