From 4d8a916ce7790caa11f99f62914f9546e2b4ad02 Mon Sep 17 00:00:00 2001 From: "ICX\\Tatsiana.Hashtold" Date: Thu, 26 Sep 2024 11:48:29 +0300 Subject: [PATCH] FIO-9081: fixed an issue where required validation is not triggered for empty value of multiple component --- src/process/__tests__/process.test.ts | 161 ++++++++++++++++++ .../validation/__tests__/multiple.test.ts | 2 +- src/process/validation/index.ts | 2 +- .../validation/rules/validateRequired.ts | 1 + src/types/process/ProcessorInfo.ts | 1 + 5 files changed, 165 insertions(+), 2 deletions(-) diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index b1ed7425..4942fcb5 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -3689,6 +3689,167 @@ describe('Process Tests', () => { expect((context.scope as ValidationScope).errors).to.have.length(1); }); + it('Should validate required an empty array for multiple select', async () => { + const form = { + _id: '66f4141e34ac6c4049cc5144', + title: 'required multiple', + name: 'requiredMultiple', + path: 'requiredmultiple', + type: 'form', + display: 'form', + owner: '637b2e6b48c1227e60b1f910', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + multiple: true, + data: { + values: [ + { + label: 'a', + value: 'a', + }, + { + label: 'b', + value: 'b', + }, + { + label: 'c', + value: 'c', + }, + ], + }, + validate: { + required: true, + }, + validateWhenHidden: false, + key: 'select', + type: 'select', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + project: '66f26afae0c7ef9920ae59f6', + }; + + const submission = { + data: { select: [] }, + owner: '637b2e6b48c1227e60b1f910', + access: [], + _fvid: 0, + state: 'submitted', + _id: '66c455fc0f00757fd4b0e79d', + form: '66bc5cff7ca1729623a182db', + }; + + const errors: any = []; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: { errors }, + config: { + server: true, + }, + }; + processSync(context); + submission.data = context.data; + context.processors = ProcessTargets.evaluator; + processSync(context); + assert.equal(context.scope.errors.length, 1); + assert.equal(context.scope.errors[0].ruleName, 'required'); + }); + + it('Should validate required an value for multiple select without errors', async () => { + const form = { + _id: '66f4141e34ac6c4049cc5144', + title: 'required multiple', + name: 'requiredMultiple', + path: 'requiredmultiple', + type: 'form', + display: 'form', + owner: '637b2e6b48c1227e60b1f910', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + multiple: true, + data: { + values: [ + { + label: 'a', + value: 'a', + }, + { + label: 'b', + value: 'b', + }, + { + label: 'c', + value: 'c', + }, + ], + }, + validate: { + required: true, + }, + validateWhenHidden: false, + key: 'select', + type: 'select', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + project: '66f26afae0c7ef9920ae59f6', + }; + + const submission = { + data: { select: ['a'] }, + owner: '637b2e6b48c1227e60b1f910', + access: [], + _fvid: 0, + state: 'submitted', + _id: '66c455fc0f00757fd4b0e79d', + form: '66bc5cff7ca1729623a182db', + }; + + const errors: any = []; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: { errors }, + config: { + server: true, + }, + }; + processSync(context); + submission.data = context.data; + context.processors = ProcessTargets.evaluator; + processSync(context); + assert.equal(context.scope.errors.length, 0); + }); + describe('For EditGrid:', () => { const components = [ { diff --git a/src/process/validation/__tests__/multiple.test.ts b/src/process/validation/__tests__/multiple.test.ts index a32b686f..93684867 100644 --- a/src/process/validation/__tests__/multiple.test.ts +++ b/src/process/validation/__tests__/multiple.test.ts @@ -34,7 +34,7 @@ it('Validating required rule will work for multiple values component with no row }); it('Validati olther rules will skip for multiple values component with no rows', async () => { - const otherRules = allRules.filter((rule) => !rule.fullValue); + const otherRules = allRules.filter((rule) => !rule.fullValue && !rule.emptyMultiValue); const rulesToValidate = validationRules(context, otherRules, undefined); expect(rulesToValidate).to.have.length(0); }); diff --git a/src/process/validation/index.ts b/src/process/validation/index.ts index 5322a3f4..9745e0fe 100644 --- a/src/process/validation/index.ts +++ b/src/process/validation/index.ts @@ -51,7 +51,7 @@ export function validationRules( if (context.component.multiple && Array.isArray(context.value) && context.value?.length === 0 && - !rule.fullValue + !rule.fullValue && !rule.emptyMultiValue ) { return acc; } diff --git a/src/process/validation/rules/validateRequired.ts b/src/process/validation/rules/validateRequired.ts index 5ceb1341..19226c71 100644 --- a/src/process/validation/rules/validateRequired.ts +++ b/src/process/validation/rules/validateRequired.ts @@ -87,6 +87,7 @@ export const validateRequiredSync: RuleFnSync = (context: ValidationContext) => export const validateRequiredInfo: ProcessorInfo = { name: 'validateRequired', process: validateRequired, + emptyMultiValue: true, processSync: validateRequiredSync, shouldProcess: shouldValidate, }; diff --git a/src/types/process/ProcessorInfo.ts b/src/types/process/ProcessorInfo.ts index af54b5ce..248a3229 100644 --- a/src/types/process/ProcessorInfo.ts +++ b/src/types/process/ProcessorInfo.ts @@ -2,6 +2,7 @@ export type ProcessCheckFn = (context: ProcessorContext) => bo export type ProcessorInfo = { name: string; fullValue?: boolean; + emptyMultiValue?: boolean process?: (context: ProcessorContext) => Promise; processSync?: (context: ProcessorContext) => ProcessorReturnType; postProcess?: (context: ProcessorContext) => void;