mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
test: Update e2e tests for canvas specific actions (no-changelog) (#12614)
This commit is contained in:
parent
ac2f6476c1
commit
2d3b643f6e
|
@ -1,6 +1,7 @@
|
||||||
import { getManualChatModal } from './modals/chat-modal';
|
import { getManualChatModal } from './modals/chat-modal';
|
||||||
import { clickGetBackToCanvas, getParameterInputByName } from './ndv';
|
import { clickGetBackToCanvas, getParameterInputByName } from './ndv';
|
||||||
import { ROUTES } from '../constants';
|
import { ROUTES } from '../constants';
|
||||||
|
import type { OpenContextMenuOptions } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
|
@ -24,7 +25,36 @@ export type EndpointType =
|
||||||
* Getters
|
* Getters
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getAddInputEndpointByType(nodeName: string, endpointType: EndpointType) {
|
export function getCanvas() {
|
||||||
|
return cy.getByTestId('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCanvasPane() {
|
||||||
|
return cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('node-view-background'),
|
||||||
|
() => getCanvas().find('.vue-flow__pane'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getContextMenu() {
|
||||||
|
return cy.getByTestId('context-menu').find('.el-dropdown-menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getContextMenuAction(action: string) {
|
||||||
|
return cy.getByTestId(`context-menu-item-${action}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputPlusHandle(nodeName: string) {
|
||||||
|
return cy.ifCanvasVersion(
|
||||||
|
() => cy.get(`.add-input-endpoint[data-endpoint-name="${nodeName}"]`),
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`[data-test-id="canvas-node-input-handle"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"]`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputPlusHandleByType(nodeName: string, endpointType: EndpointType) {
|
||||||
return cy.ifCanvasVersion(
|
return cy.ifCanvasVersion(
|
||||||
() =>
|
() =>
|
||||||
cy.get(
|
cy.get(
|
||||||
|
@ -37,6 +67,29 @@ export function getAddInputEndpointByType(nodeName: string, endpointType: Endpoi
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOutputPlusHandle(nodeName: string) {
|
||||||
|
return cy.ifCanvasVersion(
|
||||||
|
() => cy.get(`.add-output-endpoint[data-endpoint-name="${nodeName}"]`),
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`[data-test-id="canvas-node-output-handle"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"]`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOutputPlusHandleByType(nodeName: string, endpointType: EndpointType) {
|
||||||
|
return cy.ifCanvasVersion(
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`.add-output-endpoint[data-jtk-scope-${endpointType}][data-endpoint-name="${nodeName}"]`,
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`[data-test-id="canvas-node-output-handle"][data-connection-type="${endpointType}"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"]`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getNodeCreatorItems() {
|
export function getNodeCreatorItems() {
|
||||||
return cy.getByTestId('item-iterator-item');
|
return cy.getByTestId('item-iterator-item');
|
||||||
}
|
}
|
||||||
|
@ -60,6 +113,13 @@ export function getNodeByName(name: string) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNodeRenderedTypeByName(name: string) {
|
||||||
|
return cy.ifCanvasVersion(
|
||||||
|
() => getNodeByName(name),
|
||||||
|
() => getNodeByName(name).find('[data-canvas-node-render-type]'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getWorkflowHistoryCloseButton() {
|
export function getWorkflowHistoryCloseButton() {
|
||||||
return cy.getByTestId('workflow-history-close-button');
|
return cy.getByTestId('workflow-history-close-button');
|
||||||
}
|
}
|
||||||
|
@ -85,6 +145,12 @@ export function getConnectionBySourceAndTarget(source: string, target: string) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConnectionLabelBySourceAndTarget(source: string, target: string) {
|
||||||
|
return cy
|
||||||
|
.getByTestId('edge-label')
|
||||||
|
.filter(`[data-source-node-name="${source}"][data-target-node-name="${target}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
export function getNodeCreatorSearchBar() {
|
export function getNodeCreatorSearchBar() {
|
||||||
return cy.getByTestId('node-creator-search-bar');
|
return cy.getByTestId('node-creator-search-bar');
|
||||||
}
|
}
|
||||||
|
@ -94,10 +160,7 @@ export function getNodeCreatorPlusButton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCanvasNodes() {
|
export function getCanvasNodes() {
|
||||||
return cy.ifCanvasVersion(
|
return cy.getByTestId('canvas-node');
|
||||||
() => cy.getByTestId('canvas-node'),
|
|
||||||
() => cy.getByTestId('canvas-node').not('[data-node-type="n8n-nodes-internal.addNodes"]'),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCanvasNodeByName(nodeName: string) {
|
export function getCanvasNodeByName(nodeName: string) {
|
||||||
|
@ -157,7 +220,7 @@ function connectNodeToParent(
|
||||||
parentNodeName: string,
|
parentNodeName: string,
|
||||||
exactMatch = false,
|
exactMatch = false,
|
||||||
) {
|
) {
|
||||||
getAddInputEndpointByType(parentNodeName, endpointType).click({ force: true });
|
getInputPlusHandleByType(parentNodeName, endpointType).click({ force: true });
|
||||||
if (exactMatch) {
|
if (exactMatch) {
|
||||||
getNodeCreatorItems()
|
getNodeCreatorItems()
|
||||||
.contains(new RegExp('^' + nodeName + '$', 'g'))
|
.contains(new RegExp('^' + nodeName + '$', 'g'))
|
||||||
|
@ -257,3 +320,34 @@ export function deleteNode(name: string) {
|
||||||
getCanvasNodeByName(name).first().click();
|
getCanvasNodeByName(name).first().click();
|
||||||
cy.get('body').type('{del}');
|
cy.get('body').type('{del}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openContextMenu(
|
||||||
|
nodeName?: string,
|
||||||
|
{ method = 'right-click', anchor = 'center' }: OpenContextMenuOptions = {},
|
||||||
|
) {
|
||||||
|
let target;
|
||||||
|
if (nodeName) {
|
||||||
|
target =
|
||||||
|
method === 'right-click' ? getNodeRenderedTypeByName(nodeName) : getNodeByName(nodeName);
|
||||||
|
} else {
|
||||||
|
target = getCanvasPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'right-click') {
|
||||||
|
target.rightclick(nodeName ? anchor : 'topLeft', { force: true });
|
||||||
|
} else {
|
||||||
|
target.realHover();
|
||||||
|
target.find('[data-test-id="overflow-node-button"]').click({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
getContextMenu().should('be.visible');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clickContextMenuAction(action: string) {
|
||||||
|
getContextMenuAction(action).click();
|
||||||
|
}
|
||||||
|
|
|
@ -184,7 +184,11 @@ describe('Canvas Actions', () => {
|
||||||
.last()
|
.last()
|
||||||
.findChildByTestId('execute-node-button')
|
.findChildByTestId('execute-node-button')
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
||||||
|
successToast().should('have.length', 1);
|
||||||
|
|
||||||
WorkflowPage.actions.executeNode(CODE_NODE_NAME);
|
WorkflowPage.actions.executeNode(CODE_NODE_NAME);
|
||||||
|
|
||||||
successToast().should('have.length', 2);
|
successToast().should('have.length', 2);
|
||||||
successToast().should('contain.text', 'Node executed successfully');
|
successToast().should('contain.text', 'Node executed successfully');
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,9 +7,17 @@ import {
|
||||||
SWITCH_NODE_NAME,
|
SWITCH_NODE_NAME,
|
||||||
MERGE_NODE_NAME,
|
MERGE_NODE_NAME,
|
||||||
} from './../constants';
|
} from './../constants';
|
||||||
|
import {
|
||||||
|
clickContextMenuAction,
|
||||||
|
getCanvasNodeByName,
|
||||||
|
getCanvasNodes,
|
||||||
|
getConnectionBySourceAndTarget,
|
||||||
|
getConnectionLabelBySourceAndTarget,
|
||||||
|
getOutputPlusHandle,
|
||||||
|
openContextMenu,
|
||||||
|
} from '../composables/workflow';
|
||||||
import { NDV, WorkflowExecutionsTab } from '../pages';
|
import { NDV, WorkflowExecutionsTab } from '../pages';
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
import { isCanvasV2 } from '../utils/workflowUtils';
|
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
const ExecutionsTab = new WorkflowExecutionsTab();
|
const ExecutionsTab = new WorkflowExecutionsTab();
|
||||||
|
@ -41,27 +49,52 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
|
|
||||||
NDVDialog.actions.close();
|
NDVDialog.actions.close();
|
||||||
for (let i = 0; i < desiredOutputs; i++) {
|
for (let i = 0; i < desiredOutputs; i++) {
|
||||||
WorkflowPage.getters.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i).click({ force: true });
|
cy.ifCanvasVersion(
|
||||||
|
() => {
|
||||||
|
WorkflowPage.getters
|
||||||
|
.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i)
|
||||||
|
.click({ force: true });
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
getOutputPlusHandle(SWITCH_NODE_NAME).eq(0).click();
|
||||||
|
},
|
||||||
|
);
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||||
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
|
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
|
||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
}
|
}
|
||||||
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
||||||
WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
|
cy.ifCanvasVersion(
|
||||||
|
() => {
|
||||||
|
WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
getOutputPlusHandle(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
|
||||||
|
},
|
||||||
|
);
|
||||||
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false);
|
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false);
|
||||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.waitForLoad();
|
cy.waitForLoad();
|
||||||
// Make sure outputless switch was connected correctly
|
// Make sure outputless switch was connected correctly
|
||||||
WorkflowPage.getters
|
cy.ifCanvasVersion(
|
||||||
.getConnectionBetweenNodes(`${EDIT_FIELDS_SET_NODE_NAME}3`, `${SWITCH_NODE_NAME}1`)
|
() => {
|
||||||
.should('exist');
|
WorkflowPage.getters
|
||||||
|
.getConnectionBetweenNodes(`${EDIT_FIELDS_SET_NODE_NAME}3`, `${SWITCH_NODE_NAME}1`)
|
||||||
|
.should('exist');
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
getConnectionBySourceAndTarget(
|
||||||
|
`${EDIT_FIELDS_SET_NODE_NAME}3`,
|
||||||
|
`${SWITCH_NODE_NAME}1`,
|
||||||
|
).should('exist');
|
||||||
|
},
|
||||||
|
);
|
||||||
// Make sure all connections are there after reload
|
// Make sure all connections are there after reload
|
||||||
for (let i = 0; i < desiredOutputs; i++) {
|
for (let i = 0; i < desiredOutputs; i++) {
|
||||||
const setName = `${EDIT_FIELDS_SET_NODE_NAME}${i > 0 ? i : ''}`;
|
const setName = `${EDIT_FIELDS_SET_NODE_NAME}${i > 0 ? i : ''}`;
|
||||||
WorkflowPage.getters
|
|
||||||
.getConnectionBetweenNodes(`${SWITCH_NODE_NAME}`, setName)
|
getConnectionBySourceAndTarget(`${SWITCH_NODE_NAME}`, setName).should('exist');
|
||||||
.should('exist');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -84,14 +117,29 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connect Set1 and Set2 to merge
|
// Connect Set1 and Set2 to merge
|
||||||
cy.draganddrop(
|
cy.ifCanvasVersion(
|
||||||
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),
|
||||||
cy.draganddrop(
|
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 0),
|
||||||
WorkflowPage.getters.getEndpointSelector('plus', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
);
|
||||||
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1),
|
cy.draganddrop(
|
||||||
|
WorkflowPage.getters.getEndpointSelector('plus', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
||||||
|
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
cy.draganddrop(
|
||||||
|
WorkflowPage.getters.getEndpointSelector('output', EDIT_FIELDS_SET_NODE_NAME),
|
||||||
|
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 0),
|
||||||
|
);
|
||||||
|
cy.draganddrop(
|
||||||
|
WorkflowPage.getters.getEndpointSelector('output', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
||||||
|
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const checkConnections = () => {
|
const checkConnections = () => {
|
||||||
WorkflowPage.getters
|
WorkflowPage.getters
|
||||||
.getConnectionBetweenNodes(
|
.getConnectionBetweenNodes(
|
||||||
|
@ -117,10 +165,22 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.actions.executeWorkflow();
|
WorkflowPage.actions.executeWorkflow();
|
||||||
WorkflowPage.getters.stopExecutionButton().should('not.exist');
|
WorkflowPage.getters.stopExecutionButton().should('not.exist');
|
||||||
|
|
||||||
|
// Make sure all connections are there after save & reload
|
||||||
|
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||||
|
cy.reload();
|
||||||
|
cy.waitForLoad();
|
||||||
|
checkConnections();
|
||||||
|
|
||||||
|
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
|
// If the merged set nodes are connected and executed correctly, there should be 2 items in the output of merge node
|
||||||
cy.ifCanvasVersion(
|
cy.ifCanvasVersion(
|
||||||
() => cy.get('[data-label="2 items"]').should('be.visible'),
|
() => cy.get('[data-label="2 items"]').should('be.visible'),
|
||||||
() => cy.getByTestId('canvas-node-output-handle').contains('2 items').should('be.visible'),
|
() =>
|
||||||
|
getConnectionLabelBySourceAndTarget(`${EDIT_FIELDS_SET_NODE_NAME}1`, MERGE_NODE_NAME)
|
||||||
|
.contains('2 items')
|
||||||
|
.should('be.visible'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -144,7 +204,10 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
|
|
||||||
cy.ifCanvasVersion(
|
cy.ifCanvasVersion(
|
||||||
() => cy.get('.plus-draggable-endpoint').should('have.class', 'ep-success'),
|
() => cy.get('.plus-draggable-endpoint').should('have.class', 'ep-success'),
|
||||||
() => cy.getByTestId('canvas-handle-plus').should('have.attr', 'data-plus-type', 'success'),
|
() =>
|
||||||
|
cy
|
||||||
|
.getByTestId('canvas-handle-plus-wrapper')
|
||||||
|
.should('have.attr', 'data-plus-type', 'success'),
|
||||||
);
|
);
|
||||||
|
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
@ -212,8 +275,8 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
WorkflowPage.actions.selectAllFromContextMenu();
|
WorkflowPage.actions.selectAllFromContextMenu();
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('delete');
|
clickContextMenuAction('delete');
|
||||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -228,41 +291,43 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
WorkflowPage.actions.selectAllFromContextMenu();
|
WorkflowPage.actions.selectAllFromContextMenu();
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('delete');
|
clickContextMenuAction('delete');
|
||||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: Canvas V2: Figure out how to test moving of the node
|
|
||||||
it('should move node', () => {
|
it('should move node', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
|
||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
WorkflowPage.getters
|
|
||||||
.canvasNodes()
|
getCanvasNodes()
|
||||||
.last()
|
.last()
|
||||||
.then(($node) => {
|
.then(($node) => {
|
||||||
const { left, top } = $node.position();
|
const { x: x1, y: y1 } = $node[0].getBoundingClientRect();
|
||||||
|
|
||||||
if (isCanvasV2()) {
|
cy.ifCanvasVersion(
|
||||||
cy.drag('.vue-flow__node', [300, 300], {
|
() => {
|
||||||
realMouse: true,
|
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
||||||
});
|
clickToFinish: true,
|
||||||
} else {
|
});
|
||||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
},
|
||||||
clickToFinish: true,
|
() => {
|
||||||
});
|
cy.drag(getCanvasNodes().last(), [50, 150], {
|
||||||
}
|
realMouse: true,
|
||||||
|
abs: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
WorkflowPage.getters
|
getCanvasNodes()
|
||||||
.canvasNodes()
|
|
||||||
.last()
|
.last()
|
||||||
.then(($node) => {
|
.then(($node) => {
|
||||||
const { left: newLeft, top: newTop } = $node.position();
|
const { x: x2, y: y2 } = $node[0].getBoundingClientRect();
|
||||||
expect(newLeft).to.be.greaterThan(left);
|
expect(x2).to.be.greaterThan(x1);
|
||||||
expect(newTop).to.be.greaterThan(top);
|
expect(y2).to.be.greaterThan(y1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -369,7 +434,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||||
WorkflowPage.actions.deselectAll();
|
WorkflowPage.actions.deselectAll();
|
||||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
getCanvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||||
WorkflowPage.actions.hitSelectAll();
|
WorkflowPage.actions.hitSelectAll();
|
||||||
|
@ -378,19 +443,19 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
|
|
||||||
// Context menu
|
// Context menu
|
||||||
WorkflowPage.actions.hitSelectAll();
|
WorkflowPage.actions.hitSelectAll();
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
||||||
WorkflowPage.actions.deselectAll();
|
WorkflowPage.actions.deselectAll();
|
||||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||||
WorkflowPage.actions.hitSelectAll();
|
WorkflowPage.actions.hitSelectAll();
|
||||||
WorkflowPage.actions.openContextMenu();
|
openContextMenu();
|
||||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
||||||
});
|
});
|
||||||
|
@ -466,7 +531,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||||
});
|
});
|
||||||
// FIXME: Canvas V2: Credentials should show issue on the first open
|
|
||||||
it('should remove unknown credentials on pasting workflow', () => {
|
it('should remove unknown credentials on pasting workflow', () => {
|
||||||
cy.fixture('workflow-with-unknown-credentials.json').then((data) => {
|
cy.fixture('workflow-with-unknown-credentials.json').then((data) => {
|
||||||
cy.get('body').paste(JSON.stringify(data));
|
cy.get('body').paste(JSON.stringify(data));
|
||||||
|
|
|
@ -57,7 +57,7 @@ const editWorkflowMoreAndActivate = () => {
|
||||||
position.left = $element.position().left;
|
position.left = $element.position().left;
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 200], { clickToFinish: true });
|
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 200]);
|
||||||
workflowPage.getters
|
workflowPage.getters
|
||||||
.canvasNodes()
|
.canvasNodes()
|
||||||
.last()
|
.last()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { BasePage } from './base';
|
import { BasePage } from './base';
|
||||||
import { NodeCreator } from './features/node-creator';
|
import { NodeCreator } from './features/node-creator';
|
||||||
|
import { clickContextMenuAction, getCanvasPane, openContextMenu } from '../composables/workflow';
|
||||||
import { META_KEY } from '../constants';
|
import { META_KEY } from '../constants';
|
||||||
import type { OpenContextMenuOptions } from '../types';
|
import type { OpenContextMenuOptions } from '../types';
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
|
@ -38,15 +39,7 @@ export class WorkflowPage extends BasePage {
|
||||||
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
|
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
|
||||||
nodeCreatorPlusButton: () => cy.getByTestId('node-creator-plus-button'),
|
nodeCreatorPlusButton: () => cy.getByTestId('node-creator-plus-button'),
|
||||||
canvasPlusButton: () => cy.getByTestId('canvas-plus-button'),
|
canvasPlusButton: () => cy.getByTestId('canvas-plus-button'),
|
||||||
canvasNodes: () =>
|
canvasNodes: () => cy.getByTestId('canvas-node'),
|
||||||
cy.ifCanvasVersion(
|
|
||||||
() => cy.getByTestId('canvas-node'),
|
|
||||||
() =>
|
|
||||||
cy
|
|
||||||
.getByTestId('canvas-node')
|
|
||||||
.not('[data-node-type="n8n-nodes-internal.addNodes"]')
|
|
||||||
.not('[data-node-type="n8n-nodes-base.stickyNote"]'),
|
|
||||||
),
|
|
||||||
canvasNodeByName: (nodeName: string) =>
|
canvasNodeByName: (nodeName: string) =>
|
||||||
this.getters.canvasNodes().filter(`:contains(${nodeName})`),
|
this.getters.canvasNodes().filter(`:contains(${nodeName})`),
|
||||||
nodeIssuesByName: (nodeName: string) =>
|
nodeIssuesByName: (nodeName: string) =>
|
||||||
|
@ -110,7 +103,7 @@ export class WorkflowPage extends BasePage {
|
||||||
disabledNodes: () =>
|
disabledNodes: () =>
|
||||||
cy.ifCanvasVersion(
|
cy.ifCanvasVersion(
|
||||||
() => cy.get('.node-box.disabled'),
|
() => cy.get('.node-box.disabled'),
|
||||||
() => cy.get('[data-test-id*="node"][class*="disabled"]'),
|
() => cy.get('[data-canvas-node-render-type][class*="disabled"]'),
|
||||||
),
|
),
|
||||||
selectedNodes: () =>
|
selectedNodes: () =>
|
||||||
cy.ifCanvasVersion(
|
cy.ifCanvasVersion(
|
||||||
|
@ -288,71 +281,77 @@ export class WorkflowPage extends BasePage {
|
||||||
nodeTypeName?: string,
|
nodeTypeName?: string,
|
||||||
{ method = 'right-click', anchor = 'center' }: OpenContextMenuOptions = {},
|
{ method = 'right-click', anchor = 'center' }: OpenContextMenuOptions = {},
|
||||||
) => {
|
) => {
|
||||||
const target = nodeTypeName
|
cy.ifCanvasVersion(
|
||||||
? this.getters.canvasNodeByName(nodeTypeName)
|
() => {
|
||||||
: this.getters.nodeViewBackground();
|
const target = nodeTypeName
|
||||||
|
? this.getters.canvasNodeByName(nodeTypeName)
|
||||||
|
: this.getters.nodeViewBackground();
|
||||||
|
|
||||||
if (method === 'right-click') {
|
if (method === 'right-click') {
|
||||||
target.rightclick(nodeTypeName ? anchor : 'topLeft', { force: true });
|
target.rightclick(nodeTypeName ? anchor : 'topLeft', { force: true });
|
||||||
} else {
|
} else {
|
||||||
target.realHover();
|
target.realHover();
|
||||||
target.find('[data-test-id="overflow-node-button"]').click({ force: true });
|
target.find('[data-test-id="overflow-node-button"]').click({ force: true });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
openContextMenu(nodeTypeName, { method, anchor });
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
openNode: (nodeTypeName: string) => {
|
openNode: (nodeTypeName: string) => {
|
||||||
this.getters.canvasNodeByName(nodeTypeName).first().dblclick();
|
this.getters.canvasNodeByName(nodeTypeName).first().dblclick();
|
||||||
},
|
},
|
||||||
duplicateNode: (nodeTypeName: string) => {
|
duplicateNode: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('duplicate');
|
clickContextMenuAction('duplicate');
|
||||||
},
|
},
|
||||||
deleteNodeFromContextMenu: (nodeTypeName: string) => {
|
deleteNodeFromContextMenu: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('delete');
|
clickContextMenuAction('delete');
|
||||||
},
|
},
|
||||||
executeNode: (nodeTypeName: string, options?: OpenContextMenuOptions) => {
|
executeNode: (nodeTypeName: string, options?: OpenContextMenuOptions) => {
|
||||||
this.actions.openContextMenu(nodeTypeName, options);
|
this.actions.openContextMenu(nodeTypeName, options);
|
||||||
this.actions.contextMenuAction('execute');
|
clickContextMenuAction('execute');
|
||||||
},
|
},
|
||||||
addStickyFromContextMenu: () => {
|
addStickyFromContextMenu: () => {
|
||||||
this.actions.openContextMenu();
|
this.actions.openContextMenu();
|
||||||
this.actions.contextMenuAction('add_sticky');
|
clickContextMenuAction('add_sticky');
|
||||||
},
|
},
|
||||||
renameNode: (nodeTypeName: string) => {
|
renameNode: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('rename');
|
clickContextMenuAction('rename');
|
||||||
},
|
},
|
||||||
copyNode: (nodeTypeName: string) => {
|
copyNode: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('copy');
|
clickContextMenuAction('copy');
|
||||||
},
|
},
|
||||||
contextMenuAction: (action: string) => {
|
contextMenuAction: (action: string) => {
|
||||||
this.getters.contextMenuAction(action).click();
|
this.getters.contextMenuAction(action).click();
|
||||||
},
|
},
|
||||||
disableNode: (nodeTypeName: string) => {
|
disableNode: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('toggle_activation');
|
clickContextMenuAction('toggle_activation');
|
||||||
},
|
},
|
||||||
pinNode: (nodeTypeName: string) => {
|
pinNode: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName);
|
this.actions.openContextMenu(nodeTypeName);
|
||||||
this.actions.contextMenuAction('toggle_pin');
|
clickContextMenuAction('toggle_pin');
|
||||||
},
|
},
|
||||||
openNodeFromContextMenu: (nodeTypeName: string) => {
|
openNodeFromContextMenu: (nodeTypeName: string) => {
|
||||||
this.actions.openContextMenu(nodeTypeName, { method: 'overflow-button' });
|
this.actions.openContextMenu(nodeTypeName, { method: 'overflow-button' });
|
||||||
this.actions.contextMenuAction('open');
|
clickContextMenuAction('open');
|
||||||
},
|
},
|
||||||
selectAllFromContextMenu: () => {
|
selectAllFromContextMenu: () => {
|
||||||
this.actions.openContextMenu();
|
this.actions.openContextMenu();
|
||||||
this.actions.contextMenuAction('select_all');
|
clickContextMenuAction('select_all');
|
||||||
},
|
},
|
||||||
deselectAll: () => {
|
deselectAll: () => {
|
||||||
cy.ifCanvasVersion(
|
cy.ifCanvasVersion(
|
||||||
() => {
|
() => {
|
||||||
this.actions.openContextMenu();
|
this.actions.openContextMenu();
|
||||||
this.actions.contextMenuAction('deselect_all');
|
clickContextMenuAction('deselect_all');
|
||||||
},
|
},
|
||||||
// rightclick doesn't work with vueFlow canvas
|
() => getCanvasPane().click('topLeft'),
|
||||||
() => this.getters.nodeViewBackground().click('topLeft'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
openExpressionEditorModal: () => {
|
openExpressionEditorModal: () => {
|
||||||
|
@ -431,7 +430,7 @@ export class WorkflowPage extends BasePage {
|
||||||
pinchToZoom: (steps: number, mode: 'zoomIn' | 'zoomOut' = 'zoomIn') => {
|
pinchToZoom: (steps: number, mode: 'zoomIn' | 'zoomOut' = 'zoomIn') => {
|
||||||
cy.window().then((win) => {
|
cy.window().then((win) => {
|
||||||
// Pinch-to-zoom simulates a 'wheel' event with ctrlKey: true (same as zooming by scrolling)
|
// Pinch-to-zoom simulates a 'wheel' event with ctrlKey: true (same as zooming by scrolling)
|
||||||
this.getters.nodeView().trigger('wheel', {
|
this.getters.canvasViewport().trigger('wheel', {
|
||||||
force: true,
|
force: true,
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
|
|
|
@ -172,6 +172,7 @@ Cypress.Commands.add('drag', (selector, pos, options) => {
|
||||||
};
|
};
|
||||||
if (options?.realMouse) {
|
if (options?.realMouse) {
|
||||||
element.realMouseDown();
|
element.realMouseDown();
|
||||||
|
element.realMouseMove(0, 0);
|
||||||
element.realMouseMove(newPosition.x, newPosition.y);
|
element.realMouseMove(newPosition.x, newPosition.y);
|
||||||
element.realMouseUp();
|
element.realMouseUp();
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,8 +219,15 @@ Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector, optio
|
||||||
const pageY = coords.top + coords.height / 2;
|
const pageY = coords.top + coords.height / 2;
|
||||||
|
|
||||||
if (draggableSelector) {
|
if (draggableSelector) {
|
||||||
// We can't use realMouseDown here because it hangs headless run
|
cy.ifCanvasVersion(
|
||||||
cy.get(draggableSelector).trigger('mousedown');
|
() => {
|
||||||
|
// We can't use realMouseDown here because it hangs headless run
|
||||||
|
cy.get(draggableSelector).trigger('mousedown');
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
cy.get(draggableSelector).realMouseDown();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// We don't chain these commands to make sure cy.get is re-trying correctly
|
// We don't chain these commands to make sure cy.get is re-trying correctly
|
||||||
cy.get(droppableSelector).realMouseMove(0, 0);
|
cy.get(droppableSelector).realMouseMove(0, 0);
|
||||||
|
|
|
@ -29,7 +29,9 @@ function onActionSelect(item: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeMenu(event: MouseEvent) {
|
function closeMenu(event: MouseEvent) {
|
||||||
event.preventDefault();
|
if (event.cancelable) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
contextMenu.close();
|
contextMenu.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import type {
|
||||||
CanvasNodeEventBusEvents,
|
CanvasNodeEventBusEvents,
|
||||||
CanvasEventBusEvents,
|
CanvasEventBusEvents,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
import { CanvasConnectionMode } from '@/types';
|
import { CanvasNodeRenderType, CanvasConnectionMode } from '@/types';
|
||||||
import NodeIcon from '@/components/NodeIcon.vue';
|
import NodeIcon from '@/components/NodeIcon.vue';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import CanvasNodeToolbar from '@/components/canvas/elements/nodes/CanvasNodeToolbar.vue';
|
import CanvasNodeToolbar from '@/components/canvas/elements/nodes/CanvasNodeToolbar.vue';
|
||||||
|
@ -107,6 +107,14 @@ const classes = computed(() => ({
|
||||||
...Object.fromEntries([...nodeClasses.value].map((c) => [c, true])),
|
...Object.fromEntries([...nodeClasses.value].map((c) => [c, true])),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const renderType = computed<CanvasNodeRenderType>(() => props.data.render.type);
|
||||||
|
|
||||||
|
const dataTestId = computed(() =>
|
||||||
|
[CanvasNodeRenderType.StickyNote, CanvasNodeRenderType.AddNodes].includes(renderType.value)
|
||||||
|
? undefined
|
||||||
|
: 'canvas-node',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event bus
|
* Event bus
|
||||||
*/
|
*/
|
||||||
|
@ -330,7 +338,7 @@ onBeforeUnmount(() => {
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="classes"
|
:class="classes"
|
||||||
data-test-id="canvas-node"
|
:data-test-id="dataTestId"
|
||||||
:data-node-name="data.name"
|
:data-node-name="data.name"
|
||||||
:data-node-type="data.type"
|
:data-node-type="data.type"
|
||||||
>
|
>
|
||||||
|
|
|
@ -13,8 +13,10 @@ const slots = defineSlots<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const Render = () => {
|
const Render = () => {
|
||||||
|
const renderType = node?.data.value.render.type ?? CanvasNodeRenderType.Default;
|
||||||
let Component;
|
let Component;
|
||||||
switch (node?.data.value.render.type) {
|
|
||||||
|
switch (renderType) {
|
||||||
case CanvasNodeRenderType.StickyNote:
|
case CanvasNodeRenderType.StickyNote:
|
||||||
Component = CanvasNodeStickyNote;
|
Component = CanvasNodeStickyNote;
|
||||||
break;
|
break;
|
||||||
|
@ -25,7 +27,13 @@ const Render = () => {
|
||||||
Component = CanvasNodeDefault;
|
Component = CanvasNodeDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
return h(Component, slots.default);
|
return h(
|
||||||
|
Component,
|
||||||
|
{
|
||||||
|
'data-canvas-node-render-type': renderType,
|
||||||
|
},
|
||||||
|
slots.default,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue