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"
/>