diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts index a05877bdbd5e2..d10f3ac850ea5 100644 --- a/cypress/e2e/1-workflows.cy.ts +++ b/cypress/e2e/1-workflows.cy.ts @@ -1,13 +1,7 @@ -import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; -import { randFirstName, randLastName } from '@ngneat/falso'; import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { v4 as uuid } from 'uuid'; -const email = DEFAULT_USER_EMAIL; -const password = DEFAULT_USER_PASSWORD; -const firstName = randFirstName(); -const lastName = randLastName(); const WorkflowsPage = new WorkflowsPageClass(); const WorkflowPage = new WorkflowPageClass(); @@ -16,17 +10,10 @@ const multipleWorkflowsCount = 5; describe('Workflows', () => { before(() => { cy.resetAll(); - cy.setup({ email, firstName, lastName, password }); + cy.skipSetup(); }); beforeEach(() => { - cy.on('uncaught:exception', (err, runnable) => { - expect(err.message).to.include('Not logged in'); - - return false; - }); - - cy.signin({ email, password }); cy.visit(WorkflowsPage.url); }); diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts new file mode 100644 index 0000000000000..518857106fdcb --- /dev/null +++ b/cypress/e2e/12-canvas-actions.cy.ts @@ -0,0 +1,172 @@ +import { + MANUAL_TRIGGER_NODE_NAME, + MANUAL_TRIGGER_NODE_DISPLAY_NAME, + CODE_NODE_NAME, + SCHEDULE_TRIGGER_NODE_NAME, + SET_NODE_NAME, + IF_NODE_NAME, + HTTP_REQUEST_NODE_NAME, +} from './../constants'; +import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; + +const WorkflowPage = new WorkflowPageClass(); +describe('Canvas Actions', () => { + before(() => { + cy.resetAll(); + cy.skipSetup(); + }); + + beforeEach(() => { + WorkflowPage.actions.visit(); + }); + + it('should render canvas', () => { + WorkflowPage.getters.nodeViewRoot().should('be.visible'); + WorkflowPage.getters.canvasPlusButton().should('be.visible'); + WorkflowPage.getters.zoomToFitButton().should('be.visible'); + WorkflowPage.getters.zoomInButton().should('be.visible'); + WorkflowPage.getters.zoomOutButton().should('be.visible'); + WorkflowPage.getters.executeWorkflowButton().should('be.visible'); + }); + + it('should connect and disconnect a simple node', () => { + WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); + WorkflowPage.getters.nodeViewBackground().click(600, 200, { force: true }); + cy.get('.jtk-connector').should('have.length', 1); + WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); + + // Change connection from Set to Set1 + cy.draganddrop( + WorkflowPage.getters.getEndpointSelector('input', SET_NODE_NAME), + WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`), + ); + + WorkflowPage.getters + .canvasNodeInputEndpointByName(`${SET_NODE_NAME}1`) + .should('have.class', 'jtk-endpoint-connected'); + + cy.get('.jtk-connector').should('have.length', 1); + // Disconnect Set1 + cy.drag(WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`), [-200, 100]); + cy.get('.jtk-connector').should('have.length', 0); + }); + + it('should add first step', () => { + WorkflowPage.getters.canvasPlusButton().should('be.visible'); + WorkflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.getters.canvasNodes().should('have.length', 1); + }); + + it('should add a node via plus endpoint drag', () => { + WorkflowPage.getters.canvasPlusButton().should('be.visible'); + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, true); + + cy.drag( + WorkflowPage.getters.getEndpointSelector('plus', SCHEDULE_TRIGGER_NODE_NAME), + [100, 100], + ); + + WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + WorkflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false); + WorkflowPage.getters.nodeViewBackground().click({ force: true }); + }); + + + it('should add a connected node using plus endpoint', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + cy.get('.plus-endpoint').should('be.visible').click(); + WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME); + WorkflowPage.getters.nodeCreatorSearchBar().type('{enter}'); + cy.get('body').type('{esc}'); + WorkflowPage.getters.canvasNodes().should('have.length', 2); + WorkflowPage.getters.nodeConnections().should('have.length', 1); + }); + + it('should add disconnected node if nothing is selected', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + // Deselect nodes + WorkflowPage.getters.nodeViewBackground().click({ force: true }); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.getters.canvasNodes().should('have.length', 2); + WorkflowPage.getters.nodeConnections().should('have.length', 0); + }); + + it('should add node between two connected nodes', () => { + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); + WorkflowPage.actions.zoomToFit(); + WorkflowPage.actions.addNodeBetweenNodes(CODE_NODE_NAME, SET_NODE_NAME, HTTP_REQUEST_NODE_NAME); + WorkflowPage.getters.canvasNodes().should('have.length', 4); + WorkflowPage.getters.nodeConnections().should('have.length', 3); + // And last node should be pushed to the right + WorkflowPage.getters + .canvasNodes() + .last() + .should('have.attr', 'style', 'left: 860px; top: 260px;'); + }); + + it('should delete connections by pressing the delete button', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.getters.nodeConnections().first().realHover(); + cy.get('.connection-actions .delete').first().click({ force: true }); + WorkflowPage.getters.nodeConnections().should('have.length', 0); + }); + + it('should delete a connection by moving it away from endpoint', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.drag(WorkflowPage.getters.getEndpointSelector('input', CODE_NODE_NAME), [0, -100]); + WorkflowPage.getters.nodeConnections().should('have.length', 0); + }); + + it('should execute node', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.getters + .canvasNodes() + .last() + .find('[data-test-id="execute-node-button"]') + .click({ force: true }); + WorkflowPage.getters.successToast().should('contain', 'Node executed successfully'); + }); + + it('should copy selected nodes', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.actions.selectAll(); + WorkflowPage.actions.hitCopy(); + WorkflowPage.getters.successToast().should('contain', 'Copied!'); + }); + + it('should select all nodes', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + WorkflowPage.actions.selectAll(); + WorkflowPage.getters.selectedNodes().should('have.length', 2); + }); + + it('should select nodes using arrow keys', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.wait(500); + cy.get('body').type('{leftArrow}'); + WorkflowPage.getters.canvasNodes().first().should('have.class', 'jtk-drag-selected'); + cy.get('body').type('{rightArrow}'); + WorkflowPage.getters.canvasNodes().last().should('have.class', 'jtk-drag-selected'); + }); + + it('should select nodes using shift and arrow keys', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.wait(500); + cy.get('body').type('{shift}', { release: false }).type('{leftArrow}'); + WorkflowPage.getters.selectedNodes().should('have.length', 2); + }); +}); diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts index 3f78e49590c31..5f2fe8db07c37 100644 --- a/cypress/e2e/12-canvas.cy.ts +++ b/cypress/e2e/12-canvas.cy.ts @@ -20,7 +20,7 @@ const ZOOM_OUT_X1_FACTOR = 0.8; const ZOOM_OUT_X2_FACTOR = 0.64; const RENAME_NODE_NAME = 'Something else'; -describe('Canvas Actions', () => { +describe('Canvas Node Manipulation and Navigation', () => { before(() => { cy.resetAll(); cy.skipSetup(); @@ -30,56 +30,6 @@ describe('Canvas Actions', () => { WorkflowPage.actions.visit(); }); - it('should render canvas', () => { - WorkflowPage.getters.nodeViewRoot().should('be.visible'); - WorkflowPage.getters.canvasPlusButton().should('be.visible'); - WorkflowPage.getters.zoomToFitButton().should('be.visible'); - WorkflowPage.getters.zoomInButton().should('be.visible'); - WorkflowPage.getters.zoomOutButton().should('be.visible'); - WorkflowPage.getters.executeWorkflowButton().should('be.visible'); - }); - - it('should connect and disconnect a simple node', () => { - WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); - WorkflowPage.getters.nodeViewBackground().click(600, 200, { force: true }); - cy.get('.jtk-connector').should('have.length', 1); - WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); - - // Change connection from Set to Set1 - cy.draganddrop( - WorkflowPage.getters.getEndpointSelector('input', SET_NODE_NAME), - WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`), - ); - - WorkflowPage.getters - .canvasNodeInputEndpointByName(`${SET_NODE_NAME}1`) - .should('have.class', 'jtk-endpoint-connected'); - - cy.get('.jtk-connector').should('have.length', 1); - // Disconnect Set1 - cy.drag(WorkflowPage.getters.getEndpointSelector('input', `${SET_NODE_NAME}1`), [-200, 100]); - cy.get('.jtk-connector').should('have.length', 0); - }); - - it('should add first step', () => { - WorkflowPage.getters.canvasPlusButton().should('be.visible'); - WorkflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodes().should('have.length', 1); - }); - - it('should add a node via plus endpoint drag', () => { - WorkflowPage.getters.canvasPlusButton().should('be.visible'); - WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, true); - - cy.drag( - WorkflowPage.getters.getEndpointSelector('plus', SCHEDULE_TRIGGER_NODE_NAME), - [100, 100], - ); - - WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); - WorkflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false); - WorkflowPage.getters.nodeViewBackground().click({ force: true }); - }); it('should add switch node and test connections', () => { WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true); @@ -142,6 +92,8 @@ describe('Canvas Actions', () => { cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 4); WorkflowPage.actions.executeWorkflow(); + WorkflowPage.getters.stopExecutionButton().should('not.exist'); + // If the merged set nodes are connected and executed correctly, there should be 2 items in the output of merge node cy.get('[data-label="2 items"]').should('be.visible'); }); @@ -167,41 +119,6 @@ describe('Canvas Actions', () => { cy.get('.jtk-connector').should('have.length', 4); }); - it('should add a connected node using plus endpoint', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - cy.get('.plus-endpoint').should('be.visible').click(); - WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); - WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME); - WorkflowPage.getters.nodeCreatorSearchBar().type('{enter}'); - cy.get('body').type('{esc}'); - WorkflowPage.getters.canvasNodes().should('have.length', 2); - WorkflowPage.getters.nodeConnections().should('have.length', 1); - }); - - it('should add disconnected node if nothing is selected', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - // Deselect nodes - WorkflowPage.getters.nodeViewBackground().click({ force: true }); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters.canvasNodes().should('have.length', 2); - WorkflowPage.getters.nodeConnections().should('have.length', 0); - }); - - it('should add note between two connected nodes', () => { - WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); - WorkflowPage.actions.zoomToFit(); - WorkflowPage.actions.addNodeBetweenNodes(CODE_NODE_NAME, SET_NODE_NAME, HTTP_REQUEST_NODE_NAME); - WorkflowPage.getters.canvasNodes().should('have.length', 4); - WorkflowPage.getters.nodeConnections().should('have.length', 3); - // And last node should be pushed to the right - WorkflowPage.getters - .canvasNodes() - .last() - .should('have.attr', 'style', 'left: 860px; top: 260px;'); - }); - it('should delete node using node action button', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); @@ -319,50 +236,6 @@ describe('Canvas Actions', () => { WorkflowPage.getters.canvasNodes().last().should('be.visible'); }); - it('should select all nodes', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.actions.selectAll(); - WorkflowPage.getters.selectedNodes().should('have.length', 2); - }); - - it('should select nodes using arrow keys', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.wait(500); - cy.get('body').type('{leftArrow}'); - WorkflowPage.getters.canvasNodes().first().should('have.class', 'jtk-drag-selected'); - cy.get('body').type('{rightArrow}'); - WorkflowPage.getters.canvasNodes().last().should('have.class', 'jtk-drag-selected'); - }); - - it('should select nodes using shift and arrow keys', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.wait(500); - cy.get('body').type('{shift}', { release: false }).type('{leftArrow}'); - WorkflowPage.getters.selectedNodes().should('have.length', 2); - }); - - it('should delete connections by pressing the delete button', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters.nodeConnections().first().realHover(); - cy.get('.connection-actions .delete').first().click({ force: true }); - WorkflowPage.getters.nodeConnections().should('have.length', 0); - }); - - it('should delete a connection by moving it away from endpoint', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.drag(WorkflowPage.getters.getEndpointSelector('input', CODE_NODE_NAME), [0, -100]); - WorkflowPage.getters.nodeConnections().should('have.length', 0); - }); - it('should disable node by pressing the disable button', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); @@ -418,23 +291,4 @@ describe('Canvas Actions', () => { WorkflowPage.getters.canvasNodes().should('have.length', 3); WorkflowPage.getters.nodeConnections().should('have.length', 1); }); - - it('should execute node', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodes() - .last() - .find('[data-test-id="execute-node-button"]') - .click({ force: true }); - WorkflowPage.getters.successToast().should('contain', 'Node executed successfully'); - }); - - it('should copy selected nodes', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.actions.selectAll(); - WorkflowPage.actions.hitCopy(); - WorkflowPage.getters.successToast().should('contain', 'Copied!'); - }); }); diff --git a/cypress/e2e/14-data-transformation-expressions.cy.ts b/cypress/e2e/14-data-transformation-expressions.cy.ts index 161f02fe1fa0f..9e64043fcda3a 100644 --- a/cypress/e2e/14-data-transformation-expressions.cy.ts +++ b/cypress/e2e/14-data-transformation-expressions.cy.ts @@ -11,7 +11,6 @@ describe('Data transformation expressions', () => { beforeEach(() => { wf.actions.visit(); - cy.waitForLoad(); cy.window() // @ts-ignore diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index e281512e09ca0..17d51ebb0ded4 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -16,6 +16,9 @@ import { } from '../constants'; import { randFirstName, randLastName } from '@ngneat/falso'; import { CredentialsPage, CredentialsModal, WorkflowPage, NDV } from '../pages'; +import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json'; +import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json'; +import CustomCredential from '../fixtures/Custom_credential.json'; const email = DEFAULT_USER_EMAIL; const password = DEFAULT_USER_PASSWORD; @@ -31,25 +34,10 @@ const NEW_CREDENTIAL_NAME = 'Something else'; describe('Credentials', () => { before(() => { cy.resetAll(); - cy.setup({ email, firstName, lastName, password }); - - // Always intercept the request to test credentials and return a success - cy.intercept('POST', '/rest/credentials/test', { - statusCode: 200, - body: { - data: { status: 'success', message: 'Tested successfully' }, - } - }); + cy.skipSetup(); }); beforeEach(() => { - cy.on('uncaught:exception', (err, runnable) => { - expect(err.message).to.include('Not logged in'); - - return false; - }); - - cy.signin({ email, password }); cy.visit(credentialsPage.url); }); @@ -250,24 +238,4 @@ describe('Credentials', () => { credentialsModal.actions.fillCredentialsForm(); workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_QUERY_AUTH_ACCOUNT_NAME); }); - - it('should render custom node with n8n credential', () => { - workflowPage.actions.visit(); - workflowPage.actions.addNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('E2E Node with native n8n credential', true, true); - workflowPage.getters.nodeCredentialsLabel().click(); - cy.contains('Create New Credential').click(); - credentialsModal.getters.editCredentialModal().should('be.visible'); - credentialsModal.getters.editCredentialModal().should('contain.text', 'Notion API'); - }); - - it('should render custom node with custom credential', () => { - workflowPage.actions.visit(); - workflowPage.actions.addNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('E2E Node with custom credential', true, true); - workflowPage.getters.nodeCredentialsLabel().click(); - cy.contains('Create New Credential').click(); - credentialsModal.getters.editCredentialModal().should('be.visible'); - credentialsModal.getters.editCredentialModal().should('contain.text', 'Custom E2E Credential'); - }); }); diff --git a/cypress/e2e/21-community-nodes.cy.ts b/cypress/e2e/21-community-nodes.cy.ts new file mode 100644 index 0000000000000..f46a829ac033f --- /dev/null +++ b/cypress/e2e/21-community-nodes.cy.ts @@ -0,0 +1,101 @@ +import { NodeCreator } from '../pages/features/node-creator'; +import CustomNodeFixture from '../fixtures/Custom_node.json'; +import { CredentialsModal, WorkflowPage } from '../pages'; +import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json'; +import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json'; +import CustomCredential from '../fixtures/Custom_credential.json'; + +const credentialsModal = new CredentialsModal(); +const nodeCreatorFeature = new NodeCreator(); +const workflowPage = new WorkflowPage(); + +// We separate-out the custom nodes because they require injecting nodes and credentials +// so the /nodes and /credentials endpoints are intercepted and non-cached. +// We want to keep the other tests as fast as possible so we don't want to break the cache in those. +describe('Community Nodes', () => { + before(() => { + cy.resetAll(); + cy.skipSetup(); + }) + beforeEach(() => { + cy.intercept('/types/nodes.json', { middleware: true }, (req) => { + req.headers['cache-control'] = 'no-cache, no-store'; + + req.on('response', (res) => { + const nodes = res.body || []; + + nodes.push(CustomNodeFixture, CustomNodeWithN8nCredentialFixture, CustomNodeWithCustomCredentialFixture); + }); + }) + + cy.intercept('/types/credentials.json', { middleware: true }, (req) => { + req.headers['cache-control'] = 'no-cache, no-store'; + + req.on('response', (res) => { + const credentials = res.body || []; + + credentials.push(CustomCredential); + }) + }) + workflowPage.actions.visit(); + }); + + it('should render and select community node', () => { + const customNode = 'E2E Node'; + + nodeCreatorFeature.actions.openNodeCreator(); + nodeCreatorFeature.getters.searchBar().find('input').clear().type(customNode); + + nodeCreatorFeature.getters + .getCreatorItem(customNode) + .findChildByTestId('node-creator-item-tooltip') + .should('exist'); + nodeCreatorFeature.actions.selectNode(customNode); + + // TODO: Replace once we have canvas feature utils + cy.get('.data-display .node-name').contains(customNode).should('exist'); + + const nodeParameters = () => cy.getByTestId('node-parameters'); + const firstParameter = () => nodeParameters().find('.parameter-item').eq(0); + const secondParameter = () => nodeParameters().find('.parameter-item').eq(1); + + // Check correct fields are rendered + nodeParameters().should('exist'); + // Test property text input + firstParameter().contains('Test property').should('exist'); + firstParameter().find('input.el-input__inner').should('have.value', 'Some default'); + // Resource select input + secondParameter().find('label').contains('Resource').should('exist'); + secondParameter().find('input.el-input__inner').should('have.value', 'option2'); + secondParameter().find('.el-select').click(); + secondParameter().find('.el-select-dropdown__list').should('exist'); + // Check if all options are rendered and select the fourth one + secondParameter().find('.el-select-dropdown__list').children().should('have.length', 4); + secondParameter() + .find('.el-select-dropdown__list') + .children() + .eq(3) + .contains('option4') + .should('exist') + .click(); + secondParameter().find('input.el-input__inner').should('have.value', 'option4'); + }); + + it('should render custom node with n8n credential', () => { + workflowPage.actions.addNodeToCanvas('Manual'); + workflowPage.actions.addNodeToCanvas('E2E Node with native n8n credential', true, true); + workflowPage.getters.nodeCredentialsLabel().click(); + cy.contains('Create New Credential').click(); + credentialsModal.getters.editCredentialModal().should('be.visible'); + credentialsModal.getters.editCredentialModal().should('contain.text', 'Notion API'); + }); + + it('should render custom node with custom credential', () => { + workflowPage.actions.addNodeToCanvas('Manual'); + workflowPage.actions.addNodeToCanvas('E2E Node with custom credential', true, true); + workflowPage.getters.nodeCredentialsLabel().click(); + cy.contains('Create New Credential').click(); + credentialsModal.getters.editCredentialModal().should('be.visible'); + credentialsModal.getters.editCredentialModal().should('contain.text', 'Custom E2E Credential'); + }); +}); diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts index 014e94aeceaa1..2ef7a41c99d4f 100644 --- a/cypress/e2e/4-node-creator.cy.ts +++ b/cypress/e2e/4-node-creator.cy.ts @@ -6,6 +6,7 @@ const nodeCreatorFeature = new NodeCreator(); const WorkflowPage = new WorkflowPageClass(); const NDVModal = new NDV(); + describe('Node Creator', () => { before(() => { cy.resetAll(); @@ -104,47 +105,6 @@ describe('Node Creator', () => { NDVModal.getters.parameterInput('operation').should('contain.text', 'Rename'); }) - it('should render and select community node', () => { - const customNode = 'E2E Node'; - - nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type(customNode); - - nodeCreatorFeature.getters - .getCreatorItem(customNode) - .findChildByTestId('node-creator-item-tooltip') - .should('exist'); - nodeCreatorFeature.actions.selectNode(customNode); - - // TODO: Replace once we have canvas feature utils - cy.get('.data-display .node-name').contains(customNode).should('exist'); - - const nodeParameters = () => cy.getByTestId('node-parameters'); - const firstParameter = () => nodeParameters().find('.parameter-item').eq(0); - const secondParameter = () => nodeParameters().find('.parameter-item').eq(1); - - // Check correct fields are rendered - nodeParameters().should('exist'); - // Test property text input - firstParameter().contains('Test property').should('exist'); - firstParameter().find('input.el-input__inner').should('have.value', 'Some default'); - // Resource select input - secondParameter().find('label').contains('Resource').should('exist'); - secondParameter().find('input.el-input__inner').should('have.value', 'option2'); - secondParameter().find('.el-select').click(); - secondParameter().find('.el-select-dropdown__list').should('exist'); - // Check if all options are rendered and select the fourth one - secondParameter().find('.el-select-dropdown__list').children().should('have.length', 4); - secondParameter() - .find('.el-select-dropdown__list') - .children() - .eq(3) - .contains('option4') - .should('exist') - .click(); - secondParameter().find('input.el-input__inner').should('have.value', 'option4'); - }); - describe('should correctly append manual trigger for regular actions', () => { // For these sources, manual node should be added const sourcesWithAppend = [ diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index a56ff58622141..cc86e6c239454 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -94,7 +94,6 @@ describe('Workflow Actions', () => { cy.get('.el-message-box').should('be.visible'); cy.get('.el-message-box').find('input').type(IMPORT_WORKFLOW_URL); cy.get('body').type('{enter}'); - cy.waitForLoad(); WorkflowPage.actions.zoomToFit(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.getters.nodeConnections().should('have.length', 1); @@ -104,7 +103,6 @@ describe('Workflow Actions', () => { WorkflowPage.getters .workflowImportInput() .selectFile('cypress/fixtures/Test_workflow-actions_paste-data.json', { force: true }); - cy.waitForLoad(); WorkflowPage.actions.zoomToFit(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.getters.nodeConnections().should('have.length', 1); diff --git a/cypress/pages/features/node-creator.ts b/cypress/pages/features/node-creator.ts index 0899b188d5735..8ebe6db702d6e 100644 --- a/cypress/pages/features/node-creator.ts +++ b/cypress/pages/features/node-creator.ts @@ -24,7 +24,6 @@ export class NodeCreator extends BasePage { }; actions = { openNodeCreator: () => { - cy.waitForLoad(); this.getters.plusButton().click(); this.getters.nodeCreator().should('be.visible'); }, diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 42b9f73c3d66e..692abb2664f4c 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -53,6 +53,10 @@ Cypress.Commands.add( ); Cypress.Commands.add('waitForLoad', () => { + // These aliases are set-up before each test in cypress/support/e2e.ts + // we can't set them up here because at this point it would be too late + // and the requests would already have been made + cy.wait(['@loadSettings', '@loadLogin']) cy.getByTestId('node-view-loader', { timeout: 20000 }).should('not.exist'); cy.get('.el-loading-mask', { timeout: 20000 }).should('not.exist'); }); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index 21236be4f117c..ee534b4dc59fd 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -14,28 +14,17 @@ // *********************************************************** import './commands'; -import CustomNodeFixture from '../fixtures/Custom_node.json'; -import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json'; -import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json'; -import CustomCredential from '../fixtures/Custom_credential.json'; // Load custom nodes and credentials fixtures beforeEach(() => { - cy.intercept('GET', '/types/nodes.json', (req) => { - req.on('response', (res) => { - const nodes = res.body || []; + cy.intercept('GET', '/rest/settings').as('loadSettings'); + cy.intercept('GET', '/rest/login').as('loadLogin'); - res.headers['cache-control'] = 'no-cache, no-store'; - nodes.push(CustomNodeFixture, CustomNodeWithN8nCredentialFixture, CustomNodeWithCustomCredentialFixture); - }); - }).as('nodesIntercept'); - - cy.intercept('GET', '/types/credentials.json', (req) => { - req.on('response', (res) => { - const credentials = res.body || []; - - res.headers['cache-control'] = 'no-cache, no-store'; - credentials.push(CustomCredential); - }) - }).as('credentialsIntercept'); + // Always intercept the request to test credentials and return a success + cy.intercept('POST', '/rest/credentials/test', { + statusCode: 200, + body: { + data: { status: 'success', message: 'Tested successfully' }, + } + }); })