mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 17:14:05 -08:00
626 lines
11 KiB
TypeScript
626 lines
11 KiB
TypeScript
|
|
import {
|
|
IConnections,
|
|
INode,
|
|
IRun,
|
|
Workflow,
|
|
} from 'n8n-workflow';
|
|
|
|
import {
|
|
createDeferredPromise,
|
|
WorkflowExecute,
|
|
} from '../src';
|
|
|
|
import * as Helpers from './Helpers';
|
|
|
|
|
|
describe('WorkflowExecute', () => {
|
|
|
|
describe('run', () => {
|
|
|
|
const tests: Array<{
|
|
description: string;
|
|
input: {
|
|
workflowData: {
|
|
nodes: INode[],
|
|
connections: IConnections,
|
|
}
|
|
},
|
|
output: {
|
|
nodeExecutionOrder: string[];
|
|
nodeData: {
|
|
[key: string]: any[][]; // tslint:disable-line:no-any
|
|
};
|
|
},
|
|
}> = [
|
|
{
|
|
description: 'should run basic two node workflow',
|
|
input: {
|
|
// Leave the workflowData in regular JSON to be able to easily
|
|
// copy it from/in the UI
|
|
workflowData: {
|
|
"nodes": [
|
|
{
|
|
"parameters": {},
|
|
"name": "Start",
|
|
"type": "n8n-nodes-base.start",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
100,
|
|
300
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value1",
|
|
"value": 1
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
280,
|
|
300
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Start": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Set",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
}
|
|
},
|
|
},
|
|
output: {
|
|
nodeExecutionOrder: [
|
|
'Start',
|
|
'Set',
|
|
],
|
|
nodeData: {
|
|
Set: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
},
|
|
],
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: 'should run node twice when it has two input connections',
|
|
input: {
|
|
// Leave the workflowData in regular JSON to be able to easily
|
|
// copy it from/in the UI
|
|
workflowData: {
|
|
"nodes": [
|
|
{
|
|
"parameters": {},
|
|
"name": "Start",
|
|
"type": "n8n-nodes-base.start",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
100,
|
|
300
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value1",
|
|
"value": 1
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set1",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
300,
|
|
250
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value2",
|
|
"value": 2
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set2",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
500,
|
|
400
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Start": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Set1",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Set2",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Set1": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Set2",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
}
|
|
},
|
|
},
|
|
output: {
|
|
nodeExecutionOrder: [
|
|
'Start',
|
|
'Set1',
|
|
'Set2',
|
|
'Set2',
|
|
],
|
|
nodeData: {
|
|
Set1: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
},
|
|
]
|
|
],
|
|
Set2: [
|
|
[
|
|
{
|
|
value2: 2,
|
|
},
|
|
],
|
|
[
|
|
{
|
|
value1: 1,
|
|
value2: 2,
|
|
},
|
|
],
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: 'should run complicated multi node workflow',
|
|
input: {
|
|
// Leave the workflowData in regular JSON to be able to easily
|
|
// copy it from/in the UI
|
|
workflowData: {
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"mode": "passThrough"
|
|
},
|
|
"name": "Merge4",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1150,
|
|
500
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value2",
|
|
"value": 2
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set2",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
290,
|
|
400
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value4",
|
|
"value": 4
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set4",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
850,
|
|
200
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value3",
|
|
"value": 3
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set3",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
650,
|
|
200
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"mode": "passThrough"
|
|
},
|
|
"name": "Merge4",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1150,
|
|
500
|
|
]
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"name": "Merge3",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1000,
|
|
400
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"mode": "passThrough",
|
|
"output": "input2"
|
|
},
|
|
"name": "Merge2",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
700,
|
|
400
|
|
]
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"name": "Merge1",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
500,
|
|
300
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "value1",
|
|
"value": 1
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"name": "Set1",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
300,
|
|
200
|
|
]
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"name": "Start",
|
|
"type": "n8n-nodes-base.start",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
100,
|
|
300
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Set2": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge1",
|
|
"type": "main",
|
|
"index": 1
|
|
},
|
|
{
|
|
"node": "Merge2",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Set4": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge3",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Set3": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Set4",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Merge3": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge4",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Merge2": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge3",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Merge1": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge2",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Set1": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Merge1",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Set3",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Start": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Set1",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Set2",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Merge4",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
}
|
|
}
|
|
},
|
|
},
|
|
output: {
|
|
nodeExecutionOrder: [
|
|
'Start',
|
|
'Set1',
|
|
'Set2',
|
|
'Set3',
|
|
'Merge1',
|
|
'Set4',
|
|
'Merge2',
|
|
'Merge3',
|
|
'Merge4',
|
|
],
|
|
nodeData: {
|
|
Set1: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
},
|
|
],
|
|
],
|
|
Set2: [
|
|
[
|
|
{
|
|
value2: 2,
|
|
},
|
|
],
|
|
],
|
|
Set3: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
value3: 3,
|
|
},
|
|
],
|
|
],
|
|
Set4: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
value3: 3,
|
|
value4: 4,
|
|
},
|
|
],
|
|
],
|
|
Merge1: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
},
|
|
{
|
|
value2: 2,
|
|
},
|
|
]
|
|
],
|
|
Merge2: [
|
|
[
|
|
{
|
|
value2: 2,
|
|
},
|
|
]
|
|
],
|
|
Merge3: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
value3: 3,
|
|
value4: 4,
|
|
},
|
|
{
|
|
value2: 2,
|
|
},
|
|
]
|
|
],
|
|
Merge4: [
|
|
[
|
|
{
|
|
value1: 1,
|
|
value3: 3,
|
|
value4: 4,
|
|
},
|
|
{
|
|
value2: 2,
|
|
},
|
|
]
|
|
],
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
|
|
const executionMode = 'manual';
|
|
const nodeTypes = Helpers.NodeTypes();
|
|
|
|
for (const testData of tests) {
|
|
test(testData.description, async () => {
|
|
|
|
const workflowInstance = new Workflow('test', testData.input.workflowData.nodes, testData.input.workflowData.connections, false, nodeTypes);
|
|
|
|
const waitPromise = await createDeferredPromise<IRun>();
|
|
const nodeExecutionOrder: string[] = [];
|
|
const additionalData = Helpers.WorkflowExecuteAdditionalData(waitPromise, nodeExecutionOrder);
|
|
|
|
const workflowExecute = new WorkflowExecute(additionalData, executionMode);
|
|
|
|
const executionId = await workflowExecute.run(workflowInstance, undefined);
|
|
expect(executionId).toBeDefined();
|
|
|
|
const result = await waitPromise.promise();
|
|
|
|
// Check if the output data of the nodes is correct
|
|
for (const nodeName of Object.keys(testData.output.nodeData)) {
|
|
if (result.data.resultData.runData[nodeName] === undefined) {
|
|
throw new Error(`Data for node "${nodeName}" is missing!`);
|
|
}
|
|
|
|
const resultData = result.data.resultData.runData[nodeName].map((nodeData) => {
|
|
if (nodeData.data === undefined) {
|
|
return null;
|
|
}
|
|
return nodeData.data.main[0]!.map((entry) => entry.json );
|
|
});
|
|
|
|
// expect(resultData).toEqual(testData.output.nodeData[nodeName]);
|
|
expect(resultData).toEqual(testData.output.nodeData[nodeName]);
|
|
}
|
|
|
|
// Check if the nodes did execute in the correct order
|
|
expect(nodeExecutionOrder).toEqual(testData.output.nodeExecutionOrder);
|
|
|
|
// Check if other data has correct value
|
|
expect(result.finished).toEqual(true);
|
|
expect(result.data.executionData!.contextData).toEqual({});
|
|
expect(result.data.executionData!.nodeExecutionStack).toEqual([]);
|
|
expect(result.data.executionData!.waitingExecution).toEqual({});
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
});
|