diff options
Diffstat (limited to 'spec/frontend/dirty_submit')
-rw-r--r-- | spec/frontend/dirty_submit/dirty_submit_collection_spec.js | 22 | ||||
-rw-r--r-- | spec/frontend/dirty_submit/dirty_submit_factory_spec.js | 18 | ||||
-rw-r--r-- | spec/frontend/dirty_submit/dirty_submit_form_spec.js | 97 | ||||
-rw-r--r-- | spec/frontend/dirty_submit/helper.js | 43 |
4 files changed, 180 insertions, 0 deletions
diff --git a/spec/frontend/dirty_submit/dirty_submit_collection_spec.js b/spec/frontend/dirty_submit/dirty_submit_collection_spec.js new file mode 100644 index 00000000000..170d581be23 --- /dev/null +++ b/spec/frontend/dirty_submit/dirty_submit_collection_spec.js @@ -0,0 +1,22 @@ +import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection'; +import { setInputValue, createForm } from './helper'; + +jest.mock('lodash/throttle', () => jest.fn(fn => fn)); + +describe('DirtySubmitCollection', () => { + const testElementsCollection = [createForm(), createForm()]; + const forms = testElementsCollection.map(testElements => testElements.form); + + new DirtySubmitCollection(forms); // eslint-disable-line no-new + + it.each(testElementsCollection)('disables submits until there are changes', testElements => { + const { input, submit } = testElements; + const originalValue = input.value; + + expect(submit.disabled).toBe(true); + setInputValue(input, `${originalValue} changes`); + expect(submit.disabled).toBe(false); + setInputValue(input, originalValue); + expect(submit.disabled).toBe(true); + }); +}); diff --git a/spec/frontend/dirty_submit/dirty_submit_factory_spec.js b/spec/frontend/dirty_submit/dirty_submit_factory_spec.js new file mode 100644 index 00000000000..40843a68582 --- /dev/null +++ b/spec/frontend/dirty_submit/dirty_submit_factory_spec.js @@ -0,0 +1,18 @@ +import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; +import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; +import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection'; +import { createForm } from './helper'; + +describe('DirtySubmitCollection', () => { + it('returns a DirtySubmitForm instance for single form elements', () => { + const { form } = createForm(); + + expect(dirtySubmitFactory(form) instanceof DirtySubmitForm).toBe(true); + }); + + it('returns a DirtySubmitCollection instance for a collection of form elements', () => { + const forms = [createForm().form, createForm().form]; + + expect(dirtySubmitFactory(forms) instanceof DirtySubmitCollection).toBe(true); + }); +}); diff --git a/spec/frontend/dirty_submit/dirty_submit_form_spec.js b/spec/frontend/dirty_submit/dirty_submit_form_spec.js new file mode 100644 index 00000000000..d7f690df1f3 --- /dev/null +++ b/spec/frontend/dirty_submit/dirty_submit_form_spec.js @@ -0,0 +1,97 @@ +import { range as rge, throttle } from 'lodash'; +import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; +import { getInputValue, setInputValue, createForm } from './helper'; + +jest.mock('lodash/throttle', () => jest.fn(fn => fn)); +const lodash = jest.requireActual('lodash'); + +function expectToToggleDisableOnDirtyUpdate(submit, input) { + const originalValue = getInputValue(input); + + expect(submit.disabled).toBe(true); + + setInputValue(input, `${originalValue} changes`); + expect(submit.disabled).toBe(false); + setInputValue(input, originalValue); + expect(submit.disabled).toBe(true); +} + +describe('DirtySubmitForm', () => { + describe('submit button tests', () => { + it('disables submit until there are changes', () => { + const { form, input, submit } = createForm(); + + new DirtySubmitForm(form); // eslint-disable-line no-new + + expectToToggleDisableOnDirtyUpdate(submit, input); + }); + + it('disables submit until there are changes when initializing with a falsy value', () => { + const { form, input, submit } = createForm(); + input.value = ''; + + new DirtySubmitForm(form); // eslint-disable-line no-new + + expectToToggleDisableOnDirtyUpdate(submit, input); + }); + + it('disables submit until there are changes for radio inputs', () => { + const { form, input, submit } = createForm('radio'); + + new DirtySubmitForm(form); // eslint-disable-line no-new + + expectToToggleDisableOnDirtyUpdate(submit, input); + }); + + it('disables submit until there are changes for checkbox inputs', () => { + const { form, input, submit } = createForm('checkbox'); + + new DirtySubmitForm(form); // eslint-disable-line no-new + + expectToToggleDisableOnDirtyUpdate(submit, input); + }); + }); + + describe('throttling tests', () => { + beforeEach(() => { + throttle.mockImplementation(lodash.throttle); + jest.useFakeTimers(); + }); + + afterEach(() => { + throttle.mockReset(); + }); + + it('throttles updates when rapid changes are made to a single form element', () => { + const { form, input } = createForm(); + const updateDirtyInputSpy = jest.spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); + + rge(10).forEach(i => { + setInputValue(input, `change ${i}`, false); + }); + + jest.runOnlyPendingTimers(); + + expect(updateDirtyInputSpy).toHaveBeenCalledTimes(1); + }); + + it('does not throttle updates when rapid changes are made to different form elements', () => { + const form = document.createElement('form'); + const range = rge(10); + range.forEach(i => { + form.innerHTML += `<input type="text" name="input-${i}" class="js-input-${i}"/>`; + }); + + const updateDirtyInputSpy = jest.spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); + + range.forEach(i => { + const input = form.querySelector(`.js-input-${i}`); + setInputValue(input, `change`, false); + }); + + jest.runOnlyPendingTimers(); + + expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length); + }); + }); +}); diff --git a/spec/frontend/dirty_submit/helper.js b/spec/frontend/dirty_submit/helper.js new file mode 100644 index 00000000000..c02512b7671 --- /dev/null +++ b/spec/frontend/dirty_submit/helper.js @@ -0,0 +1,43 @@ +function isCheckableType(type) { + return /^(radio|checkbox)$/.test(type); +} + +export function setInputValue(element, value) { + const { type } = element; + let eventType; + + if (isCheckableType(type)) { + element.checked = !element.checked; + eventType = 'change'; + } else { + element.value = value; + eventType = 'input'; + } + + element.dispatchEvent( + new Event(eventType, { + bubbles: true, + }), + ); +} + +export function getInputValue(input) { + return isCheckableType(input.type) ? input.checked : input.value; +} + +export function createForm(type = 'text') { + const form = document.createElement('form'); + form.innerHTML = ` + <input type="${type}" name="${type}" class="js-input"/> + <button type="submit" class="js-dirty-submit"></button> + `; + + const input = form.querySelector('.js-input'); + const submit = form.querySelector('.js-dirty-submit'); + + return { + form, + input, + submit, + }; +} |