From 124778a1e6a130c6b170a6e0d996dbe66af4c290 Mon Sep 17 00:00:00 2001 From: "ICX\\Tatsiana.Hashtold" Date: Mon, 21 Oct 2024 12:06:42 +0300 Subject: [PATCH] FIO-9228: fixed an issue where error messages are duplicated in error list for nested wizard --- src/Wizard.js | 2 +- .../formWithNestedWizardAndRequiredFields.js | 591 ++++++++++++++++++ test/unit/Wizard.unit.js | 50 ++ 3 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 test/forms/formWithNestedWizardAndRequiredFields.js diff --git a/src/Wizard.js b/src/Wizard.js index 7c814ec71f..37ffb053cf 100644 --- a/src/Wizard.js +++ b/src/Wizard.js @@ -1060,7 +1060,7 @@ export default class Wizard extends Webform { : this.currentPage.components; return components.reduce( - (check, comp) => comp.checkValidity(data, dirty, row, childErrors) && check, + (check, comp) => comp.checkValidity(data, dirty, row, currentPageOnly, childErrors) && check, true ); } diff --git a/test/forms/formWithNestedWizardAndRequiredFields.js b/test/forms/formWithNestedWizardAndRequiredFields.js new file mode 100644 index 0000000000..b3ccefed43 --- /dev/null +++ b/test/forms/formWithNestedWizardAndRequiredFields.js @@ -0,0 +1,591 @@ +const parentWizard = { + _id: '671218f1f2a193e287605944', + title: 'Wizard with Child Wizard failing validation cannot be linked to', + name: 'wizardWithChildWizardFailingValidationCannotBeLinkedTo', + path: 'wizardwithchildwizardfailingvalidationcannotbelinkedto', + type: 'form', + display: 'wizard', + owner: '671225d7f2a193e287607cec', + components: [ + { + title: 'Page 1', + breadcrumbClickable: true, + buttonSettings: { + previous: true, + cancel: false, + next: true, + }, + navigateOnEnter: false, + saveOnEnter: false, + scrollToTop: false, + collapsible: false, + key: 'page1', + type: 'panel', + label: 'Page 1', + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + input: false, + tableView: false, + }, + { + title: 'Page 2', + breadcrumbClickable: true, + buttonSettings: { + previous: true, + cancel: true, + next: true, + }, + navigateOnEnter: false, + saveOnEnter: false, + scrollToTop: false, + collapsible: false, + key: 'page2', + type: 'panel', + label: 'Page 2', + components: [ + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + ], + input: false, + tableView: false, + }, + { + title: 'Page 3', + breadcrumbClickable: true, + buttonSettings: { + previous: true, + cancel: true, + next: true, + }, + navigateOnEnter: false, + saveOnEnter: false, + scrollToTop: false, + collapsible: false, + key: 'page3', + type: 'panel', + label: 'Page 3', + input: false, + tableView: false, + components: [ + { + label: 'Child', + tableView: true, + form: '671218f1f2a193e28760593d', + useOriginalRevision: false, + key: 'child', + type: 'form', + input: true, + }, + ], + }, + ], + pdfComponents: [], + settings: {}, + project: '66f26afae0c7ef9920ae596d', + created: '2024-10-18T08:14:41.116Z', + modified: '2024-10-18T09:31:29.298Z', +}; + +const childWizard = { + _id: '671218f1f2a193e28760593d', + title: 'Wizard child with required field', + name: 'wizardChildWithRequiredField', + path: 'wizardchildwithrequiredfield', + type: 'form', + display: 'wizard', + owner: '67122610f2a193e287607e58', + components: [ + { + title: 'Page 1', + label: 'Page 1', + type: 'panel', + key: 'page1', + components: [ + { + label: 'Text Field child', + applyMaskOn: 'change', + tableView: true, + validateWhenHidden: false, + key: 'textField', + type: 'textfield', + input: true, + id: 'ews598d', + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: { + type: 'input', + }, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + minLength: '', + maxLength: '', + pattern: '', + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + mask: false, + inputType: 'text', + inputFormat: 'plain', + inputMask: '', + displayMask: '', + spellcheck: true, + truncateMultipleSpaces: false, + }, + ], + input: false, + tableView: false, + id: 'emsfd4n', + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: false, + hidden: false, + clearOnHide: false, + refreshOn: '', + redrawOn: '', + modalEdit: false, + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + tree: false, + lazyLoad: false, + theme: 'default', + breadcrumb: 'default', + }, + { + title: 'Page 2', + label: 'Page 2', + type: 'panel', + key: 'page2', + components: [ + { + label: 'Text Field2 child', + applyMaskOn: 'change', + tableView: true, + validate: { + required: true, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + minLength: '', + maxLength: '', + pattern: '', + }, + validateWhenHidden: false, + key: 'textField2Child', + type: 'textfield', + input: true, + id: 'ezycu4u', + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: { + type: 'input', + }, + attributes: {}, + validateOn: 'change', + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + mask: false, + inputType: 'text', + inputFormat: 'plain', + inputMask: '', + displayMask: '', + spellcheck: true, + truncateMultipleSpaces: false, + }, + ], + input: false, + tableView: false, + id: 'ehk76vf', + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: false, + hidden: false, + clearOnHide: false, + refreshOn: '', + redrawOn: '', + modalEdit: false, + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + tree: false, + lazyLoad: false, + theme: 'default', + breadcrumb: 'default', + }, + { + title: 'Page 3', + label: 'Page 3', + type: 'panel', + key: 'page3', + components: [ + { + label: 'Text Field 3 child', + labelPosition: 'top', + placeholder: '', + description: '', + tooltip: '', + prefix: '', + suffix: '', + widget: { + type: 'input', + }, + inputMask: '', + displayMask: '', + applyMaskOn: 'change', + allowMultipleMasks: false, + customClass: '', + tabindex: '', + autocomplete: '', + hidden: false, + hideLabel: false, + showWordCount: false, + showCharCount: false, + mask: false, + autofocus: false, + spellcheck: true, + disabled: false, + tableView: true, + modalEdit: false, + multiple: false, + persistent: true, + inputFormat: 'plain', + protected: false, + dbIndex: false, + case: '', + truncateMultipleSpaces: false, + encrypted: false, + redrawOn: '', + clearOnHide: true, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + allowCalculateOverride: false, + validateOn: 'change', + validate: { + required: true, + pattern: '', + customMessage: '', + custom: '', + customPrivate: false, + json: '', + minLength: '', + maxLength: '', + strictDateValidation: false, + multiple: false, + unique: false, + }, + unique: false, + validateWhenHidden: false, + errorLabel: '', + errors: '', + key: 'textField3Child', + tags: [], + properties: {}, + conditional: { + show: null, + conjunction: '', + conditions: [], + json: '', + when: null, + eq: '', + }, + customConditional: '', + logic: [], + attributes: {}, + overlay: { + style: '', + page: '', + left: '', + top: '', + width: '', + height: '', + }, + type: 'textfield', + dataGridLabel: false, + input: true, + refreshOn: '', + addons: [], + inputType: 'text', + id: 'emz75co', + defaultValue: '', + }, + ], + id: 'enn4a7', + input: false, + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: false, + hidden: false, + clearOnHide: false, + refreshOn: '', + redrawOn: '', + tableView: false, + modalEdit: false, + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + tree: false, + lazyLoad: false, + theme: 'default', + breadcrumb: 'default', + }, + ], + project: '66f26afae0c7ef9920ae596d', + created: '2024-10-18T08:14:41.092Z', + modified: '2024-10-18T12:12:55.062Z', +}; + +export default { parentWizard, childWizard }; diff --git a/test/unit/Wizard.unit.js b/test/unit/Wizard.unit.js index 51bddf1548..cd9fb0319b 100644 --- a/test/unit/Wizard.unit.js +++ b/test/unit/Wizard.unit.js @@ -41,6 +41,7 @@ import { fastCloneDeep } from '../../src/utils/utils'; import formsWithAllowOverride from '../forms/formsWithAllowOverrideComps'; import WizardWithCheckboxes from '../forms/wizardWithCheckboxes'; import WizardWithRequiredFields from '../forms/wizardWithRequiredFields'; +import formWithNestedWizardAndRequiredFields from '../forms/formWithNestedWizardAndRequiredFields'; // eslint-disable-next-line max-statements describe('Wizard tests', () => { @@ -448,6 +449,55 @@ describe('Wizard tests', () => { .catch((err) => done(err)); }) + it('Should have validation errors when parent form is valid but nested wizard is not', function(done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const nestedWizard = _.cloneDeep(formWithNestedWizardAndRequiredFields.childWizard); + const parentWizard = _.cloneDeep(formWithNestedWizardAndRequiredFields.parentWizard); + const clickEvent = new Event('click'); + + wizard.setForm(parentWizard).then(() => { + const formio = new Formio('http://test.localhost/test', {}); + wizard.formio = formio; + const nestedFormComp = wizard.getComponent('child'); + + nestedFormComp.loadSubForm = ()=> { + nestedFormComp.formObj = nestedWizard; + nestedFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(nestedWizard)); + }; + + nestedFormComp.createSubForm(); + setTimeout(() => { + const checkPage = (pageNumber) => { + assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); + }; + checkPage(0); + const firstPageComponentInput = wizard.allPages[0].components[0].refs.input[0]; + const inputEvent = new Event('input'); + firstPageComponentInput.value = 'test'; + firstPageComponentInput.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(wizard.submission.data.textField, 'test'); + const nestedWizardBreadcrumbBtn = _.get(wizard.refs, `${wizard.wizardKey}-link[4]`); + nestedWizardBreadcrumbBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + checkPage(4); + _.get(wizard.refs, `${wizard.wizardKey}-submit`).dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(wizard.errors.length, 2); + done(); + }, 300) + }, 300) + }, 300) + }, 300) + }) + .catch((err) => done(err)); + }) + it('Should render values in HTML render mode', function(done) { const formElement = document.createElement('div'); const wizard = new Wizard(formElement, {