n8n/cypress/e2e/12-canvas.cy.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

474 lines
18 KiB
TypeScript
Raw Normal View History

import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { NDV, WorkflowExecutionsTab } from '../pages';
import {
MANUAL_TRIGGER_NODE_NAME,
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
CODE_NODE_NAME,
SCHEDULE_TRIGGER_NODE_NAME,
EDIT_FIELDS_SET_NODE_NAME,
SWITCH_NODE_NAME,
MERGE_NODE_NAME,
} from './../constants';
const WorkflowPage = new WorkflowPageClass();
const ExecutionsTab = new WorkflowExecutionsTab();
const NDVDialog = new NDV();
const DEFAULT_ZOOM_FACTOR = 1;
const ZOOM_IN_X1_FACTOR = 1.25; // Zoom in factor after one click
const ZOOM_IN_X2_FACTOR = 1.5625; // Zoom in factor after two clicks
const ZOOM_OUT_X1_FACTOR = 0.8;
const ZOOM_OUT_X2_FACTOR = 0.64;
const PINCH_ZOOM_IN_FACTOR = 1.05702;
const PINCH_ZOOM_OUT_FACTOR = 0.946058;
const RENAME_NODE_NAME = 'Something else';
const RENAME_NODE_NAME2 = 'Something different';
describe('Canvas Node Manipulation and Navigation', () => {
beforeEach(() => {
WorkflowPage.actions.visit();
});
it('should add switch node and test connections', () => {
const desiredOutputs = 4;
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true, true);
for (let i = 0; i < desiredOutputs; i++) {
cy.contains('Add Routing Rule').click();
}
NDVDialog.actions.close();
for (let i = 0; i < desiredOutputs; i++) {
WorkflowPage.getters.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i).click({ force: true });
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
WorkflowPage.actions.zoomToFit();
}
WorkflowPage.getters.nodeViewBackground().click({ force: true });
WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false);
WorkflowPage.actions.saveWorkflowOnButtonClick();
cy.reload();
cy.waitForLoad();
// Make sure outputless switch was connected correctly
cy.get(
`[data-target-node="${SWITCH_NODE_NAME}1"][data-source-node="${EDIT_FIELDS_SET_NODE_NAME}3"]`,
).should('be.visible');
// Make sure all connections are there after reload
for (let i = 0; i < desiredOutputs; i++) {
const setName = `${EDIT_FIELDS_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);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
for (let i = 0; i < 2; i++) {
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true);
WorkflowPage.getters
.nodeViewBackground()
.click((i + 1) * 200, (i + 1) * 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', `${EDIT_FIELDS_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', EDIT_FIELDS_SET_NODE_NAME),
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 0),
);
cy.draganddrop(
WorkflowPage.getters.getEndpointSelector('plus', `${EDIT_FIELDS_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();
WorkflowPage.getters.stopExecutionButton().should('not.exist');
// 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);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
for (let i = 0; i < 3; i++) {
WorkflowPage.actions.addNodeToCanvas(CODE_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(CODE_NODE_NAME);
WorkflowPage.actions.zoomToFit();
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
feat(editor): Migrate Design System and Editor UI to Vue 3 (#6476) * feat: remove vue-fragment (no-changelog) * feat: partial design-system migration * feat: migrate info-accordion and info-tip components * feat: migrate several components to vue 3 * feat: migrated several components * feat: migrate several components * feat: migrate several components * feat: migrate several components * feat: re-exported all design system components * fix: fix design for popper components * fix: editor kind of working, lots of issues to fix * fix: fix several vue 3 migration issues * fix: replace @change with @update:modelValue in several places * fix: fix translation linking * fix: fix inline-edit input * fix: fix ndv and dialog design * fix: update parameter input event bindings * fix: rename deprecated lifecycle methods * fix: fix json view mapping * build: update lock file * fix(editor): revisit last conflict with master and fix issues * fix(editor): revisit last conflict with master and fix issues * fix: fix expression editor bug causing code mirror to no longer be reactive * fix: fix resource locator bug * fix: fix vue-agile integration * fix: remove global import for vue-agile * fix: replace element-plus buttons with n8n-buttons everywhere * fix(editor): Fix various element-plus styles (#6571) * fix(editor): Fix various element-plus styles Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Remove debugging code Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Address PR comments Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * fix(editor): Fix loading in production mode [Vue 3] (#6578) Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * fix(editor): First round of e2e tests fixes with Vue 3 (#6579) * fix(editor): Fix broken smoke and workflow list e2e tests * ✔️ Fix failing canvas action tests. Updating some selectors used in credentials and workflow tests * feat: add vue 3 eslint rules and fix issues * fix: fix tags-dropdown * fix: fix white-space issues caused by i18n-t * fix: rename non-generic click events * fix: fix search in resources list layout * fix: fix datatable paginator * fix: fix popper select caret and dropdown size * fix: add width to action-dropdown * fix: fix workflow settings icon not being hidden * fix: refactor newly added code * fix: fix merge issue * fix: fix ndv credentials watcher * fix: fix workflow saving and grabber notch * fix: fix nodes list panel transition * fix: fix node title visibility * fix: fix data unpinning * fix: fix value access * fix: show input panel only if trigger panel enabled or not trigger node * fix: fix tags dropdown and executions status spcing * fix(editor): Prevent execution list to load back when leaving the route (#6697) fix(editor): prevent execution list to load back when leaving the route * fix: fix drawer visibility * fix: fix expression toggle padding * fix: fix expressions editor styling * chore: prepare for testing * fix: fix styling for el-button without patching * test: fix unit tests in design-system * test: fix most unit tests * fix: remove import cycle. * fix: fix personalization modal tests * fix further resource mapper test adjustments * fix: fix multiple tests and n8n-route attr duplication * fix: fix source control tets * fix: fixed remaining unit tests * fix: fix workflows and credentials e2e tests * fix: fix localizeNodeNames * fix: update ndv e2e tests * fix: fix popper left placement arrow * fix: fix 5-ndv e2e tests * fix: fix 6-code-node e2e tests * fix(editor): Drop click outside directive from NodeCreator (#6716) * fix(editor): Drop click outside directive from NodeCreator * fix(editor): make sure mouseup outside is unbound at least before the component is unmounted * fix: fix 10-settings-log-streaming e2e tests * fix: fix node redrawing * fix: fix tooltip buttons styling * fix: fix varous e2e suites * fix: fix 15-scheduler-node e2e suite * fix: fix route watcher * fix: fixed param name update and credential edit * feat: update event names * refactor: Remove deprecated `$data` (#6576) Co-authored-by: Alex Grozav <alex@grozav.com> * fix: fix 17-sharing e2e suite * fix: fix tags dropdown * fix: fix tags manager * fix(editor): move :deep selectors to a separate scoped style block * fix: fix sticky component and inline text edit * fix: update e2e tests * fix: remove button override references * fix(editor): Adjust spacing in templates for Vue 3 (#6744) * fix(editor): Adjust spacing in templates * fix: Undo unneeded change * fix: Undo unneeded change * fix(editor): Adjust NDV height for Vue 3 (#6742) fix(editor): Adjust NDV height * fix(editor): Restore collapsed sidebar items for Vue 3 (#6743) fix(editor): Restore collapsed sidebar items * fix: fix linting issues * fix: fix design-system deps * fix: post-merge fixes * fix: update tests * fix: increase timeout for executionslist tets * chore: fix linting issue * fix: fix 14-mapping e2e tests in ci * fix: re-enable tests * fix: fix workflow duplication e2e tests after tags update * fix(editor): Change component prop to be typed * fix: fix tags dropdown in duplicate wf modal * fix: fix focus behaviour in tags selector * fix: fix tag creation * fix: fix log streaming e2e race condition * fix(editor): Fix Vue 3 linting issues (#6748) * fix(editor): Fix Vue 3 linting issues Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * fix MainSidebar linter issues * revert pnpm lock * update pnpm lock file --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Alex Grozav <alex@grozav.com> * fix(editor): Some css fixes for vue3 branch (#6749) * ✨ Fixing filter button height * ✨ Update input modal button position * ✨ Updating tags styling * ✨ Fix event logging settings spacing * 👕 Fixing lint errors * fix: fix linting issues * Revert to `// eslint-disable-next-line @typescript-eslint/no-misused-promises` disabling of mixins init Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * fix: fix css issue * fix(editor): Lint fix * fix(editor): Fix settings initialisation (#6750) Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * fix: fix initial settings loading * fix: replace realClick with click force * fix: fix randomly failing mapping e2e tests * fix(editor): Fix menu item event handling * fix: fix resource filters dropdown events (#6752) * fix: fix resource filters dropdown events * fix: remove teleported:false * fix: fix event selection event naming (#6753) * fix: removed console.log (#6754) * fix: rever await nextTick changes * fix: redo linting changes * fix(editor): Redraw node connections if adding more than one node to canvas (#6755) * fix(editor): Redraw node connections if adding more than one node to canvas Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Update position before connection two nodes * Lint fix --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Alex Grozav <alex@grozav.com> * fix(editor): Fix `ResourceMapper` unit tests (#6758) * ✔️ Fix matching columns test * ✔️ Fix multiple matching columns test * ✔️ Removing `skip` from the last test * fix: Allow pasting a big workflow (#6760) * fix: pasting a big workflow * chore: update comment * refactor: move try/catch to function * refactor: move try/catch to function * fix(editor): Fix modal layer width * fix: fix position changes * fix: undo it.only * fix: make undo/redo multiple steps more verbose * fix: Fix value survey styles (#6764) * fix: fix value survey styles * fix: lint * Revert "fix: lint" 72869c431f1448861df021be041b61c62f1e3118 * fix: lint * fix(editor): Fix collapsed sub menu * fix: Fix drawer animation (#6767) fix: drawer animation * fix(editor): Fix source control buttons (#6769) * fix(editor): Fix App loading & auth (#6768) * fix(editor): Fix App loading & auth Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Await promises Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Fix eslint error Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Csaba Tuncsik <csaba@n8n.io> Co-authored-by: OlegIvaniv <me@olegivaniv.com> Co-authored-by: Milorad FIlipović <milorad@n8n.io> Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
2023-07-28 00:51:07 -07:00
cy.get('.jtk-connector.success').should('have.length', 3);
cy.get('.jtk-connector').should('have.length', 4);
});
it('should delete node using context menu', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.deleteNodeFromContextMenu(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().should('have.length', 1);
WorkflowPage.getters.nodeConnections().should('have.length', 0);
});
it('should delete node using keyboard shortcut', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodeByName(CODE_NODE_NAME).click();
cy.get('body').type('{backspace}');
WorkflowPage.getters.canvasNodes().should('have.length', 1);
WorkflowPage.getters.nodeConnections().should('have.length', 0);
});
it('should delete node between two connected nodes', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
WorkflowPage.getters.canvasNodes().should('have.length', 3);
WorkflowPage.getters.nodeConnections().should('have.length', 2);
WorkflowPage.getters.canvasNodeByName(CODE_NODE_NAME).click();
WorkflowPage.actions.zoomToFit();
cy.get('body').type('{backspace}');
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
});
it('should delete multiple nodes (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
WorkflowPage.actions.hitDeleteAllNodes();
WorkflowPage.getters.canvasNodes().should('have.length', 0);
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
WorkflowPage.actions.selectAllFromContextMenu();
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('delete');
WorkflowPage.getters.canvasNodes().should('have.length', 0);
});
it('should delete multiple nodes (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
WorkflowPage.actions.hitDeleteAllNodes();
WorkflowPage.getters.canvasNodes().should('have.length', 0);
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
WorkflowPage.actions.selectAllFromContextMenu();
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('delete');
WorkflowPage.getters.canvasNodes().should('have.length', 0);
});
it('should move node', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.zoomToFit();
WorkflowPage.getters
.canvasNodes()
.last()
.then(($node) => {
const { left, top } = $node.position();
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
clickToFinish: true,
});
WorkflowPage.getters
.canvasNodes()
.last()
.then(($node) => {
const { left: newLeft, top: newTop } = $node.position();
expect(newLeft).to.be.greaterThan(left);
expect(newTop).to.be.greaterThan(top);
});
});
});
it('should zoom in', () => {
WorkflowPage.getters.zoomInButton().should('be.visible').click();
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${ZOOM_IN_X1_FACTOR}, 0, 0, ${ZOOM_IN_X1_FACTOR}, 0, 0)`,
);
WorkflowPage.getters.zoomInButton().click();
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${ZOOM_IN_X2_FACTOR}, 0, 0, ${ZOOM_IN_X2_FACTOR}, 0, 0)`,
);
});
it('should zoom out', () => {
WorkflowPage.getters.zoomOutButton().should('be.visible').click();
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${ZOOM_OUT_X1_FACTOR}, 0, 0, ${ZOOM_OUT_X1_FACTOR}, 0, 0)`,
);
WorkflowPage.getters.zoomOutButton().click();
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${ZOOM_OUT_X2_FACTOR}, 0, 0, ${ZOOM_OUT_X2_FACTOR}, 0, 0)`,
);
});
it('should zoom using scroll or pinch gesture', () => {
WorkflowPage.actions.pinchToZoom(1, 'zoomIn');
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${PINCH_ZOOM_IN_FACTOR}, 0, 0, ${PINCH_ZOOM_IN_FACTOR}, 0, 0)`,
);
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
// Zoom in 1x + Zoom out 1x should reset to default (=1)
WorkflowPage.getters.nodeView().should('have.css', 'transform', 'matrix(1, 0, 0, 1, 0, 0)');
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${PINCH_ZOOM_OUT_FACTOR}, 0, 0, ${PINCH_ZOOM_OUT_FACTOR}, 0, 0)`,
);
});
it('should reset zoom', () => {
// Reset zoom should not appear until zoom level changed
WorkflowPage.getters.resetZoomButton().should('not.exist');
WorkflowPage.getters.zoomInButton().click();
WorkflowPage.getters.resetZoomButton().should('be.visible').click();
WorkflowPage.getters
.nodeView()
.should(
'have.css',
'transform',
`matrix(${DEFAULT_ZOOM_FACTOR}, 0, 0, ${DEFAULT_ZOOM_FACTOR}, 0, 0)`,
);
});
it('should zoom to fit', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
// At this point last added node should be off-screen
WorkflowPage.getters.canvasNodes().last().should('not.be.visible');
WorkflowPage.getters.zoomToFitButton().click();
WorkflowPage.getters.canvasNodes().last().should('be.visible');
});
it('should disable node (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().last().click();
WorkflowPage.actions.hitDisableNodeShortcut();
WorkflowPage.getters.disabledNodes().should('have.length', 1);
WorkflowPage.actions.disableNode(CODE_NODE_NAME);
WorkflowPage.getters.disabledNodes().should('have.length', 0);
});
it('should disable multiple nodes (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.get('body').type('{esc}');
cy.get('body').type('{esc}');
// Keyboard shortcut
WorkflowPage.actions.hitSelectAll();
WorkflowPage.actions.hitDisableNodeShortcut();
WorkflowPage.getters.disabledNodes().should('have.length', 2);
WorkflowPage.actions.hitDisableNodeShortcut();
WorkflowPage.getters.disabledNodes().should('have.length', 0);
WorkflowPage.actions.deselectAll();
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.hitDisableNodeShortcut();
WorkflowPage.getters.disabledNodes().should('have.length', 1);
WorkflowPage.actions.hitSelectAll();
WorkflowPage.actions.hitDisableNodeShortcut();
WorkflowPage.getters.disabledNodes().should('have.length', 2);
// Context menu
WorkflowPage.actions.hitSelectAll();
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('toggle_activation');
WorkflowPage.getters.disabledNodes().should('have.length', 0);
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('toggle_activation');
WorkflowPage.getters.disabledNodes().should('have.length', 2);
WorkflowPage.actions.deselectAll();
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('toggle_activation');
WorkflowPage.getters.disabledNodes().should('have.length', 1);
WorkflowPage.actions.hitSelectAll();
WorkflowPage.actions.openContextMenu();
WorkflowPage.actions.contextMenuAction('toggle_activation');
WorkflowPage.getters.disabledNodes().should('have.length', 2);
});
it('should rename node (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().last().click();
cy.get('body').trigger('keydown', { key: 'F2' });
cy.get('.rename-prompt').should('be.visible');
cy.get('body').type(RENAME_NODE_NAME);
cy.get('body').type('{enter}');
WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME).should('exist');
WorkflowPage.actions.renameNode(RENAME_NODE_NAME);
cy.get('.rename-prompt').should('be.visible');
cy.get('body').type(RENAME_NODE_NAME2);
cy.get('body').type('{enter}');
WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME2).should('exist');
});
it('should not allow empty strings for node names', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().last().click();
cy.get('body').trigger('keydown', { key: 'F2' });
cy.get('.rename-prompt').should('be.visible');
cy.get('body').type('{backspace}');
cy.get('body').type('{enter}');
cy.get('.rename-prompt').should('contain', 'Invalid Name');
});
it('should duplicate nodes (context menu or shortcut)', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.duplicateNode(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().should('have.length', 3);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
WorkflowPage.actions.hitSelectAll();
WorkflowPage.actions.hitDuplicateNode();
WorkflowPage.getters.canvasNodes().should('have.length', 5);
});
// ADO-1240: Connections would get deleted after activating and deactivating NodeView
it('should preserve connections after rename & node-view switch', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.executeWorkflow();
ExecutionsTab.actions.switchToExecutionsTab();
ExecutionsTab.getters.successfulExecutionListItems().should('have.length', 1);
ExecutionsTab.actions.switchToEditorTab();
ExecutionsTab.actions.switchToExecutionsTab();
ExecutionsTab.getters.successfulExecutionListItems().should('have.length', 1);
ExecutionsTab.actions.switchToEditorTab();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.canvasNodes().last().click();
cy.get('body').trigger('keydown', { key: 'F2' });
cy.get('.rename-prompt').should('be.visible');
cy.get('body').type(RENAME_NODE_NAME);
cy.get('body').type('{enter}');
WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME).should('exist');
// Make sure all connections are there after save & reload
WorkflowPage.actions.saveWorkflowOnButtonClick();
cy.reload();
cy.waitForLoad();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 1);
});
it('should remove unknown credentials on pasting workflow', () => {
cy.fixture('workflow-with-unknown-credentials.json').then((data) => {
cy.get('body').paste(JSON.stringify(data));
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);
WorkflowPage.actions.openNodeFromContextMenu('n8n');
cy.get('[class*=hasIssues]').should('have.length', 1);
NDVDialog.actions.close();
});
});
it('should render connections correctly if unkown nodes are present', () => {
const unknownNodeName = 'Unknown node';
cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes');
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 1`).should('exist');
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 2`).should('exist');
WorkflowPage.actions.zoomToFit();
cy.draganddrop(
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 1`),
WorkflowPage.getters.getEndpointSelector('input', EDIT_FIELDS_SET_NODE_NAME),
);
cy.draganddrop(
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 2`),
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
);
WorkflowPage.actions.executeWorkflow();
cy.contains('Unrecognized node type').should('be.visible');
WorkflowPage.actions.deselectAll();
WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 1`);
WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 2`);
WorkflowPage.actions.executeWorkflow();
cy.contains('Unrecognized node type').should('not.exist');
});
});