diff --git a/app/assets/javascripts/preventDuplicateFormSubmissions.js b/app/assets/javascripts/preventDuplicateFormSubmissions.js index 0eca28039..61e2e6890 100644 --- a/app/assets/javascripts/preventDuplicateFormSubmissions.js +++ b/app/assets/javascripts/preventDuplicateFormSubmissions.js @@ -4,7 +4,7 @@ let disableSubmitButtons = function(event) { - $submitButton = $(this).find(':submit'); + var $submitButton = $(this).find(':submit'); if ($submitButton.data('clicked') == 'true') { diff --git a/tests/javascripts/preventDuplicateFormSubmissions.test.js b/tests/javascripts/preventDuplicateFormSubmissions.test.js new file mode 100644 index 000000000..181b78853 --- /dev/null +++ b/tests/javascripts/preventDuplicateFormSubmissions.test.js @@ -0,0 +1,68 @@ +const helpers = require('./support/helpers.js'); + +beforeAll(() => { + jest.useFakeTimers(); +}); + +afterAll(() => { + require('./support/teardown.js'); +}); + +describe('Prevent duplicate form submissions', () => { + + let form; + let button; + let formSubmitSpy; + + beforeEach(() => { + + // set up DOM + document.body.innerHTML = ` +
`; + + form = document.querySelector('form'); + button = document.querySelector('button'); + + // requires a helper due to JSDOM not implementing the submit method + formSubmitSpy = helpers.spyOnFormSubmit(jest, form); + + require('../../app/assets/javascripts/preventDuplicateFormSubmissions.js'); + + }); + + afterEach(() => { + + document.body.innerHTML = ''; + + // we run the previewPane.js script every test + // the module cache needs resetting each time for the script to execute + jest.resetModules(); + + formSubmitSpy.mockClear(); + + }); + + test("It should prevent any clicks of the 'submit' button after the first one submitting the form", () => { + + helpers.triggerEvent(button, 'click'); + helpers.triggerEvent(button, 'click'); + + expect(formSubmitSpy.mock.calls.length).toEqual(1); + + }); + + test("It should allow clicks again after 1.5 seconds", () => { + + helpers.triggerEvent(button, 'click'); + + jest.advanceTimersByTime(1500); + + helpers.triggerEvent(button, 'click'); + + expect(formSubmitSpy.mock.calls.length).toEqual(2); + + }); + +}); diff --git a/tests/javascripts/support/helpers.js b/tests/javascripts/support/helpers.js index 620bba003..65c7f6a0d 100644 --- a/tests/javascripts/support/helpers.js +++ b/tests/javascripts/support/helpers.js @@ -3,6 +3,7 @@ const domInterfaces = require('./helpers/dom_interfaces.js'); const html = require('./helpers/html.js'); const elements = require('./helpers/elements.js'); const rendering = require('./helpers/rendering.js'); +const forms = require('./helpers/forms.js'); exports.triggerEvent = events.triggerEvent; exports.clickElementWithMouse = events.clickElementWithMouse; @@ -15,3 +16,4 @@ exports.getRadios = html.getRadios; exports.element = elements.element; exports.WindowMock = rendering.WindowMock; exports.ScreenMock = rendering.ScreenMock; +exports.spyOnFormSubmit = forms.spyOnFormSubmit; diff --git a/tests/javascripts/support/helpers/forms.js b/tests/javascripts/support/helpers/forms.js new file mode 100644 index 000000000..415ac0dbe --- /dev/null +++ b/tests/javascripts/support/helpers/forms.js @@ -0,0 +1,33 @@ + +// helper for spying on the submit method on a form element +// JSDOM's implementation of submit just wraps a 'not implemented' error so we need to mock that to track calls to it +// +// * Remove when JSDOM implements submit on its form elements * +// +// elements in JSDOM have a public API and a private implementation API, only used by internal code +// For form elements, these are instances of the following classes: +// - HTMLFormElement +// - HTMLFormElementImpl +// +// form elements link to their implementation instance via a symbol property +// this spies on the submit method of the implementation instance for a form element and mocks it to prevent 'not implemented' errors +function spyOnFormSubmit (jest, form) { + + const formImplementationSymbols = Object.getOwnPropertySymbols(form).filter( + symbol => form[symbol].constructor.name === 'HTMLFormElementImpl' + ); + + if (!formImplementationSymbols.length) { + throw Error("Error mocking form.submit: symbol reference to HTMLFormElementImpl instance not found on form element"); + } + + const HTMLFormElementImpl = form[formImplementationSymbols[0]]; + + const submitSpy = jest.spyOn(HTMLFormElementImpl, 'submit') + + submitSpy.mockImplementation(); + return submitSpy; + +}; + +exports.spyOnFormSubmit = spyOnFormSubmit;