From 3b9eec77ecf25c765a349bf174e7d42a545109c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milorad=20FIlipovi=C4=87?= Date: Tue, 14 Feb 2023 11:39:19 +0100 Subject: [PATCH] test(editor): Add e2e tests for executions preview (#5458) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✅ Added initial tests for executions preview * 🔥 Removing unneeded actions * 👌 Renaming test suite, moving mock executions logic to util function --- cypress/e2e/20-workflow-executions.cy.ts | 40 +++++++++++ .../Test_workflow_4_executions_view.json | 69 +++++++++++++++++++ cypress/pages/workflow-executions-tab.ts | 40 +++++++++++ cypress/pages/workflow.ts | 11 +++ .../N8nRadioButtons/RadioButton.vue | 1 + .../ExecutionsView/ExecutionCard.vue | 8 ++- .../ExecutionsView/ExecutionPreview.vue | 22 ++++-- .../ExecutionsView/ExecutionsSidebar.vue | 34 +++++++-- 8 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 cypress/e2e/20-workflow-executions.cy.ts create mode 100644 cypress/fixtures/Test_workflow_4_executions_view.json create mode 100644 cypress/pages/workflow-executions-tab.ts diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts new file mode 100644 index 0000000000..bd7dc0a210 --- /dev/null +++ b/cypress/e2e/20-workflow-executions.cy.ts @@ -0,0 +1,40 @@ +import { WorkflowPage } from "../pages"; +import { WorkflowExecutionsTab } from "../pages/workflow-executions-tab"; + +const workflowPage = new WorkflowPage(); +const executionsTab = new WorkflowExecutionsTab(); + +// Test suite for executions tab +describe('Current Workflow Executions', () => { + before(() => { + cy.resetAll(); + cy.skipSetup(); + workflowPage.actions.visit(); + cy.waitForLoad(); + cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', `My test workflow`); + createMockExecutions(); + }); + + it('should render executions tab correctly', () => { + cy.waitForLoad(); + executionsTab.getters.executionListItems().should('have.length', 11); + executionsTab.getters.successfulExecutionListItems().should('have.length', 9); + executionsTab.getters.failedExecutionListItems().should('have.length', 2); + executionsTab.getters.executionListItems().first().invoke('attr','class').should('match', /_active_/); + }); + +}); + + +const createMockExecutions = () => { + workflowPage.actions.turnOnManualExecutionSaving(); + executionsTab.actions.createManualExecutions(5); + // Make some failed executions by enabling Code node with syntax error + executionsTab.actions.toggleNodeEnabled('Error'); + executionsTab.actions.createManualExecutions(2); + // Then add some more successful ones + executionsTab.actions.toggleNodeEnabled('Error'); + executionsTab.actions.createManualExecutions(4); + executionsTab.actions.switchToExecutionsTab(); + cy.waitForLoad(); +} diff --git a/cypress/fixtures/Test_workflow_4_executions_view.json b/cypress/fixtures/Test_workflow_4_executions_view.json new file mode 100644 index 0000000000..a0be9eae35 --- /dev/null +++ b/cypress/fixtures/Test_workflow_4_executions_view.json @@ -0,0 +1,69 @@ +{ + "meta": { + "instanceId": "6b85439d79c07750ea49eced4bc2a12b283cfcba0ab2917cd4f3fee36080e869" + }, + "nodes": [ + { + "parameters": { + "jsCode": "// Loop over input items and add a new field\n// called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n error\n}\n\nreturn $input.all();" + }, + "id": "d0ab7e12-0e1b-4c08-8081-83107794f37d", + "name": "Error", + "type": "n8n-nodes-base.code", + "typeVersion": 1, + "position": [ + 680, + 460 + ], + "disabled": true + }, + { + "parameters": {}, + "id": "f5026145-66c1-463c-8ac8-46a1309a6632", + "name": "On clicking 'execute'", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 460, + 460 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field\n// called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "9926f884-348a-4af0-872e-dd7c8b3da811", + "name": "Code", + "type": "n8n-nodes-base.code", + "typeVersion": 1, + "position": [ + 900, + 460 + ] + } + ], + "connections": { + "Error": { + "main": [ + [ + { + "node": "Code", + "type": "main", + "index": 0 + } + ] + ] + }, + "On clicking 'execute'": { + "main": [ + [ + { + "node": "Error", + "type": "main", + "index": 0 + } + ] + ] + } + } +} diff --git a/cypress/pages/workflow-executions-tab.ts b/cypress/pages/workflow-executions-tab.ts new file mode 100644 index 0000000000..ea6c8b4fd5 --- /dev/null +++ b/cypress/pages/workflow-executions-tab.ts @@ -0,0 +1,40 @@ +import { BasePage } from "./base"; +import { WorkflowPage } from "./workflow"; + +const workflowPage = new WorkflowPage(); + +export class WorkflowExecutionsTab extends BasePage { + getters = { + executionsTabButton: () => cy.getByTestId('radio-button-executions'), + executionsSidebar: () => cy.getByTestId('executions-sidebar'), + autoRefreshCheckBox: () => cy.getByTestId('auto-refresh-checkbox'), + executionsList: () => cy.getByTestId('current-executions-list'), + executionListItems: () => this.getters.executionsList().find('div.execution-card'), + successfulExecutionListItems: () => cy.get('[data-test-execution-status="success"]'), + failedExecutionListItems: () => cy.get('[data-test-execution-status="error"]'), + executionCard: (executionId: string) => cy.getByTestId(`execution-details-${executionId}`), + executionPreviewDetails: () => cy.get('[data-test-id^="execution-preview-details-"]'), + executionPreviewDetailsById: (executionId: string) => cy.getByTestId(`execution-preview-details-${executionId}`), + executionPreviewTime: () => this.getters.executionPreviewDetails().find('[data-test-id="execution-time"]'), + executionPreviewStatus: () => this.getters.executionPreviewDetails().find('[data-test-id="execution-preview-label"]'), + executionPreviewId: () => this.getters.executionPreviewDetails().find('[data-test-id="execution-preview-id"]'), + }; + actions = { + toggleNodeEnabled: (nodeName: string) => { + workflowPage.getters.canvasNodeByName(nodeName).click(); + cy.get('body').type('d', { force: true }); + }, + createManualExecutions: (count: number) => { + for (let i=0; i { + this.getters.executionsTabButton().click(); + }, + switchToEditorTab: () => { + workflowPage.getters.editorTabButton().click(); + } + }; +}; diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index 8a6a78c3a7..d895ccfab4 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -103,6 +103,7 @@ export class WorkflowPage extends BasePage { cy.get( `.connection-actions[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`, ), + editorTabButton: () => cy.getByTestId('radio-button-workflow'), }; actions = { visit: () => { @@ -230,5 +231,15 @@ export class WorkflowPage extends BasePage { .first() .click({ force: true }); }, + turnOnManualExecutionSaving: () => { + this.getters.workflowMenu().click(); + this.getters.workflowMenuItemSettings().click(); + this.getters + .workflowSettingsSaveManualExecutionsSelect() + .find('li:contains("Yes")') + .click({ force: true }); + this.getters.workflowSettingsSaveButton().click(); + this.getters.successToast().should('exist'); + }, }; } diff --git a/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue b/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue index 4d35e6b110..7f729df7cb 100644 --- a/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue +++ b/packages/design-system/src/components/N8nRadioButtons/RadioButton.vue @@ -17,6 +17,7 @@ [$style[size]]: true, [$style.disabled]: disabled, }" + :data-test-id="`radio-button-${value}`" @click="$emit('click')" > {{ label }} diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionCard.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionCard.vue index 45cacf47a7..aaa602f0ca 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionCard.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionCard.vue @@ -14,11 +14,12 @@ name: VIEWS.EXECUTION_PREVIEW, params: { workflowId: currentWorkflow, executionId: execution.id }, }" + :data-test-execution-status="executionUIDetails.name" >
- {{ - executionUIDetails.startTime - }} + + {{ executionUIDetails.startTime }} +
diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionPreview.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionPreview.vue index 4d2c118c1b..43e727b1dd 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionPreview.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionPreview.vue @@ -17,9 +17,10 @@
- {{ + {{ executionUIDetails.startTime }}
@@ -28,9 +29,13 @@ size="small" :class="[$style.spinner, 'mr-4xs']" /> - {{ - executionUIDetails.label - }} + + {{ executionUIDetails.label }} + {{ $locale.baseText('executionDetails.runningTimeRunning', { @@ -39,7 +44,12 @@ }} | ID#{{ activeExecution.id }} - + {{ $locale.baseText('executionDetails.runningTimeFinished', { interpolate: { time: executionUIDetails.runningTime }, @@ -80,6 +90,7 @@ type="tertiary" :title="$locale.baseText('executionsList.retryExecution')" icon="redo" + data-test-id="execution-preview-retry-button" @blur="onRetryButtonBlur" /> @@ -99,6 +110,7 @@ icon="trash" size="large" type="tertiary" + data-test-id="execution-preview-delete-button" @click="onDeleteExecution" />
diff --git a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue index e1664e0026..69c190e0f1 100644 --- a/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue +++ b/packages/editor-ui/src/components/ExecutionsView/ExecutionsSidebar.vue @@ -1,18 +1,32 @@