diff --git a/src/Formulario.ts b/src/Formulario.ts index e74b6f5..e2f83a6 100644 --- a/src/Formulario.ts +++ b/src/Formulario.ts @@ -10,7 +10,6 @@ import merge from '@/utils/merge' import FileUpload from '@/FileUpload' import FormularioForm from '@/FormularioForm.vue' -import FormularioFormInterface from '@/FormularioFormInterface' import FormularioInput from '@/FormularioInput.vue' import FormularioGrouping from '@/FormularioGrouping.vue' @@ -35,7 +34,6 @@ interface FormularioOptions { */ export default class Formulario { public options: FormularioOptions - public registry: Map public idRegistry: { [name: string]: number } constructor () { @@ -54,7 +52,6 @@ export default class Formulario { validationMessages: messages, idPrefix: 'formulario-' } - this.registry = new Map() this.idRegistry = {} } @@ -116,68 +113,6 @@ export default class Formulario { } } - /** - * Given an instance of a FormularioForm register it. - */ - register (form: FormularioFormInterface): void { - if (typeof form.name === 'string') { - this.registry.set(form.name, form) - } - } - - /** - * Given an instance of a form, remove it from the registry. - */ - deregister (form: FormularioFormInterface): void { - if (typeof form.name === 'string' && this.registry.has(form.name)) { - this.registry.delete(form.name) - } - } - - /** - * Given an array, this function will attempt to make sense of the given error - * and hydrate a form with the resulting errors. - */ - handle ({ formErrors, inputErrors }: { - formErrors?: string[]; - inputErrors?: Record; - }, formName: string): void { - if (this.registry.has(formName)) { - const form = this.registry.get(formName) as FormularioFormInterface - - form.loadErrors({ - formErrors: formErrors || [], - inputErrors: inputErrors || {} - }) - } - } - - /** - * Reset a form. - */ - reset (formName: string, initialValue: Record = {}): void { - this.resetValidation(formName) - this.setValues(formName, initialValue) - } - - /** - * Reset the form's validation messages. - */ - resetValidation (formName: string): void { - if (this.registry.has(formName)) { - (this.registry.get(formName) as FormularioFormInterface).resetValidation() - } - } - - /** - * Set the form values. - */ - setValues (formName: string, values?: Record): void { - if (this.registry.has(formName) && values) { - (this.registry.get(formName) as FormularioFormInterface).setValues({ ...values }) - } - } - /** * Get the file uploader. */ diff --git a/src/FormularioForm.vue b/src/FormularioForm.vue index cfc9058..dc6448f 100644 --- a/src/FormularioForm.vue +++ b/src/FormularioForm.vue @@ -27,10 +27,8 @@ import { ValidationErrorBag } from '@/validation/types' import FileUpload from '@/FileUpload' -import FormularioFormInterface from '@/FormularioFormInterface' - @Component({ name: 'FormularioForm' }) -export default class FormularioForm extends Vue implements FormularioFormInterface { +export default class FormularioForm extends Vue { @Provide() formularioFieldValidation (errorBag: ValidationErrorBag): void { this.$emit('validation', errorBag) } @@ -133,14 +131,9 @@ export default class FormularioForm extends Vue implements FormularioFormInterfa } created (): void { - this.$formulario.register(this) this.initProxy() } - destroyed (): void { - this.$formulario.deregister(this) - } - onFormSubmit (): Promise { this.childrenShouldShowErrors = true this.registry.forEach((input: FormularioInput) => { @@ -183,12 +176,6 @@ export default class FormularioForm extends Vue implements FormularioFormInterfa this.registry.remove(field) } - loadErrors ({ formErrors, inputErrors }: { formErrors: string[]; inputErrors: Record }): void { - // given an object of errors, apply them to this form - this.localFormErrors = formErrors - this.localFieldErrors = inputErrors - } - resetValidation (): void { this.localFormErrors = [] this.localFieldErrors = {} @@ -278,5 +265,11 @@ export default class FormularioForm extends Vue implements FormularioFormInterfa this.$emit('input', { ...this.proxy }) } } + + setErrors ({ formErrors, inputErrors }: { formErrors?: string[]; inputErrors?: Record }): void { + // given an object of errors, apply them to this form + this.localFormErrors = formErrors || [] + this.localFieldErrors = inputErrors || {} + } } diff --git a/src/FormularioFormInterface.ts b/src/FormularioFormInterface.ts deleted file mode 100644 index a9f366e..0000000 --- a/src/FormularioFormInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface FormularioFormInterface { - name: string | boolean; - $options: Record; - setValues(values: Record): void; - loadErrors ({ formErrors, inputErrors }: { formErrors: string[]; inputErrors: Record }): void; - resetValidation (): void; -} diff --git a/src/FormularioInput.vue b/src/FormularioInput.vue index bd67336..ac422a0 100644 --- a/src/FormularioInput.vue +++ b/src/FormularioInput.vue @@ -305,12 +305,6 @@ export default class FormularioInput extends Vue { } } - get validators (): any { - return createValidatorGroups( - parseRules(this.validation, this.$formulario.rules(this.parsedValidationRules)) - ) - } - performValidation (): Promise { this.pendingValidation = this.validate().then(errors => { this.didValidate(errors) @@ -348,7 +342,9 @@ export default class FormularioInput extends Vue { resolve([]) } } - resolveGroups(this.validators) + resolveGroups(createValidatorGroups( + parseRules(this.validation, this.$formulario.rules(this.parsedValidationRules)) + )) }) } diff --git a/test/unit/FormularioForm.test.js b/test/unit/FormularioForm.test.js index 6c7871b..9d3e287 100644 --- a/test/unit/FormularioForm.test.js +++ b/test/unit/FormularioForm.test.js @@ -38,161 +38,128 @@ describe('FormularioForm', () => { expect(spy).toHaveBeenCalled() }) - it('Registers its subcomponents', () => { + it('Adds subcomponents to the registry', () => { const wrapper = mount(FormularioForm, { - propsData: { formularioValue: { testinput: 'has initial value' } }, + propsData: { formularioValue: {} }, slots: { default: ` - - + + ` } }) - expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2']) + expect(wrapper.vm.registry.keys()).toEqual(['sub1', 'sub2']) }) - it('deregisters a subcomponents', async () => { + it('Removes subcomponents from the registry', async () => { const wrapper = mount({ - data () { - return { - active: true - } - }, + data: () => ({ active: true }), template: ` - - + + ` }) await flushPromises() - expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['subinput1', 'subinput2']) + expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['sub1', 'sub2']) wrapper.setData({ active: false }) await flushPromises() - expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['subinput2']) + expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['sub2']) }) - it('can set a field’s initial value', async () => { + it('Can set a field’s initial value', async () => { const wrapper = mount(FormularioForm, { - propsData: { formularioValue: { testinput: 'has initial value' } }, - slots: { default: ` - - - - ` } + propsData: { formularioValue: { test: 'Has initial value' } }, + slots: { + default: ` + + + + ` + } }) await flushPromises() - expect(wrapper.find('input').element.value).toBe('has initial value') + expect(wrapper.find('input').element['value']).toBe('Has initial value') }) - it('lets individual fields override form initial value', () => { + it('Lets individual fields override form initial value', () => { const wrapper = mount(FormularioForm, { - propsData: { formularioValue: { testinput: 'has initial value' } }, - slots: { default: ` - - - - ` } + propsData: { formularioValue: { test: 'has initial value' } }, + slots: { + default: ` + + + + ` + } }) - expect(wrapper.find('input').element.value).toBe('123') + expect(wrapper.find('input').element['value']).toBe('123') }) - it('lets fields set form initial value with value prop', () => { + it('Lets fields set form initial value with value prop', () => { const wrapper = mount({ - data () { - return { - formValues: {} - } - }, - template: ` - - ` - }) - expect(wrapper.vm.formValues).toEqual({ name: '123' }) - }) - - it('receives updates to form model when individual fields are edited', () => { - const wrapper = mount({ - data () { - return { - formValues: { - testinput: '', - } - } - }, + data: () => ({ values: {} }), template: ` - - - + + + + ` + }) + expect(wrapper.vm['values']).toEqual({ test: '123' }) + }) + + it('Receives updates to form model when individual fields are edited', () => { + const wrapper = mount({ + data: () => ({ values: { test: '' } }), + template: ` + + + ` }) - wrapper.find('input').setValue('edited value') - expect(wrapper.vm.formValues).toEqual({ testinput: 'edited value' }) + wrapper.find('input').setValue('Edited value') + expect(wrapper.vm['values']).toEqual({ test: 'Edited value' }) }) - it('field data updates when it is type of date', async () => { + it('Field data updates when it is type of date', async () => { const wrapper = mount({ - data () { - return { - formValues: { - testdate: new Date(123), - } - } - }, + data: () => ({ formValues: { date: new Date(123) } }), template: ` - - {{ inputProps.context.model.getTime() }} + + {{ context.model.getTime() }} ` }) + expect(wrapper.find('span').text()).toBe('123') - wrapper.setData({ formValues: { testdate: new Date(234) } }) + wrapper.setData({ formValues: { date: new Date(234) } }) await flushPromises() expect(wrapper.find('span').text()).toBe('234') }) - // =========================================================================== - /** - * @todo in vue-test-utils version 1.0.0-beta.29 has some bugs related to - * synchronous updating. Some details are here: - * - * @update this test was re-implemented in version 1.0.0-beta.31 and seems to - * be workign now with flushPromises(). Leaving these docs here for now. - * - * https://github.com/vuejs/vue-test-utils/issues/1130 - * - * This test is being commented out until there is a resolution on this issue, - * and instead being replaced with a mock call. - */ - - it('updates initial form values when input contains a populated v-model', async () => { + it('Updates initial form values when input contains a populated v-model', async () => { const wrapper = mount({ - data () { - return { - formValues: { - testinput: '', - }, - fieldValue: '123' - } - }, + data: () => ({ + formValues: { test: '' }, + fieldValue: '123', + }), template: ` - + ` }) await flushPromises() - expect(wrapper.vm.formValues).toEqual({ testinput: '123' }) + expect(wrapper.vm['formValues']).toEqual({ test: '123' }) }) - // =========================================================================== - // Replacement test for the above test - not quite as good of a test. it('updates calls setFieldValue on form when a field contains a populated v-model on registration', () => { const wrapper = mount(FormularioForm, { @@ -208,25 +175,19 @@ describe('FormularioForm', () => { it('updates an inputs value when the form v-model is modified', async () => { const wrapper = mount({ - data () { - return { - formValues: { - testinput: 'abcd', - } - } - }, + data: () => ({ formValues: { test: 'abcd' } }), template: ` - - + + ` }) await flushPromises() - wrapper.vm.formValues = { testinput: '1234' } + wrapper.vm.formValues = { test: '1234' } await flushPromises() - expect(wrapper.find('input[type="text"]').element.value).toBe('1234') + expect(wrapper.find('input[type="text"]').element['value']).toBe('1234') }) it('resolves hasValidationErrors to true', async () => { @@ -266,64 +227,12 @@ describe('FormularioForm', () => { ` } }) await flushPromises() - expect(wrapper.find('input[type="text"]').element.value).toBe('Dave Barnett') + expect(wrapper.find('input[type="text"]').element['value']).toBe('Dave Barnett') }) - it('automatically registers with root plugin', async () => { + it('Receives a form-errors prop and displays it', async () => { const wrapper = mount(FormularioForm, { - propsData: { formularioValue: { box3: [] }, name: 'login' } - }) - expect(wrapper.vm.$formulario.registry.has('login')).toBe(true) - expect(wrapper.vm.$formulario.registry.get('login')).toBe(wrapper.vm) - }) - - it('Calls custom error handler with error and name', async () => { - const wrapper = mount({ - template: ` -
- - -
- ` - }) - wrapper.vm.$formulario.handle({ formErrors: ['This is an error message'] }, 'login') - }) - - it('Errors are displayed on correctly named components', async () => { - const wrapper = mount({ - template: ` -
- - - {{ error }} - -
- ` - }) - expect( - wrapper.vm.$formulario.registry.has('login') && - wrapper.vm.$formulario.registry.has('register') - ).toBe(true) - wrapper.vm.$formulario.handle({ formErrors: ['This is an error message'] }, 'login') - await flushPromises() - expect(wrapper.findAll('.form').length).toBe(2) - expect(wrapper.find('.form--login .error').exists()).toBe(true) - expect(wrapper.find('.form--register .error').exists()).toBe(false) - }) - - it('receives a form-errors prop and displays it', async () => { - const wrapper = mount(FormularioForm, { - propsData: { name: 'main', formErrors: ['first', 'second'] }, + propsData: { formErrors: ['first', 'second'] }, scopedSlots: { default: `
@@ -334,18 +243,18 @@ describe('FormularioForm', () => { } }) await flushPromises() - expect(wrapper.vm.$formulario.registry.get('main').mergedFormErrors.length).toBe(2) + expect(wrapper.vm.mergedFormErrors.length).toBe(2) }) - it('it aggregates form-errors prop with form-named errors', async () => { + it('Aggregates form-errors prop with form-named errors', async () => { const wrapper = mount(FormularioForm, { - propsData: { formErrors: ['first', 'second'], name: 'login' } + propsData: { formErrors: ['first', 'second'] } }) - wrapper.vm.$formulario.handle({ formErrors: ['third'] }, 'login') + wrapper.vm.setErrors({ formErrors: ['third'] }) + await flushPromises() - let errors = wrapper.vm.$formulario.registry.get('login').mergedFormErrors - expect(Object.keys(errors).length).toBe(3) + expect(Object.keys(wrapper.vm.mergedFormErrors).length).toBe(3) }) it('displays field errors on inputs with errors prop', async () => { @@ -365,76 +274,75 @@ describe('FormularioForm', () => { expect(wrapper.find('span').text()).toEqual('This field has an error') }) + it('Is able to display multiple errors on multiple elements', async () => { + const wrapper = mount(FormularioForm, { + propsData: { + errors: { inputA: ['first'], inputB: ['first', 'second']}, + }, + slots: { + default: ` + + + ` + }, + }) + + await wrapper.vm.$nextTick() + + expect(Object.keys(wrapper.vm.mergedFieldErrors).length).toBe(2) + expect(wrapper.vm.mergedFieldErrors.inputA.length).toBe(1) + expect(wrapper.vm.mergedFieldErrors.inputB.length).toBe(2) + }) + + it('Can set multiple field errors with setErrors()', async () => { + const wrapper = mount(FormularioForm, { + slots: { + default: ` + + + ` + }, + }) + + expect(Object.keys(wrapper.vm.mergedFieldErrors).length).toBe(0) + + wrapper.vm.setErrors({ + inputErrors: { + inputA: ['first'], + inputB: ['first', 'second'], + } + }) + + await wrapper.vm.$nextTick() + await flushPromises() + + expect(Object.keys(wrapper.vm.mergedFieldErrors).length).toBe(2) + expect(wrapper.vm.mergedFieldErrors.inputA.length).toBe(1) + expect(wrapper.vm.mergedFieldErrors.inputB.length).toBe(2) + }) + return - it('is able to display multiple errors on multiple elements', async () => { - const wrapper = mount({ - template: ` - - - - - - ` - }) - await wrapper.vm.$nextTick() - - let errors = wrapper.vm.$formulario.registry.get('register').mergedFieldErrors - expect(Object.keys(errors).length).toBe(3) - expect(errors.inputA.length).toBe(2) - expect(errors.inputB.length).toBe(1) - expect(errors.inputC.length).toBe(1) - }) - - it('it can set multiple field errors with handle()', async () => { - const wrapper = mount({ - template: ` - - - - - - ` - }) - - let errors = wrapper.vm.$formulario.registry.get('register').mergedFieldErrors - expect(Object.keys(errors).length).toBe(0) - - wrapper.vm.$formulario.handle({ inputErrors: {inputA: ['first', 'second'], inputB: 'only one here', inputC: ['and one here']} }, "register") - await wrapper.vm.$nextTick() - await flushPromises() - - errors = wrapper.vm.$formulario.registry.get('register').mergedFieldErrors - expect(Object.keys(errors).length).toBe(3) - expect(errors.inputA.length).toBe(2) - expect(errors.inputB.length).toBe(1) - expect(errors.inputC.length).toBe(1) - - }) - - it('emits correct validation event on entry', async () => { + it('Emits correct validation event on entry', async () => { const wrapper = mount(FormularioForm, { slots: { default: ` -
- - - - -
+ + + + ` } }) - wrapper.find('input[type="text"]').setValue('foo') + wrapper.find('input[type="text"]').setValue('bar') + await flushPromises() + const errorObjects = wrapper.emitted('validation') // There should be 3 events, both inputs mounting, and the value being set removing required on testinput expect(errorObjects.length).toBe(3) // this should be the event from the setValue() const errorObject = errorObjects[2][0] expect(errorObject).toEqual({ - name: 'testinput', + name: 'foo', errors: [ expect.any(String) ], @@ -490,41 +398,41 @@ describe('FormularioForm', () => { expect(wrapper.vm.formData).toEqual({ foo: 'bar' }) }) - it('it allows resetting a form, hiding validation and clearing inputs.', async () => { + it('Allows resetting a form, hiding validation and clearing inputs.', async () => { const wrapper = mount({ + data: () => ({ values: {} }), template: ` - - + + - - + + `, - data () { - return { - formData: {} - } - } }) + const password = wrapper.find('input[type="password"]') password.setValue('foo') password.trigger('blur') + wrapper.find('form').trigger('submit') - wrapper.vm.$formulario.handle({ - inputErrors: { username: ['Failed'] } - }, 'login') + wrapper.vm.$refs.form.setErrors({ inputErrors: { username: ['Failed'] } }) + await flushPromises() - // First make sure we caugth the errors + + // First make sure we caught the errors expect(Object.keys(wrapper.vm.$refs.form.mergedFieldErrors).length).toBe(1) - wrapper.vm.$formulario.reset('login') + wrapper.vm.$refs.form.resetValidation() + wrapper.vm.$refs.form.setValues({ }) + await flushPromises() expect(Object.keys(wrapper.vm.$refs.form.mergedFieldErrors).length).toBe(0) - expect(wrapper.vm.formData).toEqual({}) + expect(wrapper.vm.values).toEqual({}) }) })