mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
test: Enable Canvas V2 E2E Testing (#11321)
Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
parent
c0b5b92f62
commit
de04c93f2c
6
.github/workflows/e2e-reusable.yml
vendored
6
.github/workflows/e2e-reusable.yml
vendored
|
@ -41,6 +41,11 @@ on:
|
||||||
description: 'PR number to run tests for.'
|
description: 'PR number to run tests for.'
|
||||||
required: false
|
required: false
|
||||||
type: number
|
type: number
|
||||||
|
node_view_version:
|
||||||
|
description: 'Node View version to run tests with.'
|
||||||
|
required: false
|
||||||
|
default: '1'
|
||||||
|
type: string
|
||||||
secrets:
|
secrets:
|
||||||
CYPRESS_RECORD_KEY:
|
CYPRESS_RECORD_KEY:
|
||||||
description: 'Cypress record key.'
|
description: 'Cypress record key.'
|
||||||
|
@ -160,6 +165,7 @@ jobs:
|
||||||
spec: '${{ inputs.spec }}'
|
spec: '${{ inputs.spec }}'
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --dns-result-order=ipv4first
|
NODE_OPTIONS: --dns-result-order=ipv4first
|
||||||
|
CYPRESS_NODE_VIEW_VERSION: ${{ inputs.node_view_version }}
|
||||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
E2E_TESTS: true
|
E2E_TESTS: true
|
||||||
|
|
6
.github/workflows/e2e-tests.yml
vendored
6
.github/workflows/e2e-tests.yml
vendored
|
@ -27,6 +27,11 @@ on:
|
||||||
description: 'URL to call after workflow is done.'
|
description: 'URL to call after workflow is done.'
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
|
node_view_version:
|
||||||
|
description: 'Node View version to run tests with.'
|
||||||
|
required: false
|
||||||
|
default: '1'
|
||||||
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
calls-start-url:
|
calls-start-url:
|
||||||
|
@ -46,6 +51,7 @@ jobs:
|
||||||
branch: ${{ github.event.inputs.branch || 'master' }}
|
branch: ${{ github.event.inputs.branch || 'master' }}
|
||||||
user: ${{ github.event.inputs.user || 'PR User' }}
|
user: ${{ github.event.inputs.user || 'PR User' }}
|
||||||
spec: ${{ github.event.inputs.spec || 'e2e/*' }}
|
spec: ${{ github.event.inputs.spec || 'e2e/*' }}
|
||||||
|
node_view_version: ${{ github.event.inputs.node_view_version || '1' }}
|
||||||
secrets:
|
secrets:
|
||||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ describe('Undo/Redo', () => {
|
||||||
WorkflowPage.actions.visit();
|
WorkflowPage.actions.visit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix redo connections
|
||||||
it('should undo/redo adding node in the middle', () => {
|
it('should undo/redo adding node in the middle', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
@ -114,6 +115,7 @@ describe('Undo/Redo', () => {
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix moving of nodes via e2e tests
|
||||||
it('should undo/redo moving nodes', () => {
|
it('should undo/redo moving nodes', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
@ -146,18 +148,14 @@ describe('Undo/Redo', () => {
|
||||||
it('should undo/redo deleting a connection using context menu', () => {
|
it('should undo/redo deleting a connection using context menu', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
WorkflowPage.getters.nodeConnections().realHover();
|
WorkflowPage.actions.deleteNodeBetweenNodes(SCHEDULE_TRIGGER_NODE_NAME, CODE_NODE_NAME);
|
||||||
cy.get('.connection-actions .delete')
|
|
||||||
.filter(':visible')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({ force: true });
|
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
WorkflowPage.actions.hitUndo();
|
WorkflowPage.actions.hitUndo();
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||||
WorkflowPage.actions.hitRedo();
|
WorkflowPage.actions.hitRedo();
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
// FIXME: Canvas V2: Fix disconnecting by moving
|
||||||
it('should undo/redo deleting a connection by moving it away', () => {
|
it('should undo/redo deleting a connection by moving it away', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
@ -206,6 +204,7 @@ describe('Undo/Redo', () => {
|
||||||
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix undo renaming node
|
||||||
it('should undo/redo renaming node using keyboard shortcut', () => {
|
it('should undo/redo renaming node using keyboard shortcut', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
@ -244,6 +243,7 @@ describe('Undo/Redo', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Figure out why moving doesn't work from e2e
|
||||||
it('should undo/redo multiple steps', () => {
|
it('should undo/redo multiple steps', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
|
|
|
@ -16,6 +16,7 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.actions.visit();
|
WorkflowPage.actions.visit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Missing execute button if no nodes
|
||||||
it('should render canvas', () => {
|
it('should render canvas', () => {
|
||||||
WorkflowPage.getters.nodeViewRoot().should('be.visible');
|
WorkflowPage.getters.nodeViewRoot().should('be.visible');
|
||||||
WorkflowPage.getters.canvasPlusButton().should('be.visible');
|
WorkflowPage.getters.canvasPlusButton().should('be.visible');
|
||||||
|
@ -25,10 +26,11 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.getters.executeWorkflowButton().should('be.visible');
|
WorkflowPage.getters.executeWorkflowButton().should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix changing of connection
|
||||||
it('should connect and disconnect a simple node', () => {
|
it('should connect and disconnect a simple node', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
|
||||||
WorkflowPage.getters.nodeViewBackground().click(600, 200, { force: true });
|
WorkflowPage.getters.nodeViewBackground().click(600, 200, { force: true });
|
||||||
cy.get('.jtk-connector').should('have.length', 1);
|
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||||
|
|
||||||
WorkflowPage.getters.nodeViewBackground().click(600, 400, { force: true });
|
WorkflowPage.getters.nodeViewBackground().click(600, 400, { force: true });
|
||||||
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
|
||||||
|
@ -40,16 +42,16 @@ describe('Canvas Actions', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
WorkflowPage.getters
|
WorkflowPage.getters
|
||||||
.canvasNodeInputEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}1`)
|
.getConnectionBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, `${EDIT_FIELDS_SET_NODE_NAME}1`)
|
||||||
.should('have.class', 'jtk-endpoint-connected');
|
.should('be.visible');
|
||||||
|
|
||||||
cy.get('.jtk-connector').should('have.length', 1);
|
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||||
// Disconnect Set1
|
// Disconnect Set1
|
||||||
cy.drag(
|
cy.drag(
|
||||||
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
||||||
[-200, 100],
|
[-200, 100],
|
||||||
);
|
);
|
||||||
cy.get('.jtk-connector').should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add first step', () => {
|
it('should add first step', () => {
|
||||||
|
@ -74,7 +76,7 @@ describe('Canvas Actions', () => {
|
||||||
|
|
||||||
it('should add a connected node using plus endpoint', () => {
|
it('should add a connected node using plus endpoint', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
cy.get('.plus-endpoint').should('be.visible').click();
|
WorkflowPage.getters.canvasNodePlusEndpointByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().type('{enter}');
|
WorkflowPage.getters.nodeCreatorSearchBar().type('{enter}');
|
||||||
|
@ -85,7 +87,7 @@ describe('Canvas Actions', () => {
|
||||||
|
|
||||||
it('should add a connected node dragging from node creator', () => {
|
it('should add a connected node dragging from node creator', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
cy.get('.plus-endpoint').should('be.visible').click();
|
WorkflowPage.getters.canvasNodePlusEndpointByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
||||||
cy.drag(WorkflowPage.getters.nodeCreatorNodeItems().first(), [100, 100], {
|
cy.drag(WorkflowPage.getters.nodeCreatorNodeItems().first(), [100, 100], {
|
||||||
|
@ -99,7 +101,7 @@ describe('Canvas Actions', () => {
|
||||||
|
|
||||||
it('should open a category when trying to drag and drop it on the canvas', () => {
|
it('should open a category when trying to drag and drop it on the canvas', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
cy.get('.plus-endpoint').should('be.visible').click();
|
WorkflowPage.getters.canvasNodePlusEndpointByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||||
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME);
|
||||||
cy.drag(WorkflowPage.getters.nodeCreatorActionItems().first(), [100, 100], {
|
cy.drag(WorkflowPage.getters.nodeCreatorActionItems().first(), [100, 100], {
|
||||||
|
@ -114,7 +116,7 @@ describe('Canvas Actions', () => {
|
||||||
it('should add disconnected node if nothing is selected', () => {
|
it('should add disconnected node if nothing is selected', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
// Deselect nodes
|
// Deselect nodes
|
||||||
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
WorkflowPage.getters.nodeView().click({ force: true });
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
|
@ -136,10 +138,10 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 3);
|
WorkflowPage.getters.nodeConnections().should('have.length', 3);
|
||||||
|
|
||||||
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).then(($editFieldsNode) => {
|
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).then(($editFieldsNode) => {
|
||||||
const editFieldsNodeLeft = parseFloat($editFieldsNode.css('left'));
|
const editFieldsNodeLeft = WorkflowPage.getters.getNodeLeftPosition($editFieldsNode);
|
||||||
|
|
||||||
WorkflowPage.getters.canvasNodeByName(HTTP_REQUEST_NODE_NAME).then(($httpNode) => {
|
WorkflowPage.getters.canvasNodeByName(HTTP_REQUEST_NODE_NAME).then(($httpNode) => {
|
||||||
const httpNodeLeft = parseFloat($httpNode.css('left'));
|
const httpNodeLeft = WorkflowPage.getters.getNodeLeftPosition($httpNode);
|
||||||
expect(httpNodeLeft).to.be.lessThan(editFieldsNodeLeft);
|
expect(httpNodeLeft).to.be.lessThan(editFieldsNodeLeft);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -159,10 +161,12 @@ describe('Canvas Actions', () => {
|
||||||
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.getters.nodeConnections().first().realHover();
|
WorkflowPage.getters.nodeConnections().first().realHover();
|
||||||
cy.get('.connection-actions .delete').first().click({ force: true });
|
WorkflowPage.actions.deleteNodeBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, CODE_NODE_NAME);
|
||||||
|
|
||||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix disconnecting of connection by dragging it
|
||||||
it('should delete a connection by moving it away from endpoint', () => {
|
it('should delete a connection by moving it away from endpoint', () => {
|
||||||
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();
|
||||||
|
@ -216,10 +220,10 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.actions.hitSelectAll();
|
WorkflowPage.actions.hitSelectAll();
|
||||||
|
|
||||||
WorkflowPage.actions.hitCopy();
|
WorkflowPage.actions.hitCopy();
|
||||||
successToast().should('contain', 'Copied!');
|
successToast().should('contain', 'Copied to clipboard');
|
||||||
|
|
||||||
WorkflowPage.actions.copyNode(CODE_NODE_NAME);
|
WorkflowPage.actions.copyNode(CODE_NODE_NAME);
|
||||||
successToast().should('contain', 'Copied!');
|
successToast().should('contain', 'Copied to clipboard');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select/deselect all nodes', () => {
|
it('should select/deselect all nodes', () => {
|
||||||
|
@ -231,17 +235,31 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.getters.selectedNodes().should('have.length', 0);
|
WorkflowPage.getters.selectedNodes().should('have.length', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Selection via arrow keys is broken
|
||||||
it('should select nodes using arrow keys', () => {
|
it('should select nodes using arrow keys', () => {
|
||||||
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);
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get('body').type('{leftArrow}');
|
cy.get('body').type('{leftArrow}');
|
||||||
WorkflowPage.getters.canvasNodes().first().should('have.class', 'jtk-drag-selected');
|
const selectedCanvasNodes = () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => WorkflowPage.getters.canvasNodes(),
|
||||||
|
() => WorkflowPage.getters.canvasNodes().parent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => selectedCanvasNodes().first().should('have.class', 'jtk-drag-selected'),
|
||||||
|
() => selectedCanvasNodes().first().should('have.class', 'selected'),
|
||||||
|
);
|
||||||
cy.get('body').type('{rightArrow}');
|
cy.get('body').type('{rightArrow}');
|
||||||
WorkflowPage.getters.canvasNodes().last().should('have.class', 'jtk-drag-selected');
|
cy.ifCanvasVersion(
|
||||||
|
() => selectedCanvasNodes().last().should('have.class', 'jtk-drag-selected'),
|
||||||
|
() => selectedCanvasNodes().last().should('have.class', 'selected'),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Selection via shift and arrow keys is broken
|
||||||
it('should select nodes using shift and arrow keys', () => {
|
it('should select nodes using shift and arrow keys', () => {
|
||||||
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();
|
||||||
|
@ -251,6 +269,7 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.getters.selectedNodes().should('have.length', 2);
|
WorkflowPage.getters.selectedNodes().should('have.length', 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix select & deselect
|
||||||
it('should not break lasso selection when dragging node action buttons', () => {
|
it('should not break lasso selection when dragging node action buttons', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.getters
|
WorkflowPage.getters
|
||||||
|
@ -262,6 +281,7 @@ describe('Canvas Actions', () => {
|
||||||
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Fix select & deselect
|
||||||
it('should not break lasso selection with multiple clicks on node action buttons', () => {
|
it('should not break lasso selection with multiple clicks on node action buttons', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from './../constants';
|
} from './../constants';
|
||||||
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();
|
||||||
|
@ -52,15 +53,15 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.waitForLoad();
|
cy.waitForLoad();
|
||||||
// Make sure outputless switch was connected correctly
|
// Make sure outputless switch was connected correctly
|
||||||
cy.get(
|
WorkflowPage.getters
|
||||||
`[data-target-node="${SWITCH_NODE_NAME}1"][data-source-node="${EDIT_FIELDS_SET_NODE_NAME}3"]`,
|
.getConnectionBetweenNodes(`${EDIT_FIELDS_SET_NODE_NAME}3`, `${SWITCH_NODE_NAME}1`)
|
||||||
).should('be.visible');
|
.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
|
WorkflowPage.getters
|
||||||
.canvasNodeInputEndpointByName(setName)
|
.getConnectionBetweenNodes(`${SWITCH_NODE_NAME}`, setName)
|
||||||
.should('have.class', 'jtk-endpoint-connected');
|
.should('exist');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,9 +70,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i++) {
|
||||||
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true);
|
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true);
|
||||||
WorkflowPage.getters
|
WorkflowPage.getters.nodeView().click((i + 1) * 200, (i + 1) * 200, { force: true });
|
||||||
.nodeViewBackground()
|
|
||||||
.click((i + 1) * 200, (i + 1) * 200, { force: true });
|
|
||||||
}
|
}
|
||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
|
|
||||||
|
@ -84,8 +83,6 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
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
|
// Connect Set1 and Set2 to merge
|
||||||
cy.draganddrop(
|
cy.draganddrop(
|
||||||
WorkflowPage.getters.getEndpointSelector('plus', EDIT_FIELDS_SET_NODE_NAME),
|
WorkflowPage.getters.getEndpointSelector('plus', EDIT_FIELDS_SET_NODE_NAME),
|
||||||
|
@ -95,20 +92,36 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.getters.getEndpointSelector('plus', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
WorkflowPage.getters.getEndpointSelector('plus', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
||||||
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1),
|
WorkflowPage.getters.getEndpointSelector('input', MERGE_NODE_NAME, 1),
|
||||||
);
|
);
|
||||||
|
const checkConnections = () => {
|
||||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4);
|
WorkflowPage.getters
|
||||||
|
.getConnectionBetweenNodes(
|
||||||
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
|
`${EDIT_FIELDS_SET_NODE_NAME}1`,
|
||||||
|
)
|
||||||
|
.should('exist');
|
||||||
|
WorkflowPage.getters
|
||||||
|
.getConnectionBetweenNodes(EDIT_FIELDS_SET_NODE_NAME, MERGE_NODE_NAME)
|
||||||
|
.should('exist');
|
||||||
|
WorkflowPage.getters
|
||||||
|
.getConnectionBetweenNodes(`${EDIT_FIELDS_SET_NODE_NAME}1`, MERGE_NODE_NAME)
|
||||||
|
.should('exist');
|
||||||
|
};
|
||||||
|
checkConnections();
|
||||||
|
|
||||||
// Make sure all connections are there after save & reload
|
// Make sure all connections are there after save & reload
|
||||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.waitForLoad();
|
cy.waitForLoad();
|
||||||
|
checkConnections();
|
||||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4);
|
// cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4);
|
||||||
WorkflowPage.actions.executeWorkflow();
|
WorkflowPage.actions.executeWorkflow();
|
||||||
WorkflowPage.getters.stopExecutionButton().should('not.exist');
|
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.get('[data-label="2 items"]').should('be.visible');
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('[data-label="2 items"]').should('be.visible'),
|
||||||
|
() => cy.getByTestId('canvas-node-output-handle').contains('2 items').should('be.visible'),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add nodes and check execution success', () => {
|
it('should add nodes and check execution success', () => {
|
||||||
|
@ -120,16 +133,42 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
WorkflowPage.actions.executeWorkflow();
|
WorkflowPage.actions.executeWorkflow();
|
||||||
|
|
||||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
cy.ifCanvasVersion(
|
||||||
cy.get('.data-count').should('have.length', 4);
|
() => cy.get('.jtk-connector.success').should('have.length', 3),
|
||||||
cy.get('.plus-draggable-endpoint').should('have.class', 'ep-success');
|
() => cy.get('[data-edge-status=success]').should('have.length', 3),
|
||||||
|
);
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.data-count').should('have.length', 4),
|
||||||
|
() => cy.getByTestId('canvas-node-status-success').should('have.length', 4),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.plus-draggable-endpoint').should('have.class', 'ep-success'),
|
||||||
|
() => cy.getByTestId('canvas-handle-plus').should('have.attr', 'data-plus-type', 'success'),
|
||||||
|
);
|
||||||
|
|
||||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
|
|
||||||
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
|
cy.ifCanvasVersion(
|
||||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
() =>
|
||||||
cy.get('.jtk-connector').should('have.length', 4);
|
cy
|
||||||
|
.get('.plus-draggable-endpoint')
|
||||||
|
.filter(':visible')
|
||||||
|
.should('not.have.class', 'ep-success'),
|
||||||
|
() =>
|
||||||
|
cy.getByTestId('canvas-handle-plus').should('not.have.attr', 'data-plus-type', 'success'),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.jtk-connector.success').should('have.length', 3),
|
||||||
|
// The new version of the canvas correctly shows executed data being passed to the input of the next node
|
||||||
|
() => cy.get('[data-edge-status=success]').should('have.length', 4),
|
||||||
|
);
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.data-count').should('have.length', 4),
|
||||||
|
() => cy.getByTestId('canvas-node-status-success').should('have.length', 4),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete node using context menu', () => {
|
it('should delete node using context menu', () => {
|
||||||
|
@ -194,19 +233,29 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
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
|
WorkflowPage.getters
|
||||||
.canvasNodes()
|
.canvasNodes()
|
||||||
.last()
|
.last()
|
||||||
.then(($node) => {
|
.then(($node) => {
|
||||||
const { left, top } = $node.position();
|
const { left, top } = $node.position();
|
||||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
|
||||||
clickToFinish: true,
|
if (isCanvasV2()) {
|
||||||
});
|
cy.drag('.vue-flow__node', [300, 300], {
|
||||||
|
realMouse: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
||||||
|
clickToFinish: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
WorkflowPage.getters
|
WorkflowPage.getters
|
||||||
.canvasNodes()
|
.canvasNodes()
|
||||||
.last()
|
.last()
|
||||||
|
@ -218,91 +267,80 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should zoom in', () => {
|
describe('Canvas Zoom Functionality', () => {
|
||||||
WorkflowPage.getters.zoomInButton().should('be.visible').click();
|
const getContainer = () =>
|
||||||
WorkflowPage.getters
|
cy.ifCanvasVersion(
|
||||||
.nodeView()
|
() => WorkflowPage.getters.nodeView(),
|
||||||
.should(
|
() => WorkflowPage.getters.canvasViewport(),
|
||||||
'have.css',
|
|
||||||
'transform',
|
|
||||||
`matrix(${ZOOM_IN_X1_FACTOR}, 0, 0, ${ZOOM_IN_X1_FACTOR}, 0, 0)`,
|
|
||||||
);
|
);
|
||||||
WorkflowPage.getters.zoomInButton().click();
|
const checkZoomLevel = (expectedFactor: number) => {
|
||||||
WorkflowPage.getters
|
return getContainer().should(($nodeView) => {
|
||||||
.nodeView()
|
const newTransform = $nodeView.css('transform');
|
||||||
.should(
|
const newScale = parseFloat(newTransform.split(',')[0].slice(7));
|
||||||
'have.css',
|
|
||||||
'transform',
|
|
||||||
`matrix(${ZOOM_IN_X2_FACTOR}, 0, 0, ${ZOOM_IN_X2_FACTOR}, 0, 0)`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should zoom out', () => {
|
expect(newScale).to.be.closeTo(expectedFactor, 0.2);
|
||||||
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', () => {
|
const zoomAndCheck = (action: 'zoomIn' | 'zoomOut', expectedFactor: number) => {
|
||||||
WorkflowPage.actions.pinchToZoom(1, 'zoomIn');
|
WorkflowPage.getters[`${action}Button`]().click();
|
||||||
WorkflowPage.getters
|
checkZoomLevel(expectedFactor);
|
||||||
.nodeView()
|
};
|
||||||
.should(
|
|
||||||
'have.css',
|
it('should zoom in', () => {
|
||||||
'transform',
|
WorkflowPage.getters.zoomInButton().should('be.visible');
|
||||||
`matrix(${PINCH_ZOOM_IN_FACTOR}, 0, 0, ${PINCH_ZOOM_IN_FACTOR}, 0, 0)`,
|
getContainer().then(($nodeView) => {
|
||||||
|
const initialTransform = $nodeView.css('transform');
|
||||||
|
const initialScale =
|
||||||
|
initialTransform === 'none' ? 1 : parseFloat(initialTransform.split(',')[0].slice(7));
|
||||||
|
|
||||||
|
zoomAndCheck('zoomIn', initialScale * ZOOM_IN_X1_FACTOR);
|
||||||
|
zoomAndCheck('zoomIn', initialScale * ZOOM_IN_X2_FACTOR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should zoom out', () => {
|
||||||
|
zoomAndCheck('zoomOut', ZOOM_OUT_X1_FACTOR);
|
||||||
|
zoomAndCheck('zoomOut', ZOOM_OUT_X2_FACTOR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should zoom using scroll or pinch gesture', () => {
|
||||||
|
WorkflowPage.actions.pinchToZoom(1, 'zoomIn');
|
||||||
|
|
||||||
|
// V2 Canvas is using the same zoom factor for both pinch and scroll
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => checkZoomLevel(PINCH_ZOOM_IN_FACTOR),
|
||||||
|
() => checkZoomLevel(ZOOM_IN_X1_FACTOR),
|
||||||
);
|
);
|
||||||
|
|
||||||
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
|
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
|
||||||
// Zoom in 1x + Zoom out 1x should reset to default (=1)
|
checkZoomLevel(1); // 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.actions.pinchToZoom(1, 'zoomOut');
|
||||||
WorkflowPage.getters
|
|
||||||
.nodeView()
|
cy.ifCanvasVersion(
|
||||||
.should(
|
() => checkZoomLevel(PINCH_ZOOM_OUT_FACTOR),
|
||||||
'have.css',
|
() => checkZoomLevel(ZOOM_OUT_X1_FACTOR),
|
||||||
'transform',
|
|
||||||
`matrix(${PINCH_ZOOM_OUT_FACTOR}, 0, 0, ${PINCH_ZOOM_OUT_FACTOR}, 0, 0)`,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset zoom', () => {
|
it('should reset zoom', () => {
|
||||||
// Reset zoom should not appear until zoom level changed
|
WorkflowPage.getters.resetZoomButton().should('not.exist');
|
||||||
WorkflowPage.getters.resetZoomButton().should('not.exist');
|
WorkflowPage.getters.zoomInButton().click();
|
||||||
WorkflowPage.getters.zoomInButton().click();
|
WorkflowPage.getters.resetZoomButton().should('be.visible').click();
|
||||||
WorkflowPage.getters.resetZoomButton().should('be.visible').click();
|
checkZoomLevel(DEFAULT_ZOOM_FACTOR);
|
||||||
WorkflowPage.getters
|
});
|
||||||
.nodeView()
|
|
||||||
.should(
|
|
||||||
'have.css',
|
|
||||||
'transform',
|
|
||||||
`matrix(${DEFAULT_ZOOM_FACTOR}, 0, 0, ${DEFAULT_ZOOM_FACTOR}, 0, 0)`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should zoom to fit', () => {
|
it('should zoom to fit', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
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);
|
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
|
// At this point last added node should be off-screen
|
||||||
WorkflowPage.getters.canvasNodes().last().should('not.be.visible');
|
WorkflowPage.getters.canvasNodes().last().should('not.be.visible');
|
||||||
WorkflowPage.getters.zoomToFitButton().click();
|
WorkflowPage.getters.zoomToFitButton().click();
|
||||||
WorkflowPage.getters.canvasNodes().last().should('be.visible');
|
WorkflowPage.getters.canvasNodes().last().should('be.visible');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable node (context menu or shortcut)', () => {
|
it('should disable node (context menu or shortcut)', () => {
|
||||||
|
@ -426,9 +464,9 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.waitForLoad();
|
cy.waitForLoad();
|
||||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').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));
|
||||||
|
@ -441,6 +479,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Unknown nodes should still render connection endpoints
|
||||||
it('should render connections correctly if unkown nodes are present', () => {
|
it('should render connections correctly if unkown nodes are present', () => {
|
||||||
const unknownNodeName = 'Unknown node';
|
const unknownNodeName = 'Unknown node';
|
||||||
cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes');
|
cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { SCHEDULE_TRIGGER_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
|
import { SCHEDULE_TRIGGER_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from '../constants';
|
||||||
import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages';
|
import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages';
|
||||||
import { clearNotifications, errorToast, successToast } from '../pages/notifications';
|
import { clearNotifications, errorToast, successToast } from '../pages/notifications';
|
||||||
|
import { isCanvasV2 } from '../utils/workflowUtils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPageClass();
|
const workflowPage = new WorkflowPageClass();
|
||||||
const executionsTab = new WorkflowExecutionsTab();
|
const executionsTab = new WorkflowExecutionsTab();
|
||||||
|
@ -117,15 +118,22 @@ describe('Execution', () => {
|
||||||
.canvasNodeByName('Manual')
|
.canvasNodeByName('Manual')
|
||||||
.within(() => cy.get('.fa-check'))
|
.within(() => cy.get('.fa-check'))
|
||||||
.should('exist');
|
.should('exist');
|
||||||
workflowPage.getters
|
|
||||||
.canvasNodeByName('Wait')
|
if (isCanvasV2()) {
|
||||||
.within(() => cy.get('.fa-sync-alt').should('not.visible'));
|
workflowPage.getters
|
||||||
|
.canvasNodeByName('Wait')
|
||||||
|
.within(() => cy.get('.fa-sync-alt').should('not.exist'));
|
||||||
|
} else {
|
||||||
|
workflowPage.getters
|
||||||
|
.canvasNodeByName('Wait')
|
||||||
|
.within(() => cy.get('.fa-sync-alt').should('not.be.visible'));
|
||||||
|
}
|
||||||
|
|
||||||
workflowPage.getters
|
workflowPage.getters
|
||||||
.canvasNodeByName('Set')
|
.canvasNodeByName('Set')
|
||||||
.within(() => cy.get('.fa-check').should('not.exist'));
|
.within(() => cy.get('.fa-check').should('not.exist'));
|
||||||
|
|
||||||
successToast().should('be.visible');
|
successToast().should('be.visible');
|
||||||
clearNotifications();
|
|
||||||
|
|
||||||
// Clear execution data
|
// Clear execution data
|
||||||
workflowPage.getters.clearExecutionDataButton().should('be.visible');
|
workflowPage.getters.clearExecutionDataButton().should('be.visible');
|
||||||
|
@ -206,6 +214,7 @@ describe('Execution', () => {
|
||||||
workflowPage.getters.clearExecutionDataButton().should('not.exist');
|
workflowPage.getters.clearExecutionDataButton().should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Webhook should show waiting state but it doesn't
|
||||||
it('should test webhook workflow stop', () => {
|
it('should test webhook workflow stop', () => {
|
||||||
cy.createFixtureWorkflow('Webhook_wait_set.json');
|
cy.createFixtureWorkflow('Webhook_wait_set.json');
|
||||||
|
|
||||||
|
@ -267,9 +276,17 @@ describe('Execution', () => {
|
||||||
.canvasNodeByName('Webhook')
|
.canvasNodeByName('Webhook')
|
||||||
.within(() => cy.get('.fa-check'))
|
.within(() => cy.get('.fa-check'))
|
||||||
.should('exist');
|
.should('exist');
|
||||||
workflowPage.getters
|
|
||||||
.canvasNodeByName('Wait')
|
if (isCanvasV2()) {
|
||||||
.within(() => cy.get('.fa-sync-alt').should('not.visible'));
|
workflowPage.getters
|
||||||
|
.canvasNodeByName('Wait')
|
||||||
|
.within(() => cy.get('.fa-sync-alt').should('not.exist'));
|
||||||
|
} else {
|
||||||
|
workflowPage.getters
|
||||||
|
.canvasNodeByName('Wait')
|
||||||
|
.within(() => cy.get('.fa-sync-alt').should('not.be.visible'));
|
||||||
|
}
|
||||||
|
|
||||||
workflowPage.getters
|
workflowPage.getters
|
||||||
.canvasNodeByName('Set')
|
.canvasNodeByName('Set')
|
||||||
.within(() => cy.get('.fa-check').should('not.exist'));
|
.within(() => cy.get('.fa-check').should('not.exist'));
|
||||||
|
@ -295,6 +312,7 @@ describe('Execution', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Canvas V2: Missing pinned states for `edge-label-wrapper`
|
||||||
describe('connections should be colored differently for pinned data', () => {
|
describe('connections should be colored differently for pinned data', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.createFixtureWorkflow('Schedule_pinned.json');
|
cy.createFixtureWorkflow('Schedule_pinned.json');
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { BasePage } from './base';
|
||||||
import { NodeCreator } from './features/node-creator';
|
import { NodeCreator } from './features/node-creator';
|
||||||
import { META_KEY } from '../constants';
|
import { META_KEY } from '../constants';
|
||||||
import { getVisibleSelect } from '../utils';
|
import { getVisibleSelect } from '../utils';
|
||||||
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
import { getUniqueWorkflowName, isCanvasV2 } from '../utils/workflowUtils';
|
||||||
|
|
||||||
const nodeCreator = new NodeCreator();
|
const nodeCreator = new NodeCreator();
|
||||||
export class WorkflowPage extends BasePage {
|
export class WorkflowPage extends BasePage {
|
||||||
|
@ -27,7 +27,11 @@ 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: () => cy.getByTestId('canvas-node'),
|
canvasNodes: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('canvas-node'),
|
||||||
|
() => cy.getByTestId('canvas-node').not('[data-node-type="n8n-nodes-internal.addNodes"]'),
|
||||||
|
),
|
||||||
canvasNodeByName: (nodeName: string) =>
|
canvasNodeByName: (nodeName: string) =>
|
||||||
this.getters.canvasNodes().filter(`:contains(${nodeName})`),
|
this.getters.canvasNodes().filter(`:contains(${nodeName})`),
|
||||||
nodeIssuesByName: (nodeName: string) =>
|
nodeIssuesByName: (nodeName: string) =>
|
||||||
|
@ -37,6 +41,17 @@ export class WorkflowPage extends BasePage {
|
||||||
.should('have.length.greaterThan', 0)
|
.should('have.length.greaterThan', 0)
|
||||||
.findChildByTestId('node-issues'),
|
.findChildByTestId('node-issues'),
|
||||||
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
|
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
|
||||||
|
if (isCanvasV2()) {
|
||||||
|
if (type === 'input') {
|
||||||
|
return `[data-test-id="canvas-node-input-handle"][data-node-name="${nodeName}"][data-handle-index="${index}"]`;
|
||||||
|
}
|
||||||
|
if (type === 'output') {
|
||||||
|
return `[data-test-id="canvas-node-output-handle"][data-node-name="${nodeName}"][data-handle-index="${index}"]`;
|
||||||
|
}
|
||||||
|
if (type === 'plus') {
|
||||||
|
return `[data-test-id="canvas-node-output-handle"][data-node-name="${nodeName}"][data-handle-index="${index}"] [data-test-id="canvas-handle-plus"] .clickable`;
|
||||||
|
}
|
||||||
|
}
|
||||||
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`;
|
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`;
|
||||||
},
|
},
|
||||||
canvasNodeInputEndpointByName: (nodeName: string, index = 0) => {
|
canvasNodeInputEndpointByName: (nodeName: string, index = 0) => {
|
||||||
|
@ -46,7 +61,15 @@ export class WorkflowPage extends BasePage {
|
||||||
return cy.get(this.getters.getEndpointSelector('output', nodeName, index));
|
return cy.get(this.getters.getEndpointSelector('output', nodeName, index));
|
||||||
},
|
},
|
||||||
canvasNodePlusEndpointByName: (nodeName: string, index = 0) => {
|
canvasNodePlusEndpointByName: (nodeName: string, index = 0) => {
|
||||||
return cy.get(this.getters.getEndpointSelector('plus', nodeName, index));
|
return cy.ifCanvasVersion(
|
||||||
|
() => cy.get(this.getters.getEndpointSelector('plus', nodeName, index)),
|
||||||
|
() =>
|
||||||
|
cy
|
||||||
|
.get(
|
||||||
|
`[data-test-id="canvas-node-output-handle"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"] .clickable`,
|
||||||
|
)
|
||||||
|
.eq(index),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
|
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
|
||||||
workflowMenu: () => cy.getByTestId('workflow-menu'),
|
workflowMenu: () => cy.getByTestId('workflow-menu'),
|
||||||
|
@ -56,13 +79,29 @@ export class WorkflowPage extends BasePage {
|
||||||
expressionModalInput: () => cy.getByTestId('expression-modal-input').find('[role=textbox]'),
|
expressionModalInput: () => cy.getByTestId('expression-modal-input').find('[role=textbox]'),
|
||||||
expressionModalOutput: () => cy.getByTestId('expression-modal-output'),
|
expressionModalOutput: () => cy.getByTestId('expression-modal-output'),
|
||||||
|
|
||||||
nodeViewRoot: () => cy.getByTestId('node-view-root'),
|
nodeViewRoot: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('node-view-root'),
|
||||||
|
() => this.getters.nodeView(),
|
||||||
|
),
|
||||||
copyPasteInput: () => cy.getByTestId('hidden-copy-paste'),
|
copyPasteInput: () => cy.getByTestId('hidden-copy-paste'),
|
||||||
nodeConnections: () => cy.get('.jtk-connector'),
|
nodeConnections: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.jtk-connector'),
|
||||||
|
() => cy.getByTestId('edge-label-wrapper'),
|
||||||
|
),
|
||||||
zoomToFitButton: () => cy.getByTestId('zoom-to-fit'),
|
zoomToFitButton: () => cy.getByTestId('zoom-to-fit'),
|
||||||
nodeEndpoints: () => cy.get('.jtk-endpoint-connected'),
|
nodeEndpoints: () => cy.get('.jtk-endpoint-connected'),
|
||||||
disabledNodes: () => cy.get('.node-box.disabled'),
|
disabledNodes: () =>
|
||||||
selectedNodes: () => this.getters.canvasNodes().filter('.jtk-drag-selected'),
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.get('.node-box.disabled'),
|
||||||
|
() => cy.get('[data-test-id="canvas-trigger-node"][class*="disabled"]'),
|
||||||
|
),
|
||||||
|
selectedNodes: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => this.getters.canvasNodes().filter('.jtk-drag-selected'),
|
||||||
|
() => this.getters.canvasNodes().parent().filter('.selected'),
|
||||||
|
),
|
||||||
// Workflow menu items
|
// Workflow menu items
|
||||||
workflowMenuItemDuplicate: () => cy.getByTestId('workflow-menu-item-duplicate'),
|
workflowMenuItemDuplicate: () => cy.getByTestId('workflow-menu-item-duplicate'),
|
||||||
workflowMenuItemDownload: () => cy.getByTestId('workflow-menu-item-download'),
|
workflowMenuItemDownload: () => cy.getByTestId('workflow-menu-item-download'),
|
||||||
|
@ -92,8 +131,21 @@ export class WorkflowPage extends BasePage {
|
||||||
shareButton: () => cy.getByTestId('workflow-share-button'),
|
shareButton: () => cy.getByTestId('workflow-share-button'),
|
||||||
|
|
||||||
duplicateWorkflowModal: () => cy.getByTestId('duplicate-modal'),
|
duplicateWorkflowModal: () => cy.getByTestId('duplicate-modal'),
|
||||||
nodeViewBackground: () => cy.getByTestId('node-view-background'),
|
nodeViewBackground: () =>
|
||||||
nodeView: () => cy.getByTestId('node-view'),
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('node-view-background'),
|
||||||
|
() => cy.getByTestId('canvas'),
|
||||||
|
),
|
||||||
|
nodeView: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('node-view'),
|
||||||
|
() => cy.get('[data-test-id="canvas-wrapper"]'),
|
||||||
|
),
|
||||||
|
canvasViewport: () =>
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('node-view'),
|
||||||
|
() => cy.get('.vue-flow__transformationpane.vue-flow__container'),
|
||||||
|
),
|
||||||
inlineExpressionEditorInput: () =>
|
inlineExpressionEditorInput: () =>
|
||||||
cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
|
cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
|
||||||
inlineExpressionEditorOutput: () => cy.getByTestId('inline-expression-editor-output'),
|
inlineExpressionEditorOutput: () => cy.getByTestId('inline-expression-editor-output'),
|
||||||
|
@ -115,12 +167,26 @@ export class WorkflowPage extends BasePage {
|
||||||
ndvParameters: () => cy.getByTestId('parameter-item'),
|
ndvParameters: () => cy.getByTestId('parameter-item'),
|
||||||
nodeCredentialsLabel: () => cy.getByTestId('credentials-label'),
|
nodeCredentialsLabel: () => cy.getByTestId('credentials-label'),
|
||||||
getConnectionBetweenNodes: (sourceNodeName: string, targetNodeName: string) =>
|
getConnectionBetweenNodes: (sourceNodeName: string, targetNodeName: string) =>
|
||||||
cy.get(
|
cy.ifCanvasVersion(
|
||||||
`.jtk-connector[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`.jtk-connector[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`[data-test-id="edge-label-wrapper"][data-source-node-name="${sourceNodeName}"][data-target-node-name="${targetNodeName}"]`,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
getConnectionActionsBetweenNodes: (sourceNodeName: string, targetNodeName: string) =>
|
getConnectionActionsBetweenNodes: (sourceNodeName: string, targetNodeName: string) =>
|
||||||
cy.get(
|
cy.ifCanvasVersion(
|
||||||
`.connection-actions[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`.connection-actions[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
cy.get(
|
||||||
|
`[data-test-id="edge-label-wrapper"][data-source-node-name="${sourceNodeName}"][data-target-node-name="${targetNodeName}"] [data-test-id="canvas-edge-toolbar"]`,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
addStickyButton: () => cy.getByTestId('add-sticky-button'),
|
addStickyButton: () => cy.getByTestId('add-sticky-button'),
|
||||||
stickies: () => cy.getByTestId('sticky'),
|
stickies: () => cy.getByTestId('sticky'),
|
||||||
|
@ -128,6 +194,18 @@ export class WorkflowPage extends BasePage {
|
||||||
workflowHistoryButton: () => cy.getByTestId('workflow-history-button'),
|
workflowHistoryButton: () => cy.getByTestId('workflow-history-button'),
|
||||||
colors: () => cy.getByTestId('color'),
|
colors: () => cy.getByTestId('color'),
|
||||||
contextMenuAction: (action: string) => cy.getByTestId(`context-menu-item-${action}`),
|
contextMenuAction: (action: string) => cy.getByTestId(`context-menu-item-${action}`),
|
||||||
|
getNodeLeftPosition: (element: JQuery<HTMLElement>) => {
|
||||||
|
if (isCanvasV2()) {
|
||||||
|
return parseFloat(element.parent().css('transform').split(',')[4]);
|
||||||
|
}
|
||||||
|
return parseFloat(element.css('left'));
|
||||||
|
},
|
||||||
|
getNodeTopPosition: (element: JQuery<HTMLElement>) => {
|
||||||
|
if (isCanvasV2()) {
|
||||||
|
return parseFloat(element.parent().css('transform').split(',')[5]);
|
||||||
|
}
|
||||||
|
return parseFloat(element.css('top'));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
|
@ -332,7 +410,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.nodeViewBackground().trigger('wheel', {
|
this.getters.nodeView().trigger('wheel', {
|
||||||
force: true,
|
force: true,
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
|
@ -391,9 +469,12 @@ export class WorkflowPage extends BasePage {
|
||||||
action?: string,
|
action?: string,
|
||||||
) => {
|
) => {
|
||||||
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
|
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
|
||||||
this.getters
|
const connectionsBetweenNodes = () =>
|
||||||
.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName)
|
this.getters.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName);
|
||||||
.find('.add')
|
cy.ifCanvasVersion(
|
||||||
|
() => connectionsBetweenNodes().find('.add'),
|
||||||
|
() => connectionsBetweenNodes().get('[data-test-id="add-connection-button"]'),
|
||||||
|
)
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
||||||
|
@ -401,9 +482,12 @@ export class WorkflowPage extends BasePage {
|
||||||
},
|
},
|
||||||
deleteNodeBetweenNodes: (sourceNodeName: string, targetNodeName: string) => {
|
deleteNodeBetweenNodes: (sourceNodeName: string, targetNodeName: string) => {
|
||||||
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
|
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
|
||||||
this.getters
|
const connectionsBetweenNodes = () =>
|
||||||
.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName)
|
this.getters.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName);
|
||||||
.find('.delete')
|
cy.ifCanvasVersion(
|
||||||
|
() => connectionsBetweenNodes().find('.delete'),
|
||||||
|
() => connectionsBetweenNodes().get('[data-test-id="delete-connection-button"]'),
|
||||||
|
)
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
N8N_AUTH_COOKIE,
|
N8N_AUTH_COOKIE,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { WorkflowPage } from '../pages';
|
import { WorkflowPage } from '../pages';
|
||||||
import { getUniqueWorkflowName } from '../utils/workflowUtils';
|
import { getUniqueWorkflowName, isCanvasV2 } from '../utils/workflowUtils';
|
||||||
|
|
||||||
Cypress.Commands.add('setAppDate', (targetDate: number | Date) => {
|
Cypress.Commands.add('setAppDate', (targetDate: number | Date) => {
|
||||||
cy.window().then((win) => {
|
cy.window().then((win) => {
|
||||||
|
@ -26,6 +26,10 @@ Cypress.Commands.add('getByTestId', (selector, ...args) => {
|
||||||
return cy.get(`[data-test-id="${selector}"]`, ...args);
|
return cy.get(`[data-test-id="${selector}"]`, ...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('ifCanvasVersion', (getterV1, getterV2) => {
|
||||||
|
return isCanvasV2() ? getterV2() : getterV1();
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add(
|
Cypress.Commands.add(
|
||||||
'createFixtureWorkflow',
|
'createFixtureWorkflow',
|
||||||
(fixtureKey: string, workflowName = getUniqueWorkflowName()) => {
|
(fixtureKey: string, workflowName = getUniqueWorkflowName()) => {
|
||||||
|
|
|
@ -20,6 +20,11 @@ beforeEach(() => {
|
||||||
win.localStorage.setItem('N8N_THEME', 'light');
|
win.localStorage.setItem('N8N_THEME', 'light');
|
||||||
win.localStorage.setItem('N8N_AUTOCOMPLETE_ONBOARDED', 'true');
|
win.localStorage.setItem('N8N_AUTOCOMPLETE_ONBOARDED', 'true');
|
||||||
win.localStorage.setItem('N8N_MAPPING_ONBOARDED', 'true');
|
win.localStorage.setItem('N8N_MAPPING_ONBOARDED', 'true');
|
||||||
|
|
||||||
|
const nodeViewVersion = Cypress.env('NODE_VIEW_VERSION');
|
||||||
|
if (nodeViewVersion) {
|
||||||
|
win.localStorage.setItem('NodeView.version', nodeViewVersion);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.intercept('GET', '/rest/settings', (req) => {
|
cy.intercept('GET', '/rest/settings', (req) => {
|
||||||
|
|
|
@ -28,6 +28,7 @@ declare global {
|
||||||
selector: string,
|
selector: string,
|
||||||
...args: Array<Partial<Loggable & Timeoutable & Withinable & Shadow> | undefined>
|
...args: Array<Partial<Loggable & Timeoutable & Withinable & Shadow> | undefined>
|
||||||
): Chainable<JQuery<HTMLElement>>;
|
): Chainable<JQuery<HTMLElement>>;
|
||||||
|
ifCanvasVersion<T1, T2>(getterV1: () => T1, getterV2: () => T2): T1 | T2;
|
||||||
findChildByTestId(childTestId: string): Chainable<JQuery<HTMLElement>>;
|
findChildByTestId(childTestId: string): Chainable<JQuery<HTMLElement>>;
|
||||||
/**
|
/**
|
||||||
* Creates a workflow from the given fixture and optionally renames it.
|
* Creates a workflow from the given fixture and optionally renames it.
|
||||||
|
|
|
@ -3,3 +3,7 @@ import { nanoid } from 'nanoid';
|
||||||
export function getUniqueWorkflowName(workflowNamePrefix?: string) {
|
export function getUniqueWorkflowName(workflowNamePrefix?: string) {
|
||||||
return workflowNamePrefix ? `${workflowNamePrefix} ${nanoid(12)}` : nanoid(12);
|
return workflowNamePrefix ? `${workflowNamePrefix} ${nanoid(12)}` : nanoid(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCanvasV2() {
|
||||||
|
return Cypress.env('NODE_VIEW_VERSION') === 2;
|
||||||
|
}
|
||||||
|
|
|
@ -78,14 +78,14 @@ watch(
|
||||||
void externalHooks.run('expressionEdit.dialogVisibleChanged', {
|
void externalHooks.run('expressionEdit.dialogVisibleChanged', {
|
||||||
dialogVisible: newValue,
|
dialogVisible: newValue,
|
||||||
parameter: props.parameter,
|
parameter: props.parameter,
|
||||||
value: props.modelValue,
|
value: props.modelValue.toString(),
|
||||||
resolvedExpressionValue,
|
resolvedExpressionValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
const telemetryPayload = createExpressionTelemetryPayload(
|
const telemetryPayload = createExpressionTelemetryPayload(
|
||||||
segments.value,
|
segments.value,
|
||||||
props.modelValue,
|
props.modelValue.toString(),
|
||||||
workflowsStore.workflowId,
|
workflowsStore.workflowId,
|
||||||
ndvStore.pushRef,
|
ndvStore.pushRef,
|
||||||
ndvStore.activeNode?.type ?? '',
|
ndvStore.activeNode?.type ?? '',
|
||||||
|
|
|
@ -668,7 +668,7 @@ onBeforeUnmount(() => {
|
||||||
width="auto"
|
width="auto"
|
||||||
:append-to="`#${APP_MODALS_ELEMENT_ID}`"
|
:append-to="`#${APP_MODALS_ELEMENT_ID}`"
|
||||||
data-test-id="ndv"
|
data-test-id="ndv"
|
||||||
z-index="1800"
|
:z-index="1800"
|
||||||
:data-has-output-connection="hasOutputConnection"
|
:data-has-output-connection="hasOutputConnection"
|
||||||
>
|
>
|
||||||
<n8n-tooltip
|
<n8n-tooltip
|
||||||
|
|
|
@ -713,7 +713,8 @@ const populateSettings = () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'stopWorkflow',
|
default: 'stopWorkflow',
|
||||||
noDataExpression: i18n.baseText('nodeSettings.onError.description'),
|
description: i18n.baseText('nodeSettings.onError.description'),
|
||||||
|
noDataExpression: true,
|
||||||
},
|
},
|
||||||
] as INodeProperties[]),
|
] as INodeProperties[]),
|
||||||
);
|
);
|
||||||
|
|
|
@ -52,7 +52,7 @@ const { nodes: mappedNodes, connections: mappedConnections } = useCanvasMapping(
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.wrapper">
|
<div :class="$style.wrapper" data-test-id="canvas-wrapper">
|
||||||
<div :class="$style.canvas">
|
<div :class="$style.canvas">
|
||||||
<Canvas
|
<Canvas
|
||||||
v-if="workflow"
|
v-if="workflow"
|
||||||
|
|
|
@ -117,6 +117,9 @@ function onDelete() {
|
||||||
<EdgeLabelRenderer>
|
<EdgeLabelRenderer>
|
||||||
<div
|
<div
|
||||||
data-test-id="edge-label-wrapper"
|
data-test-id="edge-label-wrapper"
|
||||||
|
:data-source-node-name="sourceNode?.label"
|
||||||
|
:data-target-node-name="targetNode?.label"
|
||||||
|
:data-edge-status="status"
|
||||||
:style="edgeToolbarStyle"
|
:style="edgeToolbarStyle"
|
||||||
:class="$style.edgeLabelWrapper"
|
:class="$style.edgeLabelWrapper"
|
||||||
@mouseenter="isHovered = true"
|
@mouseenter="isHovered = true"
|
||||||
|
|
|
@ -73,6 +73,7 @@ function onClickAdd() {
|
||||||
v-if="!isConnected && !isReadOnly"
|
v-if="!isConnected && !isReadOnly"
|
||||||
v-show="isHandlePlusVisible"
|
v-show="isHandlePlusVisible"
|
||||||
data-test-id="canvas-handle-plus"
|
data-test-id="canvas-handle-plus"
|
||||||
|
:data-plus-type="plusType"
|
||||||
:line-size="plusLineSize"
|
:line-size="plusLineSize"
|
||||||
:handle-classes="handleClasses"
|
:handle-classes="handleClasses"
|
||||||
:type="plusType"
|
:type="plusType"
|
||||||
|
|
|
@ -259,6 +259,7 @@ onBeforeUnmount(() => {
|
||||||
<div
|
<div
|
||||||
:class="[$style.canvasNode, { [$style.showToolbar]: showToolbar }]"
|
:class="[$style.canvasNode, { [$style.showToolbar]: showToolbar }]"
|
||||||
data-test-id="canvas-node"
|
data-test-id="canvas-node"
|
||||||
|
:data-node-type="data.type"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="source in mappedOutputs"
|
v-for="source in mappedOutputs"
|
||||||
|
@ -269,7 +270,9 @@ onBeforeUnmount(() => {
|
||||||
:mode="CanvasConnectionMode.Output"
|
:mode="CanvasConnectionMode.Output"
|
||||||
:is-read-only="readOnly"
|
:is-read-only="readOnly"
|
||||||
:is-valid-connection="isValidConnection"
|
:is-valid-connection="isValidConnection"
|
||||||
|
:data-node-name="label"
|
||||||
data-test-id="canvas-node-output-handle"
|
data-test-id="canvas-node-output-handle"
|
||||||
|
:data-handle-index="source.index"
|
||||||
@add="onAdd"
|
@add="onAdd"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -284,6 +287,8 @@ onBeforeUnmount(() => {
|
||||||
:is-read-only="readOnly"
|
:is-read-only="readOnly"
|
||||||
:is-valid-connection="isValidConnection"
|
:is-valid-connection="isValidConnection"
|
||||||
data-test-id="canvas-node-input-handle"
|
data-test-id="canvas-node-input-handle"
|
||||||
|
:data-handle-index="target.index"
|
||||||
|
:data-node-name="label"
|
||||||
@add="onAdd"
|
@add="onAdd"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1784,9 +1784,6 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
workflowsStore.addWorkflowTagIds(tagIds);
|
workflowsStore.addWorkflowTagIds(tagIds);
|
||||||
setTimeout(() => {
|
|
||||||
nodeHelpers.addPinDataConnections(workflowsStore.pinnedWorkflowData);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchWorkflowDataFromUrl(url: string): Promise<IWorkflowDataUpdate | undefined> {
|
async function fetchWorkflowDataFromUrl(url: string): Promise<IWorkflowDataUpdate | undefined> {
|
||||||
|
|
|
@ -1443,6 +1443,7 @@ function selectNodes(ids: string[]) {
|
||||||
function onClickPane(position: CanvasNode['position']) {
|
function onClickPane(position: CanvasNode['position']) {
|
||||||
lastClickPosition.value = [position.x, position.y];
|
lastClickPosition.value = [position.x, position.y];
|
||||||
uiStore.isCreateNodeActive = false;
|
uiStore.isCreateNodeActive = false;
|
||||||
|
setNodeSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1704,7 +1704,7 @@ export default defineComponent({
|
||||||
if (data.nodes.length > 0) {
|
if (data.nodes.length > 0) {
|
||||||
if (!isCut) {
|
if (!isCut) {
|
||||||
this.showMessage({
|
this.showMessage({
|
||||||
title: 'Copied!',
|
title: this.$locale.baseText('generic.copiedToClipboard'),
|
||||||
message: '',
|
message: '',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue