mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
test: Add unit testing to nodes (no-changelog) (#4890)
* 🧪 Add base for building unit testing within nodes * Improve helper functions * 🧪 If node test * 🧪 Airtable node test * 🧪 If node test improvements * 🧪 Airtable node test improvements * ♻️ cleanup node unit tests * ♻️ refactor getting node result data to use helper method * ⚡ removed unused variables * ♻️ Helper to read json files --------- Co-authored-by: Marcus <marcus@n8n.io> Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
parent
dbcbe595cc
commit
5b9c650e55
|
@ -0,0 +1,64 @@
|
|||
import { INodeType } from 'n8n-workflow';
|
||||
import { executeWorkflow } from '../ExecuteWorkflow';
|
||||
import * as Helpers from '../Helpers';
|
||||
import { WorkflowTestData } from '../types';
|
||||
import nock from 'nock';
|
||||
|
||||
import { ManualTrigger } from '../../../nodes/ManualTrigger/ManualTrigger.node';
|
||||
import { Airtable } from '../../../nodes/Airtable/Airtable.node';
|
||||
|
||||
const records = [
|
||||
{
|
||||
id: 'rec2BWBoyS5QsS7pT',
|
||||
createdTime: '2022-08-25T08:22:34.000Z',
|
||||
fields: {
|
||||
name: 'Tim',
|
||||
email: 'tim@email.com',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('Execute Airtable Node', () => {
|
||||
beforeEach(() => {
|
||||
nock.disableNetConnect();
|
||||
nock('https://api.airtable.com/v0')
|
||||
.get('/appIaXXdDqS5ORr4V/tbljyBEdYzCPF0NDh?pageSize=100')
|
||||
.reply(200, { records });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nock.restore();
|
||||
});
|
||||
|
||||
const tests: Array<WorkflowTestData> = [
|
||||
{
|
||||
description: 'List Airtable Records',
|
||||
input: {
|
||||
workflowData: Helpers.readJsonFileSync('test/nodes/Airtable/workflow.json'),
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Airtable: [[...records]],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const nodes: INodeType[] = [new ManualTrigger(), new Airtable()];
|
||||
const nodeTypes = Helpers.setup(nodes);
|
||||
|
||||
for (const testData of tests) {
|
||||
test(testData.description, async () => {
|
||||
// execute workflow
|
||||
const { result } = await executeWorkflow(testData, nodeTypes);
|
||||
|
||||
// check if result node data matches expected test data
|
||||
const resultNodeData = Helpers.getResultNodeData(result, testData);
|
||||
resultNodeData.forEach(({ nodeName, resultData }) =>
|
||||
expect(resultData).toEqual(testData.output.nodeData[nodeName]),
|
||||
);
|
||||
|
||||
expect(result.finished).toEqual(true);
|
||||
});
|
||||
}
|
||||
});
|
63
packages/nodes-base/test/nodes/Airtable/workflow.json
Normal file
63
packages/nodes-base/test/nodes/Airtable/workflow.json
Normal file
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "104a4d08d8897b8bdeb38aaca515021075e0bd8544c983c2bb8c86e6a8e6081c"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "f857c37f-36c1-4c9c-9b5f-f6ef49db67e3",
|
||||
"name": "On clicking 'execute'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
820,
|
||||
380
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "list",
|
||||
"application": {
|
||||
"__rl": true,
|
||||
"value": "https://airtable.com/appIaXXdDqS5ORr4V/tbljyBEdYzCPF0NDh/viwInsMdsxffad0aU",
|
||||
"mode": "url",
|
||||
"__regex": "https://airtable.com/([a-zA-Z0-9]{2,})"
|
||||
},
|
||||
"table": {
|
||||
"__rl": true,
|
||||
"value": "https://airtable.com/appIaXXdDqS5ORr4V/tbljyBEdYzCPF0NDh/viwInsMdsxffad0aU",
|
||||
"mode": "url",
|
||||
"__regex": "https://airtable.com/[a-zA-Z0-9]{2,}/([a-zA-Z0-9]{2,})"
|
||||
},
|
||||
"additionalOptions": {}
|
||||
},
|
||||
"id": "5654d3b3-fe83-4988-889b-94f107d41807",
|
||||
"name": "Airtable",
|
||||
"type": "n8n-nodes-base.airtable",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1020,
|
||||
380
|
||||
],
|
||||
"credentials": {
|
||||
"airtableApi": {
|
||||
"id": "20",
|
||||
"name": "Airtable account"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"On clicking 'execute'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Airtable",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
24
packages/nodes-base/test/nodes/ExecuteWorkflow.ts
Normal file
24
packages/nodes-base/test/nodes/ExecuteWorkflow.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { WorkflowExecute } from 'n8n-core';
|
||||
import { createDeferredPromise, INodeTypes, IRun, Workflow } from 'n8n-workflow';
|
||||
import * as Helpers from './Helpers';
|
||||
|
||||
export async function executeWorkflow(testData, nodeTypes: INodeTypes) {
|
||||
const executionMode = 'manual';
|
||||
const workflowInstance = new Workflow({
|
||||
id: 'test',
|
||||
nodes: testData.input.workflowData.nodes,
|
||||
connections: testData.input.workflowData.connections,
|
||||
active: false,
|
||||
nodeTypes,
|
||||
});
|
||||
|
||||
const waitPromise = await createDeferredPromise<IRun>();
|
||||
const nodeExecutionOrder: string[] = [];
|
||||
const additionalData = Helpers.WorkflowExecuteAdditionalData(waitPromise, nodeExecutionOrder);
|
||||
|
||||
const workflowExecute = new WorkflowExecute(additionalData, executionMode);
|
||||
|
||||
const executionData = await workflowExecute.run(workflowInstance);
|
||||
const result = await waitPromise.promise();
|
||||
return { executionData, result, nodeExecutionOrder };
|
||||
}
|
183
packages/nodes-base/test/nodes/Helpers.ts
Normal file
183
packages/nodes-base/test/nodes/Helpers.ts
Normal file
|
@ -0,0 +1,183 @@
|
|||
import { readFileSync } from 'fs';
|
||||
import { Credentials } from 'n8n-core';
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsHelper,
|
||||
IDeferredPromise,
|
||||
IExecuteWorkflowInfo,
|
||||
IHttpRequestHelper,
|
||||
IHttpRequestOptions,
|
||||
ILogger,
|
||||
INode,
|
||||
INodeCredentialsDetails,
|
||||
INodeType,
|
||||
INodeTypeData,
|
||||
INodeTypes,
|
||||
IRun,
|
||||
ITaskData,
|
||||
IVersionedNodeType,
|
||||
IWorkflowBase,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
LoggerProxy,
|
||||
NodeHelpers,
|
||||
WorkflowHooks,
|
||||
} from 'n8n-workflow';
|
||||
import { WorkflowTestData } from './types';
|
||||
|
||||
export class CredentialsHelper extends ICredentialsHelper {
|
||||
async authenticate(
|
||||
credentials: ICredentialDataDecryptedObject,
|
||||
typeName: string,
|
||||
requestParams: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
return requestParams;
|
||||
}
|
||||
|
||||
async preAuthentication(
|
||||
helpers: IHttpRequestHelper,
|
||||
credentials: ICredentialDataDecryptedObject,
|
||||
typeName: string,
|
||||
node: INode,
|
||||
credentialsExpired: boolean,
|
||||
): Promise<ICredentialDataDecryptedObject | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getParentTypes(name: string): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getDecrypted(
|
||||
nodeCredentials: INodeCredentialsDetails,
|
||||
type: string,
|
||||
): Promise<ICredentialDataDecryptedObject> {
|
||||
return {};
|
||||
}
|
||||
|
||||
async getCredentials(
|
||||
nodeCredentials: INodeCredentialsDetails,
|
||||
type: string,
|
||||
): Promise<Credentials> {
|
||||
return new Credentials({ id: null, name: '' }, '', [], '');
|
||||
}
|
||||
|
||||
async updateCredentials(
|
||||
nodeCredentials: INodeCredentialsDetails,
|
||||
type: string,
|
||||
data: ICredentialDataDecryptedObject,
|
||||
): Promise<void> {}
|
||||
}
|
||||
|
||||
export function WorkflowExecuteAdditionalData(
|
||||
waitPromise: IDeferredPromise<IRun>,
|
||||
nodeExecutionOrder: string[],
|
||||
): IWorkflowExecuteAdditionalData {
|
||||
const hookFunctions = {
|
||||
nodeExecuteAfter: [
|
||||
async (nodeName: string, data: ITaskData): Promise<void> => {
|
||||
nodeExecutionOrder.push(nodeName);
|
||||
},
|
||||
],
|
||||
workflowExecuteAfter: [
|
||||
async (fullRunData: IRun): Promise<void> => {
|
||||
waitPromise.resolve(fullRunData);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const workflowData: IWorkflowBase = {
|
||||
name: '',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
active: true,
|
||||
nodes: [],
|
||||
connections: {},
|
||||
};
|
||||
|
||||
return {
|
||||
credentialsHelper: new CredentialsHelper(''),
|
||||
hooks: new WorkflowHooks(hookFunctions, 'trigger', '1', workflowData),
|
||||
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {},
|
||||
sendMessageToUI: (message: string) => {},
|
||||
restApiUrl: '',
|
||||
encryptionKey: 'test',
|
||||
timezone: 'America/New_York',
|
||||
webhookBaseUrl: 'webhook',
|
||||
webhookWaitingBaseUrl: 'webhook-waiting',
|
||||
webhookTestBaseUrl: 'webhook-test',
|
||||
userId: '123',
|
||||
};
|
||||
}
|
||||
|
||||
class NodeTypesClass implements INodeTypes {
|
||||
nodeTypes: INodeTypeData = {};
|
||||
getByName(nodeType: string): INodeType | IVersionedNodeType {
|
||||
return this.nodeTypes[nodeType].type;
|
||||
}
|
||||
|
||||
addNode(nodeTypeName: string, nodeType: INodeType | IVersionedNodeType) {
|
||||
const loadedNode = {
|
||||
[nodeTypeName]: {
|
||||
sourcePath: '',
|
||||
type: nodeType,
|
||||
},
|
||||
};
|
||||
this.nodeTypes = {
|
||||
...this.nodeTypes,
|
||||
...loadedNode,
|
||||
};
|
||||
//Object.assign(this.nodeTypes, loadedNode);
|
||||
}
|
||||
|
||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||
return NodeHelpers.getVersionedNodeType(this.nodeTypes[nodeType].type, version);
|
||||
}
|
||||
}
|
||||
|
||||
let nodeTypesInstance: NodeTypesClass | undefined;
|
||||
|
||||
export function NodeTypes(): NodeTypesClass {
|
||||
if (nodeTypesInstance === undefined) {
|
||||
nodeTypesInstance = new NodeTypesClass();
|
||||
}
|
||||
return nodeTypesInstance;
|
||||
}
|
||||
|
||||
export function setup(nodes: INodeType[]) {
|
||||
const nodeTypes = NodeTypes();
|
||||
for (const node of nodes) {
|
||||
nodeTypes.addNode('n8n-nodes-base.' + node.description.name, node);
|
||||
}
|
||||
const fakeLogger = {
|
||||
log: () => {},
|
||||
debug: () => {},
|
||||
verbose: () => {},
|
||||
info: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
} as ILogger;
|
||||
LoggerProxy.init(fakeLogger);
|
||||
return nodeTypes;
|
||||
}
|
||||
|
||||
export function getResultNodeData(result: IRun, testData: WorkflowTestData) {
|
||||
return Object.keys(testData.output.nodeData).map((nodeName) => {
|
||||
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);
|
||||
});
|
||||
return {
|
||||
nodeName,
|
||||
resultData,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function readJsonFileSync(path: string) {
|
||||
return JSON.parse(readFileSync(path, 'utf-8'));
|
||||
}
|
58
packages/nodes-base/test/nodes/If/If.node.test.ts
Normal file
58
packages/nodes-base/test/nodes/If/If.node.test.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { INodeType } from 'n8n-workflow';
|
||||
import * as Helpers from '../Helpers';
|
||||
import { WorkflowTestData } from '../types';
|
||||
|
||||
import { ManualTrigger } from '../../../nodes/ManualTrigger/ManualTrigger.node';
|
||||
import { Set } from '../../../nodes/Set/Set.node';
|
||||
import { If } from '../../../nodes/If/If.node';
|
||||
import { NoOp } from '../../../nodes/NoOp/NoOp.node';
|
||||
import { Code } from '../../../nodes/Code/Code.node';
|
||||
|
||||
import { executeWorkflow } from '../ExecuteWorkflow';
|
||||
|
||||
describe('Execute If Node', () => {
|
||||
const tests: Array<WorkflowTestData> = [
|
||||
{
|
||||
description: 'should execute IF node true/false boolean',
|
||||
input: {
|
||||
workflowData: Helpers.readJsonFileSync('test/nodes/If/workflow.json'),
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
'On True': [
|
||||
[
|
||||
{
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
'On False': [
|
||||
[
|
||||
{
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const nodes: INodeType[] = [new ManualTrigger(), new Code(), new Set(), new If(), new NoOp()];
|
||||
const nodeTypes = Helpers.setup(nodes);
|
||||
|
||||
for (const testData of tests) {
|
||||
test(testData.description, async () => {
|
||||
// execute workflow
|
||||
const { result } = await executeWorkflow(testData, nodeTypes);
|
||||
|
||||
// check if result node data matches expected test data
|
||||
const resultNodeData = Helpers.getResultNodeData(result, testData);
|
||||
resultNodeData.forEach(({ nodeName, resultData }) =>
|
||||
expect(resultData).toEqual(testData.output.nodeData[nodeName]),
|
||||
);
|
||||
|
||||
expect(result.finished).toEqual(true);
|
||||
});
|
||||
}
|
||||
});
|
115
packages/nodes-base/test/nodes/If/workflow.json
Normal file
115
packages/nodes-base/test/nodes/If/workflow.json
Normal file
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "104a4d08d8897b8bdeb38aaca515021075e0bd8544c983c2bb8c86e6a8e6081c"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "47003824-c11f-4ae3-80a5-0e1a6d840b21",
|
||||
"name": "On clicking 'execute'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
720,
|
||||
460
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{ $json[\"value\"] }}",
|
||||
"value2": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "5420fe7d-a216-44e0-b91f-188ba5b6a340",
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1160,
|
||||
460
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "52d58f32-7faf-4874-afff-e6842bd02430",
|
||||
"name": "On False",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1400,
|
||||
580
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9be683ac-cd3f-4ba1-8fa4-052102c3d891",
|
||||
"name": "On True",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1400,
|
||||
340
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return [\n { value: true },\n { value: false }\n];"
|
||||
},
|
||||
"id": "5b3207e7-37e3-43c8-a4da-1ffebb0de134",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
940,
|
||||
460
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"On clicking 'execute'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "On True",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "On False",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
200
packages/nodes-base/test/nodes/Set/SetNode.test.ts
Normal file
200
packages/nodes-base/test/nodes/Set/SetNode.test.ts
Normal file
|
@ -0,0 +1,200 @@
|
|||
import { INodeType } from 'n8n-workflow';
|
||||
import * as Helpers from '../Helpers';
|
||||
import { Start } from '../../../nodes/Start/Start.node';
|
||||
import { Set } from '../../../nodes/Set/Set.node';
|
||||
import { executeWorkflow } from '../ExecuteWorkflow';
|
||||
import { WorkflowTestData } from '../types';
|
||||
|
||||
describe('Execute Set Node', () => {
|
||||
const tests: Array<WorkflowTestData> = [
|
||||
{
|
||||
description: 'should set value',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'uuid-1',
|
||||
parameters: {},
|
||||
name: 'Start',
|
||||
type: 'n8n-nodes-base.start',
|
||||
typeVersion: 1,
|
||||
position: [100, 300],
|
||||
},
|
||||
{
|
||||
id: 'uuid-2',
|
||||
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 set multiple values',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'uuid-1',
|
||||
parameters: {},
|
||||
name: 'Start',
|
||||
type: 'n8n-nodes-base.start',
|
||||
typeVersion: 1,
|
||||
position: [100, 300],
|
||||
},
|
||||
{
|
||||
id: 'uuid-2',
|
||||
parameters: {
|
||||
values: {
|
||||
number: [
|
||||
{
|
||||
name: 'value1',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
boolean: [
|
||||
{
|
||||
name: 'value2',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
name: 'Set',
|
||||
type: 'n8n-nodes-base.set',
|
||||
typeVersion: 1,
|
||||
position: [280, 300],
|
||||
},
|
||||
{
|
||||
id: 'uuid-3',
|
||||
parameters: {
|
||||
values: {
|
||||
number: [
|
||||
{
|
||||
name: 'value1',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
boolean: [
|
||||
{
|
||||
name: 'value2',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
name: 'Set1',
|
||||
type: 'n8n-nodes-base.set',
|
||||
typeVersion: 1,
|
||||
position: [280, 300],
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Start: {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Set',
|
||||
type: 'main',
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
Set: {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Set1',
|
||||
type: 'main',
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeExecutionOrder: ['Start', 'Set'],
|
||||
nodeData: {
|
||||
Set: [
|
||||
[
|
||||
{
|
||||
value1: 1,
|
||||
value2: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
Set1: [
|
||||
[
|
||||
{
|
||||
value1: 2,
|
||||
value2: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const nodes: INodeType[] = [new Start(), new Set()];
|
||||
const nodeTypes = Helpers.setup(nodes);
|
||||
|
||||
for (const testData of tests) {
|
||||
test(testData.description, async () => {
|
||||
// execute workflow
|
||||
const { result } = await executeWorkflow(testData, nodeTypes);
|
||||
|
||||
// check if result node data matches expected test data
|
||||
const resultNodeData = Helpers.getResultNodeData(result, testData);
|
||||
resultNodeData.forEach(({ nodeName, resultData }) =>
|
||||
expect(resultData).toEqual(testData.output.nodeData[nodeName]),
|
||||
);
|
||||
|
||||
// Check if other data has correct value
|
||||
expect(result.finished).toEqual(true);
|
||||
expect(result.data.executionData!.contextData).toEqual({});
|
||||
expect(result.data.executionData!.nodeExecutionStack).toEqual([]);
|
||||
});
|
||||
}
|
||||
});
|
48
packages/nodes-base/test/nodes/Start/StartNode.test.ts
Normal file
48
packages/nodes-base/test/nodes/Start/StartNode.test.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { INodeType } from 'n8n-workflow';
|
||||
import * as Helpers from '../Helpers';
|
||||
import { Start } from '../../../nodes/Start/Start.node';
|
||||
import { WorkflowTestData } from '../types';
|
||||
import { executeWorkflow } from '../ExecuteWorkflow';
|
||||
|
||||
describe('Execute Start Node', () => {
|
||||
const tests: Array<WorkflowTestData> = [
|
||||
{
|
||||
description: 'should run start node',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'uuid-1',
|
||||
parameters: {},
|
||||
name: 'Start',
|
||||
type: 'n8n-nodes-base.start',
|
||||
typeVersion: 1,
|
||||
position: [100, 300],
|
||||
},
|
||||
],
|
||||
connections: {},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeExecutionOrder: ['Start'],
|
||||
nodeData: {},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const nodes: INodeType[] = [new Start()];
|
||||
const nodeTypes = Helpers.setup(nodes);
|
||||
|
||||
for (const testData of tests) {
|
||||
test(testData.description, async () => {
|
||||
// execute workflow
|
||||
const { result, nodeExecutionOrder } = await executeWorkflow(testData, nodeTypes);
|
||||
// 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([]);
|
||||
});
|
||||
}
|
||||
});
|
17
packages/nodes-base/test/nodes/types.ts
Normal file
17
packages/nodes-base/test/nodes/types.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { INode, IConnections } from 'n8n-workflow';
|
||||
|
||||
export interface WorkflowTestData {
|
||||
description: string;
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: INode[];
|
||||
connections: IConnections;
|
||||
};
|
||||
};
|
||||
output: {
|
||||
nodeExecutionOrder?: string[];
|
||||
nodeData: {
|
||||
[key: string]: any[][];
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue