mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
test(editor): Add e2e tests for executions preview (#5458)
* ✅ Added initial tests for executions preview * 🔥 Removing unneeded actions * 👌 Renaming test suite, moving mock executions logic to util function
This commit is contained in:
committed by
GitHub
parent
856238721a
commit
3b9eec77ec
40
cypress/e2e/20-workflow-executions.cy.ts
Normal file
40
cypress/e2e/20-workflow-executions.cy.ts
Normal file
@@ -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();
|
||||||
|
}
|
||||||
69
cypress/fixtures/Test_workflow_4_executions_view.json
Normal file
69
cypress/fixtures/Test_workflow_4_executions_view.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
cypress/pages/workflow-executions-tab.ts
Normal file
40
cypress/pages/workflow-executions-tab.ts
Normal file
@@ -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<count; i++) {
|
||||||
|
workflowPage.actions.executeWorkflow();
|
||||||
|
cy.wait(300);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
switchToExecutionsTab: () => {
|
||||||
|
this.getters.executionsTabButton().click();
|
||||||
|
},
|
||||||
|
switchToEditorTab: () => {
|
||||||
|
workflowPage.getters.editorTabButton().click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -103,6 +103,7 @@ export class WorkflowPage extends BasePage {
|
|||||||
cy.get(
|
cy.get(
|
||||||
`.connection-actions[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
`.connection-actions[data-source-node="${sourceNodeName}"][data-target-node="${targetNodeName}"]`,
|
||||||
),
|
),
|
||||||
|
editorTabButton: () => cy.getByTestId('radio-button-workflow'),
|
||||||
};
|
};
|
||||||
actions = {
|
actions = {
|
||||||
visit: () => {
|
visit: () => {
|
||||||
@@ -230,5 +231,15 @@ export class WorkflowPage extends BasePage {
|
|||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.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');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
[$style[size]]: true,
|
[$style[size]]: true,
|
||||||
[$style.disabled]: disabled,
|
[$style.disabled]: disabled,
|
||||||
}"
|
}"
|
||||||
|
:data-test-id="`radio-button-${value}`"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
name: VIEWS.EXECUTION_PREVIEW,
|
name: VIEWS.EXECUTION_PREVIEW,
|
||||||
params: { workflowId: currentWorkflow, executionId: execution.id },
|
params: { workflowId: currentWorkflow, executionId: execution.id },
|
||||||
}"
|
}"
|
||||||
|
:data-test-execution-status="executionUIDetails.name"
|
||||||
>
|
>
|
||||||
<div :class="$style.description">
|
<div :class="$style.description">
|
||||||
<n8n-text color="text-dark" :bold="true" size="medium">{{
|
<n8n-text color="text-dark" :bold="true" size="medium" data-test-id="execution-time">
|
||||||
executionUIDetails.startTime
|
{{ executionUIDetails.startTime }}
|
||||||
}}</n8n-text>
|
</n8n-text>
|
||||||
<div :class="$style.executionStatus">
|
<div :class="$style.executionStatus">
|
||||||
<n8n-spinner
|
<n8n-spinner
|
||||||
v-if="executionUIDetails.name === 'running'"
|
v-if="executionUIDetails.name === 'running'"
|
||||||
@@ -62,6 +63,7 @@
|
|||||||
:class="[$style.icon, $style.retry]"
|
:class="[$style.icon, $style.retry]"
|
||||||
:items="retryExecutionActions"
|
:items="retryExecutionActions"
|
||||||
activatorIcon="redo"
|
activatorIcon="redo"
|
||||||
|
data-test-id="retry-execution-button"
|
||||||
@select="onRetryMenuItemSelect"
|
@select="onRetryMenuItemSelect"
|
||||||
/>
|
/>
|
||||||
<n8n-tooltip v-if="execution.mode === 'manual'" placement="top">
|
<n8n-tooltip v-if="execution.mode === 'manual'" placement="top">
|
||||||
|
|||||||
@@ -17,9 +17,10 @@
|
|||||||
<div
|
<div
|
||||||
:class="{ [$style.executionDetails]: true, [$style.sidebarCollapsed]: sidebarCollapsed }"
|
:class="{ [$style.executionDetails]: true, [$style.sidebarCollapsed]: sidebarCollapsed }"
|
||||||
v-if="activeExecution"
|
v-if="activeExecution"
|
||||||
|
:data-test-id="`execution-preview-details-${executionId}`"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<n8n-text size="large" color="text-base" :bold="true">{{
|
<n8n-text size="large" color="text-base" :bold="true" data-test-id="execution-time">{{
|
||||||
executionUIDetails.startTime
|
executionUIDetails.startTime
|
||||||
}}</n8n-text
|
}}</n8n-text
|
||||||
><br />
|
><br />
|
||||||
@@ -28,9 +29,13 @@
|
|||||||
size="small"
|
size="small"
|
||||||
:class="[$style.spinner, 'mr-4xs']"
|
:class="[$style.spinner, 'mr-4xs']"
|
||||||
/>
|
/>
|
||||||
<n8n-text size="medium" :class="[$style.status, $style[executionUIDetails.name]]">{{
|
<n8n-text
|
||||||
executionUIDetails.label
|
size="medium"
|
||||||
}}</n8n-text>
|
:class="[$style.status, $style[executionUIDetails.name]]"
|
||||||
|
data-test-id="execution-preview-label"
|
||||||
|
>
|
||||||
|
{{ executionUIDetails.label }}
|
||||||
|
</n8n-text>
|
||||||
<n8n-text v-if="executionUIDetails.name === 'running'" color="text-base" size="medium">
|
<n8n-text v-if="executionUIDetails.name === 'running'" color="text-base" size="medium">
|
||||||
{{
|
{{
|
||||||
$locale.baseText('executionDetails.runningTimeRunning', {
|
$locale.baseText('executionDetails.runningTimeRunning', {
|
||||||
@@ -39,7 +44,12 @@
|
|||||||
}}
|
}}
|
||||||
| ID#{{ activeExecution.id }}
|
| ID#{{ activeExecution.id }}
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
<n8n-text v-else-if="executionUIDetails.name !== 'waiting'" color="text-base" size="medium">
|
<n8n-text
|
||||||
|
v-else-if="executionUIDetails.name !== 'waiting'"
|
||||||
|
color="text-base"
|
||||||
|
size="medium"
|
||||||
|
data-test-id="execution-preview-id"
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
$locale.baseText('executionDetails.runningTimeFinished', {
|
$locale.baseText('executionDetails.runningTimeFinished', {
|
||||||
interpolate: { time: executionUIDetails.runningTime },
|
interpolate: { time: executionUIDetails.runningTime },
|
||||||
@@ -80,6 +90,7 @@
|
|||||||
type="tertiary"
|
type="tertiary"
|
||||||
:title="$locale.baseText('executionsList.retryExecution')"
|
:title="$locale.baseText('executionsList.retryExecution')"
|
||||||
icon="redo"
|
icon="redo"
|
||||||
|
data-test-id="execution-preview-retry-button"
|
||||||
@blur="onRetryButtonBlur"
|
@blur="onRetryButtonBlur"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -99,6 +110,7 @@
|
|||||||
icon="trash"
|
icon="trash"
|
||||||
size="large"
|
size="large"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
|
data-test-id="execution-preview-delete-button"
|
||||||
@click="onDeleteExecution"
|
@click="onDeleteExecution"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="['executions-sidebar', $style.container]" ref="container">
|
<div
|
||||||
|
:class="['executions-sidebar', $style.container]"
|
||||||
|
ref="container"
|
||||||
|
data-test-id="executions-sidebar"
|
||||||
|
>
|
||||||
<div :class="$style.heading">
|
<div :class="$style.heading">
|
||||||
<n8n-heading tag="h2" size="medium" color="text-dark">
|
<n8n-heading tag="h2" size="medium" color="text-dark">
|
||||||
{{ $locale.baseText('generic.executions') }}
|
{{ $locale.baseText('generic.executions') }}
|
||||||
</n8n-heading>
|
</n8n-heading>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.controls">
|
<div :class="$style.controls">
|
||||||
<el-checkbox v-model="autoRefresh" @change="onAutoRefreshToggle">{{
|
<el-checkbox
|
||||||
$locale.baseText('executionsList.autoRefresh')
|
v-model="autoRefresh"
|
||||||
}}</el-checkbox>
|
@change="onAutoRefreshToggle"
|
||||||
|
data-test-id="auto-refresh-checkbox"
|
||||||
|
>
|
||||||
|
{{ $locale.baseText('executionsList.autoRefresh') }}
|
||||||
|
</el-checkbox>
|
||||||
<n8n-popover trigger="click">
|
<n8n-popover trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div :class="$style.filterButton">
|
<div :class="$style.filterButton">
|
||||||
<n8n-button icon="filter" type="tertiary" size="medium" :active="statusFilterApplied">
|
<n8n-button
|
||||||
|
icon="filter"
|
||||||
|
type="tertiary"
|
||||||
|
size="medium"
|
||||||
|
:active="statusFilterApplied"
|
||||||
|
data-test-id="executions-filter-button"
|
||||||
|
>
|
||||||
<n8n-badge v-if="statusFilterApplied" theme="primary" class="mr-4xs">1</n8n-badge>
|
<n8n-badge v-if="statusFilterApplied" theme="primary" class="mr-4xs">1</n8n-badge>
|
||||||
{{ $locale.baseText('executionsList.filters') }}
|
{{ $locale.baseText('executionsList.filters') }}
|
||||||
</n8n-button>
|
</n8n-button>
|
||||||
@@ -33,6 +47,7 @@
|
|||||||
ref="typeInput"
|
ref="typeInput"
|
||||||
:class="$style['type-input']"
|
:class="$style['type-input']"
|
||||||
:placeholder="$locale.baseText('generic.any')"
|
:placeholder="$locale.baseText('generic.any')"
|
||||||
|
data-test-id="execution-status-select"
|
||||||
@change="onFilterChange"
|
@change="onFilterChange"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
@@ -40,6 +55,7 @@
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
|
:data-test-id="`execution-status-${item.id}`"
|
||||||
>
|
>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
@@ -60,7 +76,12 @@
|
|||||||
</n8n-link>
|
</n8n-link>
|
||||||
</n8n-info-tip>
|
</n8n-info-tip>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.executionList" ref="executionList" @scroll="loadMore(20)">
|
<div
|
||||||
|
:class="$style.executionList"
|
||||||
|
ref="executionList"
|
||||||
|
data-test-id="current-executions-list"
|
||||||
|
@scroll="loadMore(20)"
|
||||||
|
>
|
||||||
<div v-if="loading" class="mr-m">
|
<div v-if="loading" class="mr-m">
|
||||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||||
@@ -77,6 +98,7 @@
|
|||||||
:key="execution.id"
|
:key="execution.id"
|
||||||
:execution="execution"
|
:execution="execution"
|
||||||
:ref="`execution-${execution.id}`"
|
:ref="`execution-${execution.id}`"
|
||||||
|
:data-test-id="`execution-details-${execution.id}`"
|
||||||
@refresh="onRefresh"
|
@refresh="onRefresh"
|
||||||
@retryExecution="onRetryExecution"
|
@retryExecution="onRetryExecution"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user