From 9bd72eaa139649811cb1114f3cf40f22d5dfe905 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:41:26 +0200 Subject: [PATCH] fix(n8n Form Node): Hidden field fix (#14219) --- .../nodes-base/nodes/Form/test/utils.test.ts | 75 ++++++++++++++++++ packages/nodes-base/nodes/Form/utils.ts | 77 +++++++++++-------- 2 files changed, 118 insertions(+), 34 deletions(-) diff --git a/packages/nodes-base/nodes/Form/test/utils.test.ts b/packages/nodes-base/nodes/Form/test/utils.test.ts index 66b584e2c7e..1dff199237a 100644 --- a/packages/nodes-base/nodes/Form/test/utils.test.ts +++ b/packages/nodes-base/nodes/Form/test/utils.test.ts @@ -2,7 +2,9 @@ import { mock } from 'jest-mock-extended'; import { DateTime } from 'luxon'; import type { FormFieldsParameter, + IDataObject, INode, + INodeExecutionData, IWebhookFunctions, MultiPartFormData, NodeTypeAndVersion, @@ -18,6 +20,7 @@ import { sanitizeHtml, validateResponseModeConfiguration, prepareFormFields, + addFormResponseDataToReturnItem, } from '../utils'; describe('FormTrigger, parseFormDescription', () => { @@ -1032,3 +1035,75 @@ describe('validateResponseModeConfiguration', () => { }); }); }); + +describe('addFormResponseDataToReturnItem', () => { + let returnItem: INodeExecutionData; + + beforeEach(() => { + returnItem = { json: {} }; + }); + + test('should use fieldName if fieldLabel is missing', () => { + const formFields: FormFieldsParameter = [ + { fieldName: 'Alternative Field', fieldType: 'hiddenField' }, + ] as FormFieldsParameter; + const bodyData: IDataObject = { 'field-0': 'Test Value' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['Alternative Field']).toBe('Test Value'); + }); + + it('should handle null values', () => { + const formFields: FormFieldsParameter = [{ fieldLabel: 'Test Field', fieldType: 'text' }]; + const bodyData: IDataObject = {}; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['Test Field']).toBeNull(); + }); + + it('should process html fields and set elementName', () => { + const formFields: FormFieldsParameter = [ + { fieldLabel: 'HTML Field', elementName: 'htmlElement', fieldType: 'html' }, + ]; + const bodyData: IDataObject = { 'field-0': '
HTML Content
' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json.htmlElement).toBe('HTML Content
'); + }); + + it('should parse number fields correctly', () => { + const formFields: FormFieldsParameter = [{ fieldLabel: 'Number Field', fieldType: 'number' }]; + const bodyData: IDataObject = { 'field-0': '42' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['Number Field']).toBe(42); + }); + + it('should trim text fields', () => { + const formFields: FormFieldsParameter = [{ fieldLabel: 'Text Field', fieldType: 'text' }]; + const bodyData: IDataObject = { 'field-0': ' hello world ' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['Text Field']).toBe('hello world'); + }); + + it('should parse multiselect fields from JSON', () => { + const formFields: FormFieldsParameter = [ + { fieldLabel: 'Multi Field', fieldType: 'text', multiselect: true }, + ]; + const bodyData: IDataObject = { 'field-0': '["option1", "option2"]' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['Multi Field']).toEqual(['option1', 'option2']); + }); + + it('should convert single file values to an array if multipleFiles is true', () => { + const formFields: FormFieldsParameter = [ + { fieldLabel: 'File Field', fieldType: 'file', multipleFiles: true }, + ]; + const bodyData: IDataObject = { 'field-0': 'file1.pdf' }; + + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); + expect(returnItem.json['File Field']).toEqual(['file1.pdf']); + }); +}); diff --git a/packages/nodes-base/nodes/Form/utils.ts b/packages/nodes-base/nodes/Form/utils.ts index 054da42e83e..64b8cc02bea 100644 --- a/packages/nodes-base/nodes/Form/utils.ts +++ b/packages/nodes-base/nodes/Form/utils.ts @@ -269,6 +269,48 @@ export const validateResponseModeConfiguration = (context: IWebhookFunctions) => } }; +export function addFormResponseDataToReturnItem( + returnItem: INodeExecutionData, + formFields: FormFieldsParameter, + bodyData: IDataObject, +) { + for (const [index, field] of formFields.entries()) { + const key = `field-${index}`; + const name = field.fieldLabel ?? field.fieldName; + let value = bodyData[key] ?? null; + + if (value === null) { + returnItem.json[name] = null; + continue; + } + + if (field.fieldType === 'html') { + if (field.elementName) { + returnItem.json[field.elementName] = value; + } + continue; + } + + if (field.fieldType === 'number') { + value = Number(value); + } + if (field.fieldType === 'text') { + value = String(value).trim(); + } + if (field.multiselect && typeof value === 'string') { + value = jsonParse(value); + } + if (field.fieldType === 'date' && value && field.formatDate !== '') { + value = DateTime.fromFormat(String(value), 'yyyy-mm-dd').toFormat(field.formatDate as string); + } + if (field.fieldType === 'file' && field.multipleFiles && !Array.isArray(value)) { + value = [value]; + } + + returnItem.json[name] = value; + } +} + export async function prepareFormReturnItem( context: IWebhookFunctions, formFields: FormFieldsParameter, @@ -326,40 +368,7 @@ export async function prepareFormReturnItem( } } - for (const [index, field] of formFields.entries()) { - const key = `field-${index}`; - let value = bodyData[key] ?? null; - - if (value === null) { - returnItem.json[field.fieldLabel] = null; - continue; - } - - if (field.fieldType === 'html') { - if (field.elementName) { - returnItem.json[field.elementName] = value; - } - continue; - } - - if (field.fieldType === 'number') { - value = Number(value); - } - if (field.fieldType === 'text') { - value = String(value).trim(); - } - if (field.multiselect && typeof value === 'string') { - value = jsonParse(value); - } - if (field.fieldType === 'date' && value && field.formatDate !== '') { - value = DateTime.fromFormat(String(value), 'yyyy-mm-dd').toFormat(field.formatDate as string); - } - if (field.fieldType === 'file' && field.multipleFiles && !Array.isArray(value)) { - value = [value]; - } - - returnItem.json[field.fieldLabel] = value; - } + addFormResponseDataToReturnItem(returnItem, formFields, bodyData); const timezone = useWorkflowTimezone ? context.getTimezone() : 'UTC'; returnItem.json.submittedAt = DateTime.now().setZone(timezone).toISO();