diff --git a/src/Resources/public/js/FpJsFormValidator.js b/src/Resources/public/js/FpJsFormValidator.js index a6e1e00..c51371e 100755 --- a/src/Resources/public/js/FpJsFormValidator.js +++ b/src/Resources/public/js/FpJsFormValidator.js @@ -336,25 +336,47 @@ function FpJsCustomizeMethods() { //noinspection JSCheckFunctionSignatures FpJsFormValidator.each(this, function (item) { var element = item.jsFormValidator; - if (event) { - event.preventDefault(); + + if (event && element.domNode && element.domNode.__fpJsFormValidatorSubmitting) { + delete element.domNode.__fpJsFormValidatorSubmitting; + return; } + element.validateRecursively(); - if (FpJsFormValidator.ajax.queue) { - if (event) { - event.preventDefault(); + var hasAjaxQueue = FpJsFormValidator.ajax.queue > 0; + var submitCallback = function () { + element.onValidate.apply(element.domNode, [FpJsFormValidator.getAllErrors(element, {}), event]); + if (!element.isValid()) { + if (event) { + event.preventDefault(); + } + + return; } - FpJsFormValidator.ajax.callbacks.push(function () { - element.onValidate.apply(element.domNode, [FpJsFormValidator.getAllErrors(element, {}), event]); - if (element.isValid()) { + + if (!event) { + element.submitForm.apply(item, [item]); + } else if (hasAjaxQueue) { + if (element.domNode && typeof element.domNode.requestSubmit === 'function') { + element.domNode.__fpJsFormValidatorSubmitting = true; + if (event.submitter) { + element.domNode.requestSubmit(event.submitter); + } else { + element.domNode.requestSubmit(); + } + } else { element.submitForm.apply(item, [item]); } - }); - } else { - element.onValidate.apply(element.domNode, [FpJsFormValidator.getAllErrors(element, {}), event]); - if (element.isValid()) { - element.submitForm.apply(item, [item]); } + }; + + if (hasAjaxQueue) { + if (event) { + event.preventDefault(); + } + FpJsFormValidator.ajax.callbacks.push(submitCallback); + } else { + submitCallback(); } }); }; diff --git a/src/Resources/public/js/FpJsFormValidator.test.js b/src/Resources/public/js/FpJsFormValidator.test.js index 36c2280..7d7fe40 100644 --- a/src/Resources/public/js/FpJsFormValidator.test.js +++ b/src/Resources/public/js/FpJsFormValidator.test.js @@ -189,3 +189,83 @@ describe('FpJsFormValidator error mapping', () => { expect(element.showErrors).not.toHaveBeenCalled(); }); }); + +describe('FpJsFormValidator submit flow', () => { + afterEach(() => { + window.FpJsFormValidator.ajax.queue = 0; + window.FpJsFormValidator.ajax.callbacks = []; + }); + + const createElement = (valid, form) => ({ + domNode: form || {}, + errors: {}, + children: {}, + validateRecursively: jest.fn(), + onValidate: jest.fn(), + isValid: jest.fn(() => valid), + submitForm: jest.fn(), + }); + + const submit = (element, event) => { + window.FpJsFormValidator.customizeMethods.submitForm.apply( + [{ jsFormValidator: element }], + [event], + ); + }; + + test('lets a valid native submit event continue so the original submitter is preserved', () => { + const event = { preventDefault: jest.fn() }; + const element = createElement(true); + + submit(element, event); + + expect(element.validateRecursively).toHaveBeenCalled(); + expect(element.onValidate).toHaveBeenCalledWith({}, event); + expect(event.preventDefault).not.toHaveBeenCalled(); + expect(element.submitForm).not.toHaveBeenCalled(); + }); + + test('prevents a native submit event when validation fails', () => { + const event = { preventDefault: jest.fn() }; + const element = createElement(false); + + submit(element, event); + + expect(element.onValidate).toHaveBeenCalledWith({}, event); + expect(event.preventDefault).toHaveBeenCalled(); + expect(element.submitForm).not.toHaveBeenCalled(); + }); + + test('re-submits an async valid native event with the original submitter', () => { + const submitter = {}; + const form = { requestSubmit: jest.fn() }; + const event = { preventDefault: jest.fn(), submitter }; + const element = createElement(true, form); + window.FpJsFormValidator.ajax.queue = 1; + + submit(element, event); + + expect(event.preventDefault).toHaveBeenCalledTimes(1); + expect(window.FpJsFormValidator.ajax.callbacks).toHaveLength(1); + expect(form.requestSubmit).not.toHaveBeenCalled(); + + window.FpJsFormValidator.ajax.queue = 0; + window.FpJsFormValidator.ajax.callbacks[0](); + + expect(form.__fpJsFormValidatorSubmitting).toBe(true); + expect(form.requestSubmit).toHaveBeenCalledWith(submitter); + expect(element.submitForm).not.toHaveBeenCalled(); + }); + + test('allows a guarded re-submitted event to continue without validating again', () => { + const form = { __fpJsFormValidatorSubmitting: true }; + const event = { preventDefault: jest.fn() }; + const element = createElement(true, form); + + submit(element, event); + + expect(form.__fpJsFormValidatorSubmitting).toBeUndefined(); + expect(element.validateRecursively).not.toHaveBeenCalled(); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); +});