From 593b5d299a05fc17e582ed8cca347e2083c41549 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 16 Feb 2023 11:41:25 +0100 Subject: [PATCH] test(editor): Execution testing from UI perspective (no-changelog) (#5429) * test(editor): Execution testing from UI perspective * test(editor): Execution testing from UI perspective * test(editor): Execution test rename * test(editor): Execution test rename and add stop test * test(editor): Execution test with webhook * test(editor): Execution test with webhook --- cypress/e2e/19-execution.cy.ts | 225 ++++++++++++++++++++++ cypress/fixtures/Manual_wait_set.json | 81 ++++++++ cypress/fixtures/Webhook_wait_set.json | 81 ++++++++ cypress/pages/workflow.ts | 3 + packages/editor-ui/src/views/NodeView.vue | 3 + 5 files changed, 393 insertions(+) create mode 100644 cypress/e2e/19-execution.cy.ts create mode 100644 cypress/fixtures/Manual_wait_set.json create mode 100644 cypress/fixtures/Webhook_wait_set.json diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts new file mode 100644 index 0000000000..37b9561f6f --- /dev/null +++ b/cypress/e2e/19-execution.cy.ts @@ -0,0 +1,225 @@ +import { v4 as uuid } from 'uuid'; +import { NDV, WorkflowPage as WorkflowPageClass, WorkflowsPage } from '../pages'; + +const workflowsPage = new WorkflowsPage(); +const workflowPage = new WorkflowPageClass(); +const ndv = new NDV(); + +describe('Execution',() => { + beforeEach(() => { + cy.resetAll(); + cy.skipSetup(); + cy.visit('/'); + }); + + it('should test manual workflow', () => { + // Import workflow + workflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Manual_wait_set.json', `Manual wait set ${uuid()}`); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Execute the workflow + workflowPage.getters.zoomToFitButton().click(); + workflowPage.getters.executeWorkflowButton().click(); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().get('.n8n-spinner').should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('be.visible'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Check canvas nodes after 1st step (workflow passed the manual trigger node + workflowPage.getters.canvasNodeByName('Manual').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check').should('not.exist')); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + cy.wait(2000); + + // Check canvas nodes after 2nd step (waiting node finished its execution and the http request node is about to start) + workflowPage.getters.canvasNodeByName('Manual').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check')).should('be.visible'); + + // Clear execution data + workflowPage.getters.clearExecutionDataButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().click(); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + + // Check success toast (works because Cypress waits enough for the element to show after the http request node has finished) + workflowPage.getters.successToast().should('be.visible'); + }); + + it('should test manual workflow stop', () => { + // Import workflow + workflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Manual_wait_set.json', `Manual wait set ${uuid()}`); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Execute the workflow + workflowPage.getters.zoomToFitButton().click(); + workflowPage.getters.executeWorkflowButton().click(); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().get('.n8n-spinner').should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('be.visible'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Check canvas nodes after 1st step (workflow passed the manual trigger node + workflowPage.getters.canvasNodeByName('Manual').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check').should('not.exist')); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + + cy.wait(1000); + workflowPage.getters.stopExecutionButton().click(); + + // Check canvas nodes after workflow stopped + workflowPage.getters.canvasNodeByName('Manual').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt').should('not.visible')); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + // Clear execution data + workflowPage.getters.clearExecutionDataButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().click(); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + + // Check success toast (works because Cypress waits enough for the element to show after the http request node has finished) + workflowPage.getters.successToast().should('be.visible'); + }); + + it('should test webhook workflow', () => { + // Import workflow + workflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Webhook_wait_set.json', `Webhook wait set ${uuid()}`); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Execute the workflow + workflowPage.getters.zoomToFitButton().click(); + workflowPage.getters.executeWorkflowButton().click(); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().get('.n8n-spinner').should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('be.visible'); + + workflowPage.getters.canvasNodes().first().dblclick(); + + ndv.getters.copyInput().click(); + + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); + + ndv.getters.backToCanvas().click(); + + cy.readClipboard().then((url) => { + cy.request({ + method: 'GET', + url, + }).then((resp) => { + expect(resp.status).to.eq(200); + }); + }); + + // Check canvas nodes after 1st step (workflow passed the manual trigger node + workflowPage.getters.canvasNodeByName('Webhook').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check').should('not.exist')); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + cy.wait(2000); + + // Check canvas nodes after 2nd step (waiting node finished its execution and the http request node is about to start) + workflowPage.getters.canvasNodeByName('Webhook').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check')).should('be.visible'); + + // Clear execution data + workflowPage.getters.clearExecutionDataButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().click(); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + + // Check success toast (works because Cypress waits enough for the element to show after the http request node has finished) + workflowPage.getters.successToast().should('be.visible'); + }); + + it('should test webhook workflow stop', () => { + // Import workflow + workflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Webhook_wait_set.json', `Webhook wait set ${uuid()}`); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('not.exist'); + + // Execute the workflow + workflowPage.getters.zoomToFitButton().click(); + workflowPage.getters.executeWorkflowButton().click(); + + // Check workflow buttons + workflowPage.getters.executeWorkflowButton().get('.n8n-spinner').should('be.visible'); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + workflowPage.getters.stopExecutionButton().should('not.exist'); + workflowPage.getters.stopExecutionWaitingForWebhookButton().should('be.visible'); + + workflowPage.getters.canvasNodes().first().dblclick(); + + ndv.getters.copyInput().click(); + + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); + + ndv.getters.backToCanvas().click(); + + cy.readClipboard().then((url) => { + cy.request({ + method: 'GET', + url, + }).then((resp) => { + expect(resp.status).to.eq(200); + }); + }); + + // Check canvas nodes after 1st step (workflow passed the manual trigger node + workflowPage.getters.canvasNodeByName('Webhook').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check').should('not.exist')); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + cy.wait(1000); + workflowPage.getters.stopExecutionWaitingForWebhookButton().click(); + + // Check canvas nodes after workflow stopped + workflowPage.getters.canvasNodeByName('Webhook').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-check')).should('be.visible'); + workflowPage.getters.canvasNodeByName('Wait').within(() => cy.get('.fa-sync-alt').should('not.visible')); + workflowPage.getters.canvasNodeByName('Set').within(() => cy.get('.fa-check').should('not.exist')); + + // Clear execution data + workflowPage.getters.clearExecutionDataButton().should('be.visible'); + workflowPage.getters.clearExecutionDataButton().click(); + workflowPage.getters.clearExecutionDataButton().should('not.exist'); + + // Check success toast (works because Cypress waits enough for the element to show after the http request node has finished) + workflowPage.getters.successToast().should('be.visible'); + }); +}); diff --git a/cypress/fixtures/Manual_wait_set.json b/cypress/fixtures/Manual_wait_set.json new file mode 100644 index 0000000000..b05a39ee2b --- /dev/null +++ b/cypress/fixtures/Manual_wait_set.json @@ -0,0 +1,81 @@ +{ + "name": "Manual wait set", + "nodes": [ + { + "parameters": { + "amount": 2, + "unit": "seconds" + }, + "id": "ed6e0168-1145-43d0-9082-970b8a8f3cb5", + "name": "Wait", + "type": "n8n-nodes-base.wait", + "typeVersion": 1, + "position": [ + 900, + 580 + ], + "webhookId": "0f6f94a4-c28d-46f9-8468-6ab315a9fec9" + }, + { + "parameters": {}, + "id": "59467b99-4e7c-4f19-8fc2-4329788f0951", + "name": "Manual", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 680, + 580 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "6ddf089f-4d01-4691-928f-6de168e3b089", + "name": "Set", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [ + 1120, + 580 + ] + } + ], + "pinData": {}, + "connections": { + "Wait": { + "main": [ + [ + { + "node": "Set", + "type": "main", + "index": 0 + } + ] + ] + }, + "Manual": { + "main": [ + [ + { + "node": "Wait", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "saveExecutionProgress": true, + "saveManualExecutions": true, + "callerPolicy": "workflowsFromSameOwner" + }, + "versionId": "f11ff1bf-4273-46cb-bbec-65c7b2fa13cb", + "id": "1037", + "meta": { + "instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7" + }, + "tags": [] +} diff --git a/cypress/fixtures/Webhook_wait_set.json b/cypress/fixtures/Webhook_wait_set.json new file mode 100644 index 0000000000..64ef060fdf --- /dev/null +++ b/cypress/fixtures/Webhook_wait_set.json @@ -0,0 +1,81 @@ +{ + "name": "Webhook wait set", + "nodes": [ + { + "parameters": { + "path": "23fc3930-b8f9-41d9-89db-b647291a2201", + "options": {} + }, + "id": "70d84fe7-e221-4978-a15e-3984f2df645f", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 1, + "position": [ + 500, + 580 + ], + "webhookId": "23fc3930-b8f9-41d9-89db-b647291a2201" + }, + { + "parameters": { + "amount": 2, + "unit": "seconds" + }, + "id": "7f15f650-99bc-400b-8db8-67be53003fa3", + "name": "Wait", + "type": "n8n-nodes-base.wait", + "typeVersion": 1, + "position": [ + 720, + 580 + ], + "webhookId": "18a12605-1fbd-49da-854e-268ab6db1ea3" + }, + { + "parameters": { + "options": {} + }, + "id": "5bdafae4-e297-463f-991f-b8ea14983026", + "name": "Set", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [ + 940, + 580 + ] + } + ], + "pinData": {}, + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "Wait", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait": { + "main": [ + [ + { + "node": "Set", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": {}, + "versionId": "0c67e557-1faf-446e-881e-3245228c627e", + "id": "1038", + "meta": { + "instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7" + }, + "tags": [] +} diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index f09bda9f1f..bf150e038a 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -90,6 +90,9 @@ export class WorkflowPage extends BasePage { zoomOutButton: () => cy.getByTestId('zoom-out-button'), resetZoomButton: () => cy.getByTestId('reset-zoom-button'), executeWorkflowButton: () => cy.getByTestId('execute-workflow-button'), + clearExecutionDataButton: () => cy.getByTestId('clear-execution-data-button'), + stopExecutionButton: () => cy.getByTestId('stop-execution-button'), + stopExecutionWaitingForWebhookButton: () => cy.getByTestId('stop-execution-waiting-for-webhook-button'), nodeCredentialsSelect: () => cy.getByTestId('node-credentials-select'), nodeCredentialsEditButton: () => cy.getByTestId('credential-edit-button'), nodeCreatorItems: () => cy.getByTestId('item-iterator-item'), diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 700fc07699..84a3c005e7 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -133,6 +133,7 @@ " :loading="stopExecutionInProgress" @click.stop="stopExecution" + data-test-id="stop-execution-button" />