mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
add e2e tests
This commit is contained in:
parent
5f491fbde3
commit
40fd8b318b
29
cypress/composables/executions.ts
Normal file
29
cypress/composables/executions.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export const getExecutionsSidebar = () => cy.getByTestId('executions-sidebar');
|
||||
|
||||
export const getWorkflowExecutionPreviewIframe = () => cy.getByTestId('workflow-preview-iframe');
|
||||
|
||||
export const getExecutionPreviewBody = () =>
|
||||
getWorkflowExecutionPreviewIframe()
|
||||
.its('0.contentDocument.body')
|
||||
.then((el) => cy.wrap(el));
|
||||
|
||||
export const getExecutionPreviewBodyNodes = () =>
|
||||
getExecutionPreviewBody().findChildByTestId('canvas-node');
|
||||
|
||||
export const getExecutionPreviewBodyNodesByName = (name: string) =>
|
||||
getExecutionPreviewBody().findChildByTestId('canvas-node').filter(`[data-name="${name}"]`).eq(0);
|
||||
|
||||
export function getExecutionPreviewOutputPanelRelatedExecutionLink() {
|
||||
return getExecutionPreviewBody().findChildByTestId('related-execution-link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export const openExecutionPreviewNode = (name: string) =>
|
||||
getExecutionPreviewBodyNodesByName(name).dblclick();
|
|
@ -48,10 +48,38 @@ export function getOutputTableRow(row: number) {
|
|||
return getOutputTableRows().eq(row);
|
||||
}
|
||||
|
||||
export function getOutputTableHeaders() {
|
||||
return getOutputPanelDataContainer().find('table thead th');
|
||||
}
|
||||
|
||||
export function getOutputTableHeaderByText(text: string) {
|
||||
return getOutputTableHeaders().contains(text);
|
||||
}
|
||||
|
||||
export function getOutputTbodyCell(row: number, col: number) {
|
||||
return getOutputTableRows().eq(row).find('td').eq(col);
|
||||
}
|
||||
|
||||
export function getOutputRunSelector() {
|
||||
return getOutputPanel().findChildByTestId('run-selector');
|
||||
}
|
||||
|
||||
export function getOutputRunSelectorInput() {
|
||||
return getOutputRunSelector().find('input');
|
||||
}
|
||||
|
||||
export function getOutputPanelTable() {
|
||||
return getOutputPanelDataContainer().get('table');
|
||||
}
|
||||
|
||||
export function getOutputPanelItemsCount() {
|
||||
return getOutputPanel().getByTestId('ndv-items-count');
|
||||
}
|
||||
|
||||
export function getOutputPanelRelatedExecutionLink() {
|
||||
return getOutputPanel().getByTestId('related-execution-link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
@ -90,3 +118,8 @@ export function setParameterSelectByContent(name: string, content: string) {
|
|||
getParameterInputByName(name).realClick();
|
||||
getVisibleSelect().find('.option-headline').contains(content).click();
|
||||
}
|
||||
|
||||
export function changeOutputRunSelector(runName: string) {
|
||||
getOutputRunSelector().click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
|
||||
}
|
||||
|
|
|
@ -76,6 +76,14 @@ export function getCanvasNodes() {
|
|||
);
|
||||
}
|
||||
|
||||
export function getSaveButton() {
|
||||
return cy.getByTestId('workflow-save-button');
|
||||
}
|
||||
|
||||
export function getZoomToFitButton() {
|
||||
return cy.getByTestId('zoom-to-fit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
@ -170,3 +178,19 @@ export function clickManualChatButton() {
|
|||
export function openNode(nodeName: string) {
|
||||
getNodeByName(nodeName).dblclick();
|
||||
}
|
||||
|
||||
export function saveWorkflowOnButtonClick() {
|
||||
cy.intercept('POST', '/rest/workflows').as('createWorkflow');
|
||||
getSaveButton().should('contain', 'Save');
|
||||
getSaveButton().click();
|
||||
getSaveButton().should('contain', 'Saved');
|
||||
cy.url().should('not.have.string', '/new');
|
||||
}
|
||||
|
||||
export function pasteWorkflow(workflow: object) {
|
||||
cy.get('body').paste(JSON.stringify(workflow));
|
||||
}
|
||||
|
||||
export function clickZoomToFit() {
|
||||
getZoomToFitButton().click();
|
||||
}
|
||||
|
|
140
cypress/e2e/47-subworkflow-debugging.cy.ts
Normal file
140
cypress/e2e/47-subworkflow-debugging.cy.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
import {
|
||||
getExecutionPreviewOutputPanelRelatedExecutionLink,
|
||||
getExecutionsSidebar,
|
||||
getWorkflowExecutionPreviewIframe,
|
||||
openExecutionPreviewNode,
|
||||
} from '../composables/executions';
|
||||
import {
|
||||
changeOutputRunSelector,
|
||||
getOutputPanelItemsCount,
|
||||
getOutputPanelRelatedExecutionLink,
|
||||
getOutputRunSelectorInput,
|
||||
getOutputTableHeaders,
|
||||
getOutputTableRows,
|
||||
getOutputTbodyCell,
|
||||
} from '../composables/ndv';
|
||||
import {
|
||||
clickExecuteWorkflowButton,
|
||||
clickZoomToFit,
|
||||
getCanvasNodes,
|
||||
navigateToNewWorkflowPage,
|
||||
openNode,
|
||||
pasteWorkflow,
|
||||
saveWorkflowOnButtonClick,
|
||||
} from '../composables/workflow';
|
||||
import SUBWORKFLOW_DEBUGGING_EXAMPLE from '../fixtures/Subworkflow-debugging-execute-workflow.json';
|
||||
|
||||
describe('Subworkflow debugging', () => {
|
||||
beforeEach(() => {
|
||||
navigateToNewWorkflowPage();
|
||||
pasteWorkflow(SUBWORKFLOW_DEBUGGING_EXAMPLE);
|
||||
saveWorkflowOnButtonClick();
|
||||
getCanvasNodes().should('have.length', 11);
|
||||
clickZoomToFit();
|
||||
|
||||
clickExecuteWorkflowButton();
|
||||
});
|
||||
|
||||
describe('can inspect sub executed workflow', () => {
|
||||
it('(Run once with all items/ Wait for Sub-workflow completion) (default behavior)', () => {
|
||||
openNode('Execute Workflow with param');
|
||||
|
||||
getOutputPanelItemsCount().should('contain.text', '2 items, 1 sub-execution');
|
||||
getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
|
||||
getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
|
||||
|
||||
// ensure workflow executed and waited on output
|
||||
getOutputTableHeaders().should('have.length', 2);
|
||||
getOutputTbodyCell(1, 0).should('have.text', 'world Natalie Moore');
|
||||
});
|
||||
|
||||
it('(Run once for each item/ Wait for Sub-workflow completion)', () => {
|
||||
openNode('Execute Workflow with param1');
|
||||
|
||||
getOutputPanelItemsCount().should('contain.text', '2 items, 2 sub-execution');
|
||||
getOutputPanelRelatedExecutionLink().should('not.exist');
|
||||
|
||||
// ensure workflow executed and waited on output
|
||||
getOutputTableHeaders().should('have.length', 3);
|
||||
getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
|
||||
getOutputTbodyCell(1, 1).should('have.text', 'world Natalie Moore');
|
||||
});
|
||||
|
||||
it('(Run once with all items/ Wait for Sub-workflow completion)', () => {
|
||||
openNode('Execute Workflow with param2');
|
||||
|
||||
getOutputPanelItemsCount().should('not.exist');
|
||||
getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
|
||||
getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
|
||||
|
||||
// ensure workflow executed but returned same data as input
|
||||
getOutputRunSelectorInput().should('have.value', '2 of 2 (3 items, 1 sub-execution)');
|
||||
getOutputTableHeaders().should('have.length', 6);
|
||||
getOutputTableHeaders().eq(0).should('have.text', 'uid');
|
||||
getOutputTableRows().should('have.length', 4);
|
||||
getOutputTbodyCell(1, 1).should('include.text', 'Jon_Ebert@yahoo.com');
|
||||
|
||||
changeOutputRunSelector('1 of 2 (2 items, 1 sub-execution)');
|
||||
getOutputRunSelectorInput().should('have.value', '1 of 2 (2 items, 1 sub-execution)');
|
||||
getOutputTableHeaders().should('have.length', 6);
|
||||
getOutputTableHeaders().eq(0).should('have.text', 'uid');
|
||||
getOutputTableRows().should('have.length', 3);
|
||||
getOutputTbodyCell(1, 1).should('include.text', 'Terry.Dach@hotmail.com');
|
||||
});
|
||||
|
||||
it('(Run once for each item/ Wait for Sub-workflow completion)', () => {
|
||||
openNode('Execute Workflow with param3');
|
||||
|
||||
// ensure workflow executed but returned same data as input
|
||||
getOutputRunSelectorInput().should('have.value', '2 of 2 (3 items, 3 sub-executions)');
|
||||
getOutputTableHeaders().should('have.length', 7);
|
||||
getOutputTableHeaders().eq(1).should('have.text', 'uid');
|
||||
getOutputTableRows().should('have.length', 4);
|
||||
getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
|
||||
getOutputTbodyCell(1, 2).should('include.text', 'Jon_Ebert@yahoo.com');
|
||||
|
||||
changeOutputRunSelector('1 of 2 (2 items, 2 sub-executions)');
|
||||
getOutputRunSelectorInput().should('have.value', '1 of 2 (2 items, 2 sub-executions)');
|
||||
getOutputTableHeaders().should('have.length', 7);
|
||||
getOutputTableHeaders().eq(1).should('have.text', 'uid');
|
||||
getOutputTableRows().should('have.length', 3);
|
||||
|
||||
getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
|
||||
getOutputTbodyCell(1, 2).should('include.text', 'Terry.Dach@hotmail.com');
|
||||
});
|
||||
});
|
||||
|
||||
it('can inspect parent executions', () => {
|
||||
cy.url().then((workflowUrl) => {
|
||||
openNode('Execute Workflow with param');
|
||||
|
||||
getOutputPanelItemsCount().should('contain.text', '2 items, 1 sub-execution');
|
||||
getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
|
||||
getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
|
||||
|
||||
// ensure workflow executed and waited on output
|
||||
getOutputTableHeaders().should('have.length', 2);
|
||||
getOutputTbodyCell(1, 0).should('have.text', 'world Natalie Moore');
|
||||
|
||||
// cypress cannot handle new tabs so removing it
|
||||
getOutputPanelRelatedExecutionLink().invoke('removeAttr', 'target').click();
|
||||
|
||||
getExecutionsSidebar().should('be.visible');
|
||||
getWorkflowExecutionPreviewIframe().should('be.visible');
|
||||
openExecutionPreviewNode('Execute Workflow Trigger');
|
||||
|
||||
getExecutionPreviewOutputPanelRelatedExecutionLink().should(
|
||||
'include.text',
|
||||
'Inspect Parent Execution',
|
||||
);
|
||||
|
||||
getExecutionPreviewOutputPanelRelatedExecutionLink()
|
||||
.invoke('removeAttr', 'target')
|
||||
.click({ force: true });
|
||||
|
||||
cy.url().then((currentUrl) => {
|
||||
expect(currentUrl === workflowUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
354
cypress/fixtures/Subworkflow-debugging-execute-workflow.json
Normal file
354
cypress/fixtures/Subworkflow-debugging-execute-workflow.json
Normal file
|
@ -0,0 +1,354 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "08ce71ad998aeaade0abedb8dd96153d8eaa03fcb84cfccc1530095bf9ee478e"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4535ce3e-280e-49b0-8854-373472ec86d1",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 860]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"category": "randomData",
|
||||
"randomDataSeed": "0",
|
||||
"randomDataCount": 2
|
||||
},
|
||||
"id": "d7fba18a-d51f-4509-af45-68cd9425ac6b",
|
||||
"name": "DebugHelper1",
|
||||
"type": "n8n-nodes-base.debugHelper",
|
||||
"typeVersion": 1,
|
||||
"position": [280, 860]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"source": "parameter",
|
||||
"workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
|
||||
"mode": "each",
|
||||
"options": {
|
||||
"waitForSubWorkflow": false
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [680, 1540],
|
||||
"id": "f90a25da-dd89-4bf8-8f5b-bf8ee1de0b70",
|
||||
"name": "Execute Workflow with param3"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c93f26bd-3489-467b-909e-6462e1463707",
|
||||
"name": "uid",
|
||||
"value": "={{ $json.uid }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "3dd706ce-d925-4219-8531-ad12369972fe",
|
||||
"name": "email",
|
||||
"value": "={{ $json.email }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [900, 1540],
|
||||
"id": "3be57648-3be8-4b0f-abfa-8fdcafee804d",
|
||||
"name": "Edit Fields8"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"source": "parameter",
|
||||
"workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
|
||||
"options": {
|
||||
"waitForSubWorkflow": false
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [620, 1220],
|
||||
"id": "dabc2356-3660-4d17-b305-936a002029ba",
|
||||
"name": "Execute Workflow with param2"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c93f26bd-3489-467b-909e-6462e1463707",
|
||||
"name": "uid",
|
||||
"value": "={{ $json.uid }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "3dd706ce-d925-4219-8531-ad12369972fe",
|
||||
"name": "email",
|
||||
"value": "={{ $json.email }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [840, 1220],
|
||||
"id": "9d2a9dda-e2a1-43e8-a66f-a8a555692e5f",
|
||||
"name": "Edit Fields7"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"source": "parameter",
|
||||
"workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
|
||||
"mode": "each",
|
||||
"options": {
|
||||
"waitForSubWorkflow": true
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [560, 900],
|
||||
"id": "07e47f60-622a-484c-ab24-35f6f2280595",
|
||||
"name": "Execute Workflow with param1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c93f26bd-3489-467b-909e-6462e1463707",
|
||||
"name": "uid",
|
||||
"value": "={{ $json.uid }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "3dd706ce-d925-4219-8531-ad12369972fe",
|
||||
"name": "email",
|
||||
"value": "={{ $json.email }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [760, 900],
|
||||
"id": "80563d0a-0bab-444f-a04c-4041a505d78b",
|
||||
"name": "Edit Fields6"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"source": "parameter",
|
||||
"workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
|
||||
"options": {
|
||||
"waitForSubWorkflow": true
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [560, 580],
|
||||
"id": "f04af481-f4d9-4d91-a60a-a377580e8393",
|
||||
"name": "Execute Workflow with param"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c93f26bd-3489-467b-909e-6462e1463707",
|
||||
"name": "uid",
|
||||
"value": "={{ $json.uid }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "3dd706ce-d925-4219-8531-ad12369972fe",
|
||||
"name": "email",
|
||||
"value": "={{ $json.email }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [760, 580],
|
||||
"id": "80c10607-a0ac-4090-86a1-890da0a2aa52",
|
||||
"name": "Edit Fields2"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Execute Workflow (Run once with all items/ DONT Wait for Sub-workflow completion)",
|
||||
"height": 254.84308966329985,
|
||||
"width": 457.58120569815793
|
||||
},
|
||||
"id": "534ef523-3453-4a16-9ff0-8ac9f025d47d",
|
||||
"name": "Sticky Note5",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [500, 1080]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Execute Workflow (Run once with for each item/ DONT Wait for Sub-workflow completion) ",
|
||||
"height": 284.59778445962905,
|
||||
"width": 457.58120569815793
|
||||
},
|
||||
"id": "838f0fa3-5ee4-4d1a-afb8-42e009f1aa9e",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [580, 1400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"category": "randomData",
|
||||
"randomDataSeed": "1",
|
||||
"randomDataCount": 3
|
||||
},
|
||||
"id": "86699a49-2aa7-488e-8ea9-828404c98f08",
|
||||
"name": "DebugHelper",
|
||||
"type": "n8n-nodes-base.debugHelper",
|
||||
"typeVersion": 1,
|
||||
"position": [320, 1120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Execute Workflow (Run once with for each item/ Wait for Sub-workflow completion) ",
|
||||
"height": 284.59778445962905,
|
||||
"width": 457.58120569815793
|
||||
},
|
||||
"id": "885d35f0-8ae6-45ec-821b-a82c27e7577a",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [480, 760]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Execute Workflow (Run once with all items/ Wait for Sub-workflow completion) (default behavior)",
|
||||
"height": 254.84308966329985,
|
||||
"width": 457.58120569815793
|
||||
},
|
||||
"id": "505bd7f2-767e-41b8-9325-77300aed5883",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [460, 460]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "DebugHelper1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "DebugHelper",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"DebugHelper1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Execute Workflow with param3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Execute Workflow with param2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Execute Workflow with param1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Execute Workflow with param",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Execute Workflow with param3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Execute Workflow with param2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Execute Workflow with param1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Execute Workflow with param": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"DebugHelper": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Execute Workflow with param2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Execute Workflow with param3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {}
|
||||
}
|
|
@ -12,8 +12,11 @@ import type { INodeExecutionData, ITaskData, ITaskMetadata } from 'n8n-workflow'
|
|||
import { setActivePinia } from 'pinia';
|
||||
import { useNodeTypesStore } from '../stores/nodeTypes.store';
|
||||
|
||||
const { openRelatedExecution } = vi.hoisted(() => ({
|
||||
openRelatedExecution: vi.fn(),
|
||||
const MOCK_EXECUTION_URL = 'execution.url/123';
|
||||
|
||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = vi.hoisted(() => ({
|
||||
trackOpeningRelatedExecution: vi.fn(),
|
||||
resolveRelatedExecutionUrl: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('vue-router', () => {
|
||||
|
@ -26,7 +29,8 @@ vi.mock('vue-router', () => {
|
|||
|
||||
vi.mock('@/composables/useExecutionHelpers', () => ({
|
||||
useExecutionHelpers: () => ({
|
||||
openRelatedExecution,
|
||||
trackOpeningRelatedExecution,
|
||||
resolveRelatedExecutionUrl,
|
||||
}),
|
||||
}));
|
||||
|
||||
|
@ -42,6 +46,10 @@ const nodes = [
|
|||
] as INodeUi[];
|
||||
|
||||
describe('RunData', () => {
|
||||
beforeAll(() => {
|
||||
resolveRelatedExecutionUrl.mockReturnValue('execution.url/123');
|
||||
});
|
||||
|
||||
it("should render pin button in output panel disabled when there's binary data", () => {
|
||||
const { getByTestId } = render({
|
||||
defaultRunItems: [
|
||||
|
@ -253,11 +261,13 @@ describe('RunData', () => {
|
|||
|
||||
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Sub-Execution 123');
|
||||
expect(resolveRelatedExecutionUrl).toHaveBeenCalledWith(metadata);
|
||||
expect(getByTestId('related-execution-link')).toHaveAttribute('href', MOCK_EXECUTION_URL);
|
||||
|
||||
expect(getByTestId('ndv-items-count')).toHaveTextContent('1 item, 1 sub-execution');
|
||||
|
||||
getByTestId('related-execution-link').click();
|
||||
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||
expect(trackOpeningRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||
});
|
||||
|
||||
it('should render parent-execution link in header', async () => {
|
||||
|
@ -280,11 +290,13 @@ describe('RunData', () => {
|
|||
|
||||
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Parent Execution 123');
|
||||
expect(resolveRelatedExecutionUrl).toHaveBeenCalledWith(metadata);
|
||||
expect(getByTestId('related-execution-link')).toHaveAttribute('href', MOCK_EXECUTION_URL);
|
||||
|
||||
expect(getByTestId('ndv-items-count')).toHaveTextContent('1 item');
|
||||
|
||||
getByTestId('related-execution-link').click();
|
||||
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||
expect(trackOpeningRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||
});
|
||||
|
||||
it('should render sub-execution link in header with multiple items', async () => {
|
||||
|
@ -311,11 +323,13 @@ describe('RunData', () => {
|
|||
|
||||
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Sub-Execution 123');
|
||||
expect(resolveRelatedExecutionUrl).toHaveBeenCalledWith(metadata);
|
||||
expect(getByTestId('related-execution-link')).toHaveAttribute('href', MOCK_EXECUTION_URL);
|
||||
|
||||
expect(getByTestId('ndv-items-count')).toHaveTextContent('2 items, 3 sub-executions');
|
||||
|
||||
getByTestId('related-execution-link').click();
|
||||
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||
expect(trackOpeningRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||
});
|
||||
|
||||
it('should render sub-execution link in header with multiple runs', async () => {
|
||||
|
@ -359,7 +373,7 @@ describe('RunData', () => {
|
|||
expect(getByTestId('run-selector')).toBeInTheDocument();
|
||||
|
||||
getByTestId('related-execution-link').click();
|
||||
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||
expect(trackOpeningRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||
});
|
||||
|
||||
const render = ({
|
||||
|
|
|
@ -182,7 +182,7 @@ const nodeHelpers = useNodeHelpers();
|
|||
const externalHooks = useExternalHooks();
|
||||
const telemetry = useTelemetry();
|
||||
const i18n = useI18n();
|
||||
const { openRelatedExecution } = useExecutionHelpers();
|
||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
||||
|
||||
const node = toRef(props, 'node');
|
||||
|
||||
|
@ -1414,7 +1414,9 @@ defineExpose({ enterEditMode });
|
|||
"
|
||||
:class="$style.relatedExecutionInfo"
|
||||
data-test-id="related-execution-link"
|
||||
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
||||
:href="resolveRelatedExecutionUrl(activeTaskMetadata)"
|
||||
target="_blank"
|
||||
@click.stop="trackOpeningRelatedExecution(activeTaskMetadata, displayMode)"
|
||||
>
|
||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||
{{ getExecutionLinkLabel(activeTaskMetadata) }}
|
||||
|
@ -1496,7 +1498,9 @@ defineExpose({ enterEditMode });
|
|||
"
|
||||
:class="$style.relatedExecutionInfo"
|
||||
data-test-id="related-execution-link"
|
||||
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
||||
:href="resolveRelatedExecutionUrl(activeTaskMetadata)"
|
||||
target="_blank"
|
||||
@click.stop="trackOpeningRelatedExecution(activeTaskMetadata, displayMode)"
|
||||
>
|
||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||
{{ getExecutionLinkLabel(activeTaskMetadata) }}
|
||||
|
|
|
@ -32,7 +32,7 @@ const props = defineProps<{
|
|||
const nodeTypesStore = useNodeTypesStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
const { openRelatedExecution } = useExecutionHelpers();
|
||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
||||
|
||||
type TokenUsageData = {
|
||||
completionTokens: number;
|
||||
|
@ -140,7 +140,11 @@ const outputError = computed(() => {
|
|||
</n8n-tooltip>
|
||||
</li>
|
||||
<li v-if="runMeta?.subExecution">
|
||||
<a @click.stop="openRelatedExecution(runMeta, 'ai')">
|
||||
<a
|
||||
:href="resolveRelatedExecutionUrl(runMeta)"
|
||||
target="_blank"
|
||||
@click.stop="trackOpeningRelatedExecution(runMeta, 'ai')"
|
||||
>
|
||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||
{{
|
||||
$locale.baseText('runData.openSubExecution', {
|
||||
|
|
|
@ -64,7 +64,7 @@ const workflowsStore = useWorkflowsStore();
|
|||
|
||||
const i18n = useI18n();
|
||||
const telemetry = useTelemetry();
|
||||
const { openRelatedExecution } = useExecutionHelpers();
|
||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
||||
|
||||
const {
|
||||
hoveringItem,
|
||||
|
@ -455,7 +455,9 @@ watch(focusedMappableInput, (curr) => {
|
|||
icon="external-link-alt"
|
||||
data-test-id="debug-sub-execution"
|
||||
size="mini"
|
||||
@click="openRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
||||
target="_blank"
|
||||
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
</td>
|
||||
|
@ -582,15 +584,20 @@ watch(focusedMappableInput, (curr) => {
|
|||
placement="left"
|
||||
:hide-after="0"
|
||||
>
|
||||
<N8nIconButton
|
||||
<a
|
||||
v-if="tableData.metadata.data[index1]"
|
||||
v-show="showExecutionLink(index1)"
|
||||
type="secondary"
|
||||
icon="external-link-alt"
|
||||
data-test-id="debug-sub-execution"
|
||||
size="mini"
|
||||
@click="openRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||
/>
|
||||
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
||||
target="_blank"
|
||||
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||
>
|
||||
<N8nIconButton
|
||||
type="secondary"
|
||||
icon="external-link-alt"
|
||||
data-test-id="debug-sub-execution"
|
||||
size="mini"
|
||||
/>
|
||||
</a>
|
||||
</N8nTooltip>
|
||||
</td>
|
||||
<td
|
||||
|
|
|
@ -84,7 +84,24 @@ export function useExecutionHelpers() {
|
|||
window.open(route.href, '_blank');
|
||||
}
|
||||
|
||||
function openRelatedExecution(
|
||||
function resolveRelatedExecutionUrl(metadata: {
|
||||
parentExecution?: RelatedExecution;
|
||||
subExecution?: RelatedExecution;
|
||||
}): string {
|
||||
const info = metadata.parentExecution || metadata.subExecution;
|
||||
if (!info) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const { workflowId, executionId } = info;
|
||||
|
||||
return router.resolve({
|
||||
name: VIEWS.EXECUTION_PREVIEW,
|
||||
params: { name: workflowId, executionId },
|
||||
}).fullPath;
|
||||
}
|
||||
|
||||
function trackOpeningRelatedExecution(
|
||||
metadata: { parentExecution?: RelatedExecution; subExecution?: RelatedExecution },
|
||||
view: IRunDataDisplayMode,
|
||||
) {
|
||||
|
@ -93,8 +110,6 @@ export function useExecutionHelpers() {
|
|||
return;
|
||||
}
|
||||
|
||||
openExecutionInNewTab(info.executionId, info.workflowId);
|
||||
|
||||
telemetry.track(
|
||||
metadata.parentExecution
|
||||
? 'User clicked parent execution button'
|
||||
|
@ -110,6 +125,7 @@ export function useExecutionHelpers() {
|
|||
formatDate,
|
||||
isExecutionRetriable,
|
||||
openExecutionInNewTab,
|
||||
openRelatedExecution,
|
||||
trackOpeningRelatedExecution,
|
||||
resolveRelatedExecutionUrl,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue