2023-04-19 04:09:46 -07:00
|
|
|
import { anyNumber, mock } from 'jest-mock-extended';
|
2023-08-30 02:17:04 -07:00
|
|
|
import { NodeVM } from '@n8n/vm2';
|
2023-04-19 04:09:46 -07:00
|
|
|
import type { IExecuteFunctions, IWorkflowDataProxyData } from 'n8n-workflow';
|
|
|
|
import { NodeHelpers } from 'n8n-workflow';
|
|
|
|
import { normalizeItems } from 'n8n-core';
|
2023-09-22 08:22:12 -07:00
|
|
|
import { testWorkflows, getWorkflowFilenames, initBinaryDataService } from '@test/nodes/Helpers';
|
2023-04-19 04:09:46 -07:00
|
|
|
import { Code } from '../Code.node';
|
|
|
|
import { ValidationError } from '../ValidationError';
|
2023-02-09 04:56:39 -08:00
|
|
|
|
2023-05-04 11:00:00 -07:00
|
|
|
describe('Test Code Node', () => {
|
|
|
|
const workflows = getWorkflowFilenames(__dirname);
|
2023-02-09 04:56:39 -08:00
|
|
|
|
2023-05-04 11:00:00 -07:00
|
|
|
beforeAll(async () => {
|
2023-09-22 08:22:12 -07:00
|
|
|
await initBinaryDataService();
|
2023-05-04 11:00:00 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
testWorkflows(workflows);
|
|
|
|
});
|
2023-04-19 04:09:46 -07:00
|
|
|
|
|
|
|
describe('Code Node unit test', () => {
|
|
|
|
const node = new Code();
|
|
|
|
const thisArg = mock<IExecuteFunctions>({
|
2023-08-01 08:47:43 -07:00
|
|
|
getNode: () => mock(),
|
2023-04-19 04:09:46 -07:00
|
|
|
helpers: { normalizeItems },
|
|
|
|
prepareOutputData: NodeHelpers.prepareOutputData,
|
|
|
|
});
|
|
|
|
const workflowDataProxy = mock<IWorkflowDataProxyData>({ $input: mock() });
|
|
|
|
thisArg.getWorkflowDataProxy.mockReturnValue(workflowDataProxy);
|
|
|
|
|
|
|
|
describe('runOnceForAllItems', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
thisArg.getNodeParameter.calledWith('mode', 0).mockReturnValueOnce('runOnceForAllItems');
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('valid return data', () => {
|
|
|
|
const tests: Record<string, [object | null, object]> = {
|
|
|
|
'should handle null': [null, []],
|
|
|
|
'should handle pre-normalized result': [
|
|
|
|
[{ json: { count: 42 } }],
|
|
|
|
[{ json: { count: 42 } }],
|
|
|
|
],
|
|
|
|
'should handle when returned data is not an array': [
|
|
|
|
{ json: { count: 42 } },
|
|
|
|
[{ json: { count: 42 } }],
|
|
|
|
],
|
|
|
|
'should handle when returned data is an array with items missing `json` key': [
|
|
|
|
[{ count: 42 }],
|
|
|
|
[{ json: { count: 42 } }],
|
|
|
|
],
|
|
|
|
'should handle when returned data missing `json` key': [
|
|
|
|
{ count: 42 },
|
|
|
|
[{ json: { count: 42 } }],
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.entries(tests).forEach(([title, [input, expected]]) =>
|
|
|
|
test(title, async () => {
|
2023-05-04 11:00:00 -07:00
|
|
|
jest.spyOn(NodeVM.prototype, 'run').mockResolvedValueOnce(input);
|
2023-04-19 04:09:46 -07:00
|
|
|
|
|
|
|
const output = await node.execute.call(thisArg);
|
|
|
|
expect(output).toEqual([expected]);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('invalid return data', () => {
|
|
|
|
const tests = {
|
|
|
|
undefined,
|
|
|
|
null: null,
|
|
|
|
date: new Date(),
|
|
|
|
string: 'string',
|
|
|
|
boolean: true,
|
|
|
|
array: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.entries(tests).forEach(([title, returnData]) =>
|
|
|
|
test(`return error if \`.json\` is ${title}`, async () => {
|
2023-05-04 11:00:00 -07:00
|
|
|
jest.spyOn(NodeVM.prototype, 'run').mockResolvedValueOnce([{ json: returnData }]);
|
2023-04-19 04:09:46 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
await node.execute.call(thisArg);
|
|
|
|
throw new Error("Validation error wasn't thrown");
|
|
|
|
} catch (error) {
|
|
|
|
expect(error).toBeInstanceOf(ValidationError);
|
2023-05-04 11:00:00 -07:00
|
|
|
expect(error.message).toEqual("A 'json' property isn't an object [item 0]");
|
2023-04-19 04:09:46 -07:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('runOnceForEachItem', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
thisArg.getNodeParameter.calledWith('mode', 0).mockReturnValueOnce('runOnceForEachItem');
|
|
|
|
thisArg.getNodeParameter.calledWith('jsCode', anyNumber()).mockReturnValueOnce('');
|
|
|
|
thisArg.getInputData.mockReturnValueOnce([{ json: {} }]);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('valid return data', () => {
|
|
|
|
const tests: Record<string, [object | null, { json: any } | null]> = {
|
|
|
|
'should handle pre-normalized result': [{ json: { count: 42 } }, { json: { count: 42 } }],
|
|
|
|
'should handle when returned data missing `json` key': [
|
|
|
|
{ count: 42 },
|
|
|
|
{ json: { count: 42 } },
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.entries(tests).forEach(([title, [input, expected]]) =>
|
|
|
|
test(title, async () => {
|
2023-05-04 11:00:00 -07:00
|
|
|
jest.spyOn(NodeVM.prototype, 'run').mockResolvedValueOnce(input);
|
2023-04-19 04:09:46 -07:00
|
|
|
|
|
|
|
const output = await node.execute.call(thisArg);
|
|
|
|
expect(output).toEqual([[{ json: expected?.json, pairedItem: { item: 0 } }]]);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('invalid return data', () => {
|
|
|
|
const tests = {
|
|
|
|
undefined,
|
|
|
|
null: null,
|
|
|
|
date: new Date(),
|
|
|
|
string: 'string',
|
|
|
|
boolean: true,
|
|
|
|
array: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.entries(tests).forEach(([title, returnData]) =>
|
|
|
|
test(`return error if \`.json\` is ${title}`, async () => {
|
2023-05-04 11:00:00 -07:00
|
|
|
jest.spyOn(NodeVM.prototype, 'run').mockResolvedValueOnce({ json: returnData });
|
2023-04-19 04:09:46 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
await node.execute.call(thisArg);
|
|
|
|
throw new Error("Validation error wasn't thrown");
|
|
|
|
} catch (error) {
|
|
|
|
expect(error).toBeInstanceOf(ValidationError);
|
|
|
|
expect(error.message).toEqual("A 'json' property isn't an object [item 0]");
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|