import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow'; import { constructExecutionMetaData } from 'n8n-core'; import get from 'lodash/get'; import { composeReturnItem, parseJsonParameter, validateEntry } from '../../v2/helpers/utils'; import type { SetNodeOptions } from '../../v2/helpers/interfaces'; export const node: INode = { id: '11', name: 'Edit Fields', type: 'n8n-nodes-base.set', typeVersion: 3, position: [42, 42], parameters: { mode: 'manual', fields: { values: [], }, include: 'none', options: {}, }, }; export const createMockExecuteFunction = (nodeParameters: IDataObject) => { const fakeExecuteFunction = { getNodeParameter( parameterName: string, _itemIndex: number, fallbackValue?: IDataObject | undefined, options?: IGetNodeParameterOptions | undefined, ) { const parameter = options?.extractValue ? `${parameterName}.value` : parameterName; return get(nodeParameters, parameter, fallbackValue); }, getNode() { return node; }, helpers: { constructExecutionMetaData }, continueOnFail: () => false, } as unknown as IExecuteFunctions; return fakeExecuteFunction; }; describe('test Set2, composeReturnItem', () => { it('should compose return item including no other fields', () => { const fakeExecuteFunction = createMockExecuteFunction({}); const inputItem = { json: { input1: 'value1', input2: 2, input3: [1, 2, 3], }, pairedItem: { item: 0, input: undefined, }, }; const newData = { num1: 55, str1: '42', arr1: ['foo', 'bar'], obj: { key: 'value', }, }; const options: SetNodeOptions = { include: 'none', }; const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options, 3.4); expect(result).toEqual({ json: { num1: 55, str1: '42', arr1: ['foo', 'bar'], obj: { key: 'value', }, }, pairedItem: { item: 0, }, }); }); it('should compose return item including selected fields', () => { const fakeExecuteFunction = createMockExecuteFunction({ includeFields: 'input1, input2' }); const inputItem = { json: { input1: 'value1', input2: 2, input3: [1, 2, 3], }, pairedItem: { item: 0, input: undefined, }, }; const newData = { num1: 55, str1: '42', arr1: ['foo', 'bar'], obj: { key: 'value', }, }; const options: SetNodeOptions = { include: 'selected', }; const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options, 3.4); expect(result).toEqual({ json: { num1: 55, str1: '42', arr1: ['foo', 'bar'], input1: 'value1', input2: 2, obj: { key: 'value', }, }, pairedItem: { item: 0, }, }); }); it('should include binary when expected in version <3.4', () => { const fakeExecuteFunction = createMockExecuteFunction({}); const inputItem = { json: { input1: 'value1', input2: 2, input3: [1, 2, 3], }, pairedItem: { item: 0, input: undefined, }, binary: { data: { data: 'content', mimeType: 'image/jpg', }, }, }; const newData = { num1: 55, str1: '42', arr1: ['foo', 'bar'], obj: { key: 'value', }, }; const resultWithIncludeBinary = composeReturnItem.call( fakeExecuteFunction, 0, inputItem, newData, { include: 'all', includeBinary: true, }, 3.3, ); expect(resultWithIncludeBinary.binary).toEqual(inputItem.binary); const resultWithoutIncludeBinary = composeReturnItem.call( fakeExecuteFunction, 0, inputItem, newData, { include: 'all', }, 3.3, ); expect(resultWithoutIncludeBinary.binary).toBeUndefined(); }); it('should include binary when expected in version >=3.4', () => { const fakeExecuteFunction = createMockExecuteFunction({}); const inputItem = { json: { input1: 'value1', input2: 2, input3: [1, 2, 3], }, pairedItem: { item: 0, input: undefined, }, binary: { data: { data: 'content', mimeType: 'image/jpg', }, }, }; const newData = { num1: 55, str1: '42', arr1: ['foo', 'bar'], obj: { key: 'value', }, }; const resultWithStripBinary = composeReturnItem.call( fakeExecuteFunction, 0, inputItem, newData, { include: 'all', stripBinary: true, }, 3.4, ); expect(resultWithStripBinary.binary).toBeUndefined(); const resultWithoutStripBinary = composeReturnItem.call( fakeExecuteFunction, 0, inputItem, newData, { include: 'all', }, 3.4, ); expect(resultWithoutStripBinary.binary).toEqual(inputItem.binary); }); }); describe('test Set2, parseJsonParameter', () => { it('should parse valid JSON string', () => { const result = parseJsonParameter('{"foo": "bar"}', node, 0, 'test'); expect(result).toEqual({ foo: 'bar', }); }); it('should tolerate single quotes in string', () => { const result = parseJsonParameter("{'foo': 'bar'}", node, 0, 'test'); expect(result).toEqual({ foo: 'bar', }); }); it('should tolerate unquoted keys', () => { const result = parseJsonParameter("{foo: 'bar'}", node, 0, 'test'); expect(result).toEqual({ foo: 'bar', }); }); it('should tolerate trailing comma', () => { const result = parseJsonParameter('{"foo": "bar"},', node, 0, 'test'); expect(result).toEqual({ foo: 'bar', }); }); it('should tolerate trailing commas in objects', () => { const result = parseJsonParameter("{foo: 'bar', baz: {'foo': 'bar',}, }", node, 0, 'test'); expect(result).toEqual({ foo: 'bar', baz: { foo: 'bar', }, }); }); }); describe('test Set2, validateEntry', () => { it('should convert number to string', () => { const result = validateEntry('foo', 'string', 42 as unknown as string, node, 0); expect(result).toEqual({ name: 'foo', value: '42', }); }); it('should convert array to string', () => { const result = validateEntry('foo', 'string', [1, 2, 3] as unknown as string, node, 0); expect(result).toEqual({ name: 'foo', value: '[1,2,3]', }); }); it('should convert object to string', () => { const result = validateEntry('foo', 'string', { foo: 'bar' } as unknown as string, node, 0); expect(result).toEqual({ name: 'foo', value: '{"foo":"bar"}', }); }); it('should convert boolean to string', () => { const result = validateEntry('foo', 'string', true as unknown as string, node, 0); expect(result).toEqual({ name: 'foo', value: 'true', }); }); it('should convert undefined to string', () => { const result = validateEntry('foo', 'string', undefined as unknown as string, node, 0); expect(result).toEqual({ name: 'foo', value: 'undefined', }); }); });