diff --git a/test/unit/Formulario.test.js b/test/unit/Formulario.test.js
new file mode 100644
index 0000000..9b8d479
--- /dev/null
+++ b/test/unit/Formulario.test.js
@@ -0,0 +1,95 @@
+import Formulario from '@/Formulario.js'
+
+describe('Formulario', () => {
+ it('can merge simple object', () => {
+ let a = {
+ optionA: true,
+ optionB: '1234'
+ }
+ let b = {
+ optionA: false
+ }
+ expect(Formulario.merge(a, b)).toEqual({
+ optionA: false,
+ optionB: '1234'
+ })
+ })
+
+ it('can add to simple array', () => {
+ let a = {
+ optionA: true,
+ optionB: ['first', 'second']
+ }
+ let b = {
+ optionB: ['third']
+ }
+ expect(Formulario.merge(a, b, true)).toEqual({
+ optionA: true,
+ optionB: ['first', 'second', 'third']
+ })
+ })
+
+ it('can merge recursively', () => {
+ let a = {
+ optionA: true,
+ optionC: {
+ first: '123',
+ third: {
+ a: 'b'
+ }
+ },
+ optionB: '1234'
+ }
+ let b = {
+ optionB: '567',
+ optionC: {
+ first: '1234',
+ second: '789',
+ }
+ }
+ expect(Formulario.merge(a, b)).toEqual({
+ optionA: true,
+ optionC: {
+ first: '1234',
+ third: {
+ a: 'b'
+ },
+ second: '789'
+ },
+ optionB: '567'
+ })
+ })
+
+ it('installs on vue instance', () => {
+ const components = [
+ 'FormularioForm',
+ 'FormularioInput',
+ 'FormularioGrouping',
+ ]
+ const registry = []
+ function Vue () {}
+ Vue.component = function (name, instance) {
+ registry.push(name)
+ }
+ Formulario.install(Vue, { extended: true })
+ expect(Vue.prototype.$formulario).toBe(Formulario)
+ expect(registry).toEqual(components)
+ })
+
+ it('can extend instance in a plugin', () => {
+ function Vue () {}
+ Vue.component = function (name, instance) {}
+ const plugin = function (i) {
+ i.extend({
+ rules: {
+ testRule: () => false
+ }
+ })
+ }
+ Formulario.install(Vue, {
+ plugins: [ plugin ]
+ })
+
+ expect(typeof Vue.prototype.$formulario.options.rules.testRule).toBe('function')
+ })
+})
diff --git a/test/unit/FormularioForm.test.js b/test/unit/FormularioForm.test.js
new file mode 100644
index 0000000..1ef9b20
--- /dev/null
+++ b/test/unit/FormularioForm.test.js
@@ -0,0 +1,527 @@
+import Vue from 'vue'
+import { mount, shallowMount } from '@vue/test-utils'
+import flushPromises from 'flush-promises'
+import Formulario from '../../src/Formulario.js'
+import FormSubmission from '../../src/FormSubmission.js'
+import FormularioForm from '@/FormularioForm.vue'
+import FormularioInput from '@/FormularioInput.vue'
+
+Vue.use(Formulario)
+
+describe('FormularioForm', () => {
+ it('render a form DOM element', () => {
+ const wrapper = mount(FormularioForm)
+ expect(wrapper.find('form').exists()).toBe(true)
+ })
+
+ it('accepts a default slot', () => {
+ const wrapper = mount(FormularioForm, {
+ slots: {
+ default: '
'
+ }
+ })
+ expect(wrapper.find('form div.default-slot-item').exists()).toBe(true)
+ })
+
+ it('intercepts submit event', () => {
+ const formSubmitted = jest.fn()
+ const wrapper = mount(FormularioForm, {
+ slots: {
+ default: ""
+ }
+ })
+ const spy = jest.spyOn(wrapper.vm, 'formSubmitted')
+ wrapper.find('form').trigger('submit')
+ expect(spy).toHaveBeenCalled()
+ })
+
+ it('registers its subcomponents', () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { formularioValue: { testinput: 'has initial value' } },
+ slots: { default: '' }
+ })
+ expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
+ })
+
+ it('deregisters a subcomponents', async () => {
+ const wrapper = mount({
+ data () {
+ return {
+ active: true
+ }
+ },
+ template: `
+
+
+
+
+ `
+ })
+ await flushPromises()
+ expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
+ wrapper.setData({ active: false })
+ await flushPromises()
+ expect(wrapper.findComponent(FormularioForm).vm.registry.keys()).toEqual(['subinput2'])
+ })
+
+ it('can set a field’s initial value', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { formularioValue: { testinput: 'has initial value' } },
+ slots: { default: `
+
+
+
+ ` }
+ })
+ await flushPromises()
+ expect(wrapper.find('input').element.value).toBe('has initial value')
+ })
+
+ it('lets individual fields override form initial value', () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { formularioValue: { testinput: 'has initial value' } },
+ slots: { default: `
+
+
+
+ ` }
+ })
+ expect(wrapper.find('input').element.value).toBe('123')
+ })
+
+ 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: '',
+ }
+ }
+ },
+ template: `
+
+
+
+
+
+ `
+ })
+ wrapper.find('input').setValue('edited value')
+ expect(wrapper.vm.formValues).toEqual({ testinput: 'edited value' })
+ })
+
+
+
+ // ===========================================================================
+ /**
+ * @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 () => {
+ const wrapper = mount({
+ data () {
+ return {
+ formValues: {
+ testinput: '',
+ },
+ fieldValue: '123'
+ }
+ },
+ template: `
+
+
+
+ `
+ })
+ await flushPromises()
+ expect(wrapper.vm.formValues).toEqual({ testinput: '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, {
+ propsData: {
+ formularioValue: { testinput: '123' }
+ },
+ slots: {
+ default: ''
+ }
+ })
+ expect(wrapper.emitted().input[wrapper.emitted().input.length - 1]).toEqual([{ testinput: 'override-data' }])
+ })
+
+ it('updates an inputs value when the form v-model is modified', async () => {
+ const wrapper = mount({
+ data () {
+ return {
+ formValues: {
+ testinput: 'abcd',
+ }
+ }
+ },
+ template: `
+
+
+
+
+
+ `
+ })
+ await flushPromises()
+ wrapper.vm.formValues = { testinput: '1234' }
+ await flushPromises()
+ expect(wrapper.find('input[type="text"]').element.value).toBe('1234')
+ })
+
+ it('emits an instance of FormSubmission', async () => {
+ const wrapper = mount(FormularioForm, {
+ slots: { default: '' }
+ })
+ wrapper.find('form').trigger('submit')
+ await flushPromises()
+ expect(wrapper.emitted('submit-raw')[0][0]).toBeInstanceOf(FormSubmission)
+ })
+
+ it('resolves hasValidationErrors to true', async () => {
+ const wrapper = mount(FormularioForm, {
+ slots: { default: '' }
+ })
+ wrapper.find('form').trigger('submit')
+ await flushPromises()
+ const submission = wrapper.emitted('submit-raw')[0][0]
+ expect(await submission.hasValidationErrors()).toBe(true)
+ })
+
+ it('resolves submitted form values to an object', async () => {
+ const wrapper = mount(FormularioForm, {
+ slots: { default: '' }
+ })
+ const submission = await wrapper.vm.formSubmitted()
+ expect(submission).toEqual({testinput: 'Justin'})
+ })
+
+ it('accepts a values prop and uses it to set the initial values', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { values: { name: 'Dave Barnett', candy: true } },
+ slots: { default: `
+
+
+
+
+ ` }
+ })
+ await flushPromises()
+ expect(wrapper.find('input[type="text"]').element.value).toBe('Dave Barnett')
+ })
+
+ it('automatically registers with root plugin', 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 mockHandler = jest.fn((err, name) => err);
+ const wrapper = mount({
+ template: `
+
+
+
+
+ `
+ })
+ wrapper.vm.$formulario.extend({ errorHandler: mockHandler })
+ wrapper.vm.$formulario.handle({ formErrors: ['This is an error message'] }, 'login')
+ expect(mockHandler.mock.calls.length).toBe(1);
+ expect(mockHandler.mock.calls[0]).toEqual([{ formErrors: ['This is an error message'] }, 'login']);
+ })
+
+ it('errors are displayed on correctly named components', async () => {
+ const wrapper = mount({
+ template: `
+
+
+ {{ error }}
+
+
+ {{ 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('.formulario-form').length).toBe(2)
+ expect(wrapper.find('.formulario-form--login span').exists()).toBe(true)
+ expect(wrapper.find('.formulario-form--register span').exists()).toBe(false)
+ })
+
+ it('errors are displayed on correctly named components', async () => {
+ const wrapper = mount({
+ template: `
+
+
+ {{ error }}
+
+
+ {{ 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('.formulario-form').length).toBe(2)
+ expect(wrapper.find('.formulario-form--login span').exists()).toBe(true)
+ expect(wrapper.find('.formulario-form--register span').exists()).toBe(false)
+ })
+
+ it('receives a form-errors prop and displays it', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { name: 'main', formErrors: ['first', 'second'] },
+ scopedSlots: {
+ default: `
+
+ {{ error }}
+
+
+ `
+ }
+ })
+ await flushPromises()
+ expect(wrapper.vm.$formulario.registry.get('main').mergedFormErrors.length).toBe(2)
+ })
+
+ it('it aggregates form-errors prop with form-named errors', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { formErrors: ['first', 'second'], name: 'login' }
+ })
+ wrapper.vm.$formulario.handle({ formErrors: ['third'] }, 'login')
+ await flushPromises()
+
+ let errors = wrapper.vm.$formulario.registry.get('login').mergedFormErrors
+ expect(Object.keys(errors).length).toBe(3)
+ })
+
+ it('displays field errors on inputs with errors prop', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { errors: { sipple: ['This field has an error'] }},
+ slots: {
+ default: `
+
+ {{ error }}
+
+ `
+ }
+ })
+ await wrapper.vm.$nextTick()
+
+ expect(wrapper.find('span').exists()).toBe(true)
+ })
+
+ 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 () => {
+ const wrapper = mount(FormularioForm, {
+ slots: { default: `
+
+
+
+
+
+
+ ` }
+ })
+ wrapper.find('input[type="text"]').setValue('foo')
+ 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',
+ errors: [
+ expect.any(String)
+ ],
+ hasErrors: true
+ })
+ })
+
+ it('emits correct validation event when no errors', async () => {
+ const wrapper = mount(FormularioForm, {
+ slots: { default: `
+
+
+
+
+
+
+ ` }
+ })
+ wrapper.find('input[type="text"]').setValue('bar')
+ await flushPromises()
+ const errorObjects = wrapper.emitted('validation')
+ expect(errorObjects.length).toBe(3)
+ const errorObject = errorObjects[2][0]
+ expect(errorObject).toEqual({
+ name: 'testinput',
+ errors: [],
+ hasErrors: false
+ })
+ })
+
+ it('removes field data when that field is de-registered', async () => {
+ const wrapper = mount({
+ template: `
+
+
+
+
+
+
+ `,
+ data () {
+ return {
+ formData: {}
+ }
+ }
+ })
+ await flushPromises()
+ wrapper.find('input[type="text"]').setValue('bar')
+ await flushPromises()
+ expect(wrapper.findComponent(FormularioForm).vm.proxy).toEqual({ foo: 'bar' })
+ expect(wrapper.vm.formData).toEqual({ foo: 'bar' })
+ })
+
+ it('it allows resetting a form, hiding validation and clearing inputs.', async () => {
+ const wrapper = mount({
+ 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')
+ await flushPromises()
+ // First make sure we caugth the errors
+ expect(Object.keys(wrapper.vm.$refs.form.mergedFieldErrors).length).toBe(1)
+ wrapper.vm.$formulario.reset('login')
+ await flushPromises()
+ expect(Object.keys(wrapper.vm.$refs.form.mergedFieldErrors).length).toBe(0)
+ expect(wrapper.vm.formData).toEqual({})
+ })
+})
diff --git a/test/unit/FormularioGrouping.test.js b/test/unit/FormularioGrouping.test.js
new file mode 100644
index 0000000..f1ab1be
--- /dev/null
+++ b/test/unit/FormularioGrouping.test.js
@@ -0,0 +1,91 @@
+import Vue from 'vue'
+import { mount } from '@vue/test-utils'
+import flushPromises from 'flush-promises'
+import Formulario from '@/Formulario.js'
+import FormularioInput from '@/FormularioInput.vue'
+import FormularioForm from '@/FormularioForm.vue'
+import FormularioGrouping from '@/FormularioGrouping.vue'
+
+Vue.use(Formulario)
+
+describe('FormularioGrouping', () => {
+ it('grouped fields to be setted', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { name: 'form', },
+ slots: {
+ default: `
+
+
+
+
+
+ `
+ }
+ })
+ expect(wrapper.findAll('input[type="text"]').length).toBe(1)
+ wrapper.find('input[type="text"]').setValue('test')
+
+ const submission = await wrapper.vm.formSubmitted()
+ expect(submission).toEqual({sub: {text: 'test'}})
+ })
+
+ it('grouped fields to be getted', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: { name: 'form', formularioValue: { sub: {text: 'initial value'}, text: 'simple text' } },
+ slots: {
+ default: `
+
+
+
+
+
+ `
+ }
+ })
+ expect(wrapper.find('input[type="text"]').element.value).toBe('initial value')
+ })
+
+ it('data reactive with grouped fields', async () => {
+ const wrapper = mount({
+ data () {
+ return {
+ formValues: {}
+ }
+ },
+ template: `
+
+
+
+
+ {{ formValues.sub.text }}
+
+
+
+ `
+ })
+ expect(wrapper.find('span').text()).toBe('')
+ wrapper.find('input[type="text"]').setValue('test')
+ await flushPromises()
+ expect(wrapper.find('span').text()).toBe('test')
+ })
+
+ it('errors are setted for grouped fields', async () => {
+ const wrapper = mount({
+ data () {
+ return {
+ formValues: {}
+ }
+ },
+ template: `
+
+
+
+ {{ error }}
+
+
+
+ `
+ })
+ expect(wrapper.findAll('span').length).toBe(1)
+ })
+})
diff --git a/test/unit/FormularioInput.test.js b/test/unit/FormularioInput.test.js
new file mode 100644
index 0000000..1cff883
--- /dev/null
+++ b/test/unit/FormularioInput.test.js
@@ -0,0 +1,259 @@
+import Vue from 'vue'
+import flushPromises from 'flush-promises'
+import { mount, createLocalVue } from '@vue/test-utils'
+import Formulario from '@/Formulario.js'
+import FormularioForm from '@/FormularioForm.vue'
+import FormularioInput from '@/FormularioInput.vue'
+
+const globalRule = jest.fn((context) => { return false })
+
+Vue.use(Formulario, {
+ rules: {
+ globalRule
+ }
+})
+
+describe('FormularioInput', () => {
+ it('allows custom field-rule level validation strings', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required|in:abcdef',
+ validationMessages: {in: 'the value was different than expected'},
+ errorBehavior: 'live',
+ value: 'other value'
+ },
+ scopedSlots: {
+ default: `{{ error }}
`
+ }
+ })
+ await flushPromises()
+ expect(wrapper.find('span').text()).toBe('the value was different than expected')
+ })
+
+ it('allows custom field-rule level validation functions', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required|in:abcdef',
+ validationMessages: { in: ({ value }) => `The string ${value} is not correct.` },
+ errorBehavior: 'live',
+ value: 'other value'
+ },
+ scopedSlots: {
+ default: `{{ error }}
`
+ }
+ })
+ await flushPromises()
+ expect(wrapper.find('span').text()).toBe('The string other value is not correct.')
+ })
+
+ it('uses custom async validation rules on defined on the field', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required|foobar',
+ validationMessages: {
+ foobar: 'failed the foobar check'
+ },
+ validationRules: {
+ foobar: async ({ value }) => value === 'foo'
+ },
+ errorBehavior: 'live',
+ value: 'bar'
+ },
+ scopedSlots: {
+ default: `{{ error }}
`
+ }
+ })
+ await flushPromises()
+ expect(wrapper.find('span').text()).toBe('failed the foobar check')
+ })
+
+ it('uses custom sync validation rules on defined on the field', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required|foobar',
+ validationMessages: {
+ foobar: 'failed the foobar check'
+ },
+ validationRules: {
+ foobar: ({ value }) => value === 'foo'
+ },
+ errorBehavior: 'live',
+ value: 'bar'
+ },
+ scopedSlots: {
+ default: `{{ error }}
`
+ }
+ })
+ await flushPromises()
+ expect(wrapper.find('span').text()).toBe('failed the foobar check')
+ })
+
+ it('uses global custom validation rules', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required|globalRule',
+ errorBehavior: 'live',
+ value: 'bar'
+ }
+ })
+ await flushPromises()
+ expect(globalRule.mock.calls.length).toBe(1)
+ })
+
+ it('emits correct validation event', async () => {
+ const wrapper = mount(FormularioInput, { propsData: {
+ name: 'test',
+ validation: 'required',
+ errorBehavior: 'live',
+ value: '',
+ name: 'testinput',
+ } })
+ await flushPromises()
+ const errorObject = wrapper.emitted('validation')[0][0]
+ expect(errorObject).toEqual({
+ name: 'testinput',
+ errors: [
+ expect.any(String)
+ ],
+ hasErrors: true
+ })
+ })
+
+ it('emits a error-visibility event on blur', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'required',
+ errorBehavior: 'blur',
+ value: '',
+ name: 'testinput',
+ },
+ scopedSlots: {
+ default: ``
+ }
+ })
+ await flushPromises()
+ expect(wrapper.emitted('error-visibility')[0][0]).toBe(false)
+ wrapper.find('input[type="text"]').trigger('blur')
+ await flushPromises()
+ expect(wrapper.emitted('error-visibility')[1][0]).toBe(true)
+ })
+
+ it('emits error-visibility event immediately when live', async () => {
+ const wrapper = mount(FormularioInput, { propsData: {
+ name: 'test',
+ validation: 'required',
+ errorBehavior: 'live',
+ value: '',
+ name: 'testinput',
+ } })
+ await flushPromises()
+ expect(wrapper.emitted('error-visibility').length).toBe(1)
+ })
+
+ it('Does not emit an error-visibility event if visibility did not change', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ name: 'test',
+ validation: 'in:xyz',
+ errorBehavior: 'live',
+ value: 'bar',
+ name: 'testinput',
+ },
+ scopedSlots: {
+ default: ``
+ }
+ })
+ await flushPromises()
+ expect(wrapper.emitted('error-visibility').length).toBe(1)
+ wrapper.find('input[type="text"]').setValue('bar')
+ await flushPromises()
+ expect(wrapper.emitted('error-visibility').length).toBe(1)
+ })
+
+ it('can bail on validation when encountering the bail rule', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: { name: 'test', validation: 'bail|required|in:xyz', errorBehavior: 'live' }
+ })
+ await flushPromises();
+ expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
+ })
+
+ it('can show multiple validation errors if they occur before the bail rule', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: { name: 'test', validation: 'required|in:xyz|bail', errorBehavior: 'live' }
+ })
+ await flushPromises();
+ expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
+ })
+
+ it('can avoid bail behavior by using modifier', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: { name: 'test', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live', value: '123' }
+ })
+ await flushPromises();
+ expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
+ })
+
+ it('prevents later error messages when modified rule fails', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: { name: 'test', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live' }
+ })
+ await flushPromises();
+ expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
+ })
+
+ it('can bail in the middle of the rule set with a modifier', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: { name: 'test', validation: 'required|^in:xyz|min:10,length', errorBehavior: 'live' }
+ })
+ await flushPromises();
+ expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
+ })
+
+ it('does not show errors on blur when set error-behavior is submit', async () => {
+ const wrapper = mount(FormularioInput, {
+ propsData: {
+ validation: 'required',
+ errorBehavior: 'submit',
+ name: 'test',
+ },
+ scopedSlots: {
+ default: `
+
+
+ {{ error }}
+
+ `
+ }
+ })
+
+ expect(wrapper.find('span').exists()).toBe(false)
+ wrapper.find('input').trigger('input')
+ wrapper.find('input').trigger('blur')
+ await flushPromises()
+ expect(wrapper.find('span').exists()).toBe(false)
+ })
+
+ it('displays errors when error-behavior is submit and form is submitted', async () => {
+ const wrapper = mount(FormularioForm, {
+ propsData: {name: 'test'},
+ slots: {
+ default: `
+
+ {{ error }}
+
+ `
+ }
+ })
+ wrapper.trigger('submit')
+ await flushPromises()
+
+ expect(wrapper.find('span').exists()).toBe(true)
+ })
+})
diff --git a/test/unit/Formulate.test.js b/test/unit/Formulate.test.js
deleted file mode 100644
index 3700641..0000000
--- a/test/unit/Formulate.test.js
+++ /dev/null
@@ -1,192 +0,0 @@
-import Formulate from '@/Formulate.js'
-
-describe('Formulate', () => {
- it('can merge simple object', () => {
- let a = {
- optionA: true,
- optionB: '1234'
- }
- let b = {
- optionA: false
- }
- expect(Formulate.merge(a, b)).toEqual({
- optionA: false,
- optionB: '1234'
- })
- })
-
- it('can add to simple array', () => {
- let a = {
- optionA: true,
- optionB: ['first', 'second']
- }
- let b = {
- optionB: ['third']
- }
- expect(Formulate.merge(a, b, true)).toEqual({
- optionA: true,
- optionB: ['first', 'second', 'third']
- })
- })
-
- it('can merge recursively', () => {
- let a = {
- optionA: true,
- optionC: {
- first: '123',
- third: {
- a: 'b'
- }
- },
- optionB: '1234'
- }
- let b = {
- optionB: '567',
- optionC: {
- first: '1234',
- second: '789',
- }
- }
- expect(Formulate.merge(a, b)).toEqual({
- optionA: true,
- optionC: {
- first: '1234',
- third: {
- a: 'b'
- },
- second: '789'
- },
- optionB: '567'
- })
- })
-
- it('installs on vue instance', () => {
- const components = [
- 'FormulateSlot',
- 'FormulateForm',
- 'FormulateHelp',
- 'FormulateLabel',
- 'FormulateInput',
- 'FormulateErrors',
- 'FormulateAddMore',
- 'FormulateGrouping',
- 'FormulateInputBox',
- 'FormulateInputText',
- 'FormulateInputFile',
- 'FormulateRepeatable',
- 'FormulateInputGroup',
- 'FormulateInputButton',
- 'FormulateInputSelect',
- 'FormulateInputSlider',
- 'FormulateInputTextArea',
- 'FormulateRepeatableRemove',
- 'FormulateRepeatableProvider'
- ]
- const registry = []
- function Vue () {}
- Vue.component = function (name, instance) {
- registry.push(name)
- }
- Formulate.install(Vue, { extended: true })
- expect(Vue.prototype.$formulate).toBe(Formulate)
- expect(registry).toEqual(components)
- })
-
- it('can extend instance in a plugin', () => {
- function Vue () {}
- Vue.component = function (name, instance) {}
- const plugin = function (i) {
- i.extend({
- rules: {
- testRule: () => false
- }
- })
- }
- Formulate.install(Vue, {
- plugins: [ plugin ]
- })
-
- expect(typeof Vue.prototype.$formulate.options.rules.testRule).toBe('function')
- })
-
- it('locale default to en', () => {
- Formulate.selectedLocale = false // reset the memoization
- function Vue () {}
- Vue.component = function (name, instance) {}
- const vm = {}
- Formulate.install(Vue, {
- locales: {
- de: {},
- fr: {},
- cn: {},
- en: {}
- }
- })
- expect(Vue.prototype.$formulate.getLocale(vm)).toBe('en')
- })
-
- it('explicitly selects language', () => {
- Formulate.selectedLocale = false // reset the memoization
- function Vue () {}
- Vue.component = function (name, instance) {}
- const vm = {}
- Formulate.install(Vue, {
- locale: 'fr-CH',
- locales: {
- de: {},
- fr: {},
- cn: {},
- en: {}
- }
- })
- expect(Vue.prototype.$formulate.getLocale(vm)).toBe('fr')
- })
-
- it('can select a specific language tag', () => {
- Formulate.selectedLocale = false // reset the memoization
- function Vue () {}
- Vue.component = function (name, instance) {}
- const vm = {}
- Formulate.install(Vue, {
- locale: 'nl-BE',
- locales: {
- de: {},
- fr: {},
- 'nl-BE': {},
- nl: {},
- cn: {},
- en: {}
- }
- })
- expect(Vue.prototype.$formulate.getLocale(vm)).toBe('nl-BE')
- })
-
- it('can select a matching locale using i18n locale string', () => {
- Formulate.selectedLocale = false // reset the memoization
- function Vue () {}
- Vue.component = function (name, instance) {}
- const vm = { $i18n: {locale: 'cn-ET' } }
- Formulate.install(Vue, {
- locales: {
- cn: {},
- em: {}
- }
- })
- expect(Vue.prototype.$formulate.getLocale(vm)).toBe('cn')
- })
-
- it('can select a matching locale using i18n locale function', () => {
- Formulate.selectedLocale = false // reset the memoization
- function Vue () {}
- Vue.component = function (name, instance) {}
- const vm = { $i18n: {locale: () => 'en-US' } }
- Formulate.install(Vue, {
- locales: {
- cn: {},
- em: {},
- en: {}
- }
- })
- expect(Vue.prototype.$formulate.getLocale(vm)).toBe('en')
- })
-})
diff --git a/test/unit/FormulateForm.test.js b/test/unit/FormulateForm.test.js
deleted file mode 100644
index dbbad1c..0000000
--- a/test/unit/FormulateForm.test.js
+++ /dev/null
@@ -1,600 +0,0 @@
-import Vue from 'vue'
-import { mount, shallowMount } from '@vue/test-utils'
-import flushPromises from 'flush-promises'
-import Formulate from '../../src/Formulate.js'
-import FormSubmission from '../../src/FormSubmission.js'
-import FormulateForm from '@/FormulateForm.vue'
-import FormulateInput from '@/FormulateInput.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateForm', () => {
- it('render a form DOM element', () => {
- const wrapper = mount(FormulateForm)
- expect(wrapper.find('form').exists()).toBe(true)
- })
-
- it('accepts a default slot', () => {
- const wrapper = mount(FormulateForm, {
- slots: {
- default: ''
- }
- })
- expect(wrapper.find('form div.default-slot-item').exists()).toBe(true)
- })
-
- it('intercepts submit event', () => {
- const formSubmitted = jest.fn()
- const wrapper = mount(FormulateForm, {
- slots: {
- default: ""
- }
- })
- const spy = jest.spyOn(wrapper.vm, 'formSubmitted')
- wrapper.find('form').trigger('submit')
- expect(spy).toHaveBeenCalled()
- })
-
- it('registers its subcomponents', () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { testinput: 'has initial value' } },
- slots: { default: '' }
- })
- expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
- })
-
- it('deregisters a subcomponents', async () => {
- const wrapper = mount({
- data () {
- return {
- active: true
- }
- },
- template: `
-
-
-
-
- `
- })
- await flushPromises()
- expect(wrapper.findComponent(FormulateForm).vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
- wrapper.setData({ active: false })
- await flushPromises()
- expect(wrapper.findComponent(FormulateForm).vm.registry.keys()).toEqual(['subinput2'])
- })
-
- it('can set a field’s initial value', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { testinput: 'has initial value' } },
- slots: { default: '' }
- })
- await flushPromises()
- expect(wrapper.find('input').element.value).toBe('has initial value')
- })
-
- it('lets individual fields override form initial value', () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { testinput: 'has initial value' } },
- slots: { default: '' }
- })
- expect(wrapper.find('input').element.value).toBe('123')
- })
-
- 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('can set initial checked attribute on single checkboxes', () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { box1: true } },
- slots: { default: '' }
- })
- expect(wrapper.find('input[type="checkbox"]').element.checked).toBeTruthy()
- });
-
- it('can set initial unchecked attribute on single checkboxes', () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { box1: false } },
- slots: { default: '' }
- })
- expect(wrapper.find('input[type="checkbox"]').element.checked).toBeFalsy()
- });
-
- it('can set checkbox initial value with options', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { box2: ['second', 'third'] } },
- slots: { default: '' }
- })
- await flushPromises()
- expect(wrapper.findAll('input').length).toBe(3)
- });
-
- it('receives updates to form model when individual fields are edited', () => {
- const wrapper = mount({
- data () {
- return {
- formValues: {
- testinput: '',
- }
- }
- },
- template: `
-
-
-
- `
- })
- wrapper.find('input').setValue('edited value')
- expect(wrapper.vm.formValues).toEqual({ testinput: 'edited value' })
- })
-
-
-
- // ===========================================================================
- /**
- * @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 () => {
- const wrapper = mount({
- data () {
- return {
- formValues: {
- testinput: '',
- },
- fieldValue: '123'
- }
- },
- template: `
-
-
-
- `
- })
- await flushPromises()
- expect(wrapper.vm.formValues).toEqual({ testinput: '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(FormulateForm, {
- propsData: {
- formulateValue: { testinput: '123' }
- },
- slots: {
- default: ''
- }
- })
- expect(wrapper.emitted().input[wrapper.emitted().input.length - 1]).toEqual([{ testinput: 'override-data' }])
- })
-
- it('updates an inputs value when the form v-model is modified', async () => {
- const wrapper = mount({
- data () {
- return {
- formValues: {
- testinput: 'abcd',
- }
- }
- },
- template: `
-
-
-
- `
- })
- await flushPromises()
- wrapper.vm.formValues = { testinput: '1234' }
- await flushPromises()
- expect(wrapper.find('input[type="text"]').element.value).toBe('1234')
- })
-
- it('emits an instance of FormSubmission', async () => {
- const wrapper = mount(FormulateForm, {
- slots: { default: '' }
- })
- wrapper.find('form').trigger('submit')
- await flushPromises()
- expect(wrapper.emitted('submit-raw')[0][0]).toBeInstanceOf(FormSubmission)
- })
-
- it('resolves hasValidationErrors to true', async () => {
- const wrapper = mount(FormulateForm, {
- slots: { default: '' }
- })
- wrapper.find('form').trigger('submit')
- await flushPromises()
- const submission = wrapper.emitted('submit-raw')[0][0]
- expect(await submission.hasValidationErrors()).toBe(true)
- })
-
- it('resolves submitted form values to an object', async () => {
- const wrapper = mount(FormulateForm, {
- slots: { default: '' }
- })
- const submission = await wrapper.vm.formSubmitted()
- expect(submission).toEqual({testinput: 'Justin'})
- })
-
- it('accepts a values prop and uses it to set the initial values', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { values: { name: 'Dave Barnett', candy: true } },
- slots: { default: `` }
- })
- await flushPromises()
- expect(wrapper.find('input[type="text"]').element.value).toBe('Dave Barnett')
- expect(wrapper.find('input[type="checkbox"]').element.checked).toBe(true)
- })
-
- it('shows error messages when it includes a checkbox with options', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { box3: [] } },
- slots: { default: '' }
- })
- wrapper.trigger('submit')
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').exists()).toBe(true)
- })
-
- it('automatically registers with root plugin', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formulateValue: { box3: [] }, name: 'login' }
- })
- expect(wrapper.vm.$formulate.registry.has('login')).toBe(true)
- expect(wrapper.vm.$formulate.registry.get('login')).toBe(wrapper.vm)
- })
-
- it('calls custom error handler with error and name', async () => {
- const mockHandler = jest.fn((err, name) => err);
- const wrapper = mount({
- template: `
-
-
-
-
- `
- })
- wrapper.vm.$formulate.extend({ errorHandler: mockHandler })
- wrapper.vm.$formulate.handle({ formErrors: ['This is an error message'] }, 'login')
- expect(mockHandler.mock.calls.length).toBe(1);
- expect(mockHandler.mock.calls[0]).toEqual([{ formErrors: ['This is an error message'] }, 'login']);
- })
-
- it('errors are displayed on correctly named components', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `
- })
- expect(wrapper.vm.$formulate.registry.has('login') && wrapper.vm.$formulate.registry.has('register')).toBe(true)
- wrapper.vm.$formulate.handle({ formErrors: ['This is an error message'] }, 'login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-form').length).toBe(2)
- expect(wrapper.find('.formulate-form--login .formulate-form-errors').exists()).toBe(true)
- expect(wrapper.find('.formulate-form--register .formulate-form-errors').exists()).toBe(false)
- })
-
- it('errors are displayed on correctly named components', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `
- })
- expect(wrapper.vm.$formulate.registry.has('login') && wrapper.vm.$formulate.registry.has('register')).toBe(true)
- wrapper.vm.$formulate.handle({ formErrors: ['This is an error message'] }, 'login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-form').length).toBe(2)
- expect(wrapper.find('.formulate-form--login .formulate-form-errors').exists()).toBe(true)
- expect(wrapper.find('.formulate-form--register .formulate-form-errors').exists()).toBe(false)
- })
-
- it('hides root FormError if another form error exists and renders in new location', async () => {
- const wrapper = mount({
- template: `
-
- Login
-
-
-
- `
- })
- wrapper.vm.$formulate.handle({ formErrors: ['This is an error message'] }, 'login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-form-errors').length).toBe(1)
- // Ensure that we moved the position of the errors
- expect(wrapper.find('h1 + *').element.classList.contains('formulate-form-errors')).toBe(true)
- })
-
- it('allows rendering multiple locations', async () => {
- const wrapper = mount({
- template: `
-
- Login
-
-
-
-
- `
- })
- wrapper.vm.$formulate.handle({ formErrors: ['This is an error message'] }, 'login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-form-errors').length).toBe(2)
- })
-
- it('receives a form-errors prop and displays it', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formErrors: ['first', 'second'] },
- slots: {
- default: ''
- }
- })
- await flushPromises()
- expect(wrapper.findAll('.formulate-form-error').length).toBe(2)
- })
-
- it('it aggregates form-errors prop with form-named errors', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { formErrors: ['first', 'second'], name: 'login' }
- })
- wrapper.vm.$formulate.handle({ formErrors: ['third'] }, 'login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-form-error').length).toBe(3)
- })
-
- it('displays field errors on inputs with errors prop', async () => {
- const wrapper = mount(FormulateForm, {
- propsData: { errors: { sipple: ['This field has an error'] }},
- slots: {
- default: ''
- }
- })
- await wrapper.vm.$nextTick()
- expect(wrapper.find('.formulate-input .formulate-input-error').exists()).toBe(true)
- })
-
- it('is able to display multiple errors on multiple elements', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
- `
- })
- await wrapper.vm.$nextTick()
- expect(wrapper.findAll('.formulate-input-error').length).toBe(4)
- })
-
- it('it can set multiple field errors with handle()', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
- `
- })
- expect(wrapper.findAll('.formulate-input-error').length).toBe(0)
- wrapper.vm.$formulate.handle({ inputErrors: {inputA: ['first', 'second'], inputB: 'only one here', inputC: ['and one here']} }, "register")
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.findAll('.formulate-input-error').length).toBe(4)
- })
-
- it('only sets 1 error when used on a FormulateGroup input', async () => {
- const wrapper = mount({
- template: `
-
-
-
- `
- })
- await wrapper.vm.$nextTick()
- expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
- })
-
- it('properly de-registers an observer when removed', async () => {
- const wrapper = mount({
- data () {
- return {
- hasField: true
- }
- },
- template: `
-
-
-
- `
- })
- await flushPromises()
- expect(wrapper.findComponent(FormulateForm).vm.errorObservers.length).toBe(1)
- wrapper.setData({ hasField: false })
- await flushPromises()
- expect(wrapper.findComponent(FormulateForm).vm.errorObservers.length).toBe(0)
- })
-
- it('emits correct validation event on entry', async () => {
- const wrapper = mount(FormulateForm, {
- slots: { default: `
-
-
-
-
- ` }
- })
- wrapper.find('input[type="text"]').setValue('foo')
- 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',
- errors: [
- expect.any(String)
- ],
- hasErrors: true
- })
- })
-
- it('emits correct validation event when no errors', async () => {
- const wrapper = mount(FormulateForm, {
- slots: { default: `
-
-
-
-
- ` }
- })
- wrapper.find('input[type="text"]').setValue('bar')
- await flushPromises()
- const errorObjects = wrapper.emitted('validation')
- expect(errorObjects.length).toBe(3)
- const errorObject = errorObjects[2][0]
- expect(errorObject).toEqual({
- name: 'testinput',
- errors: [],
- hasErrors: false
- })
- })
-
- it('removes field data when that field is de-registered', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `,
- data () {
- return {
- formData: {}
- }
- }
- })
- await flushPromises()
- wrapper.find('input[type="text"]').setValue('bar')
- await flushPromises()
- expect(wrapper.findComponent(FormulateForm).vm.proxy).toEqual({ foo: 'bar' })
- expect(wrapper.vm.formData).toEqual({ foo: 'bar' })
- })
-
- it('it allows the removal of properties in proxy.', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `,
- data () {
- return {
- formData: {},
- username: undefined
- }
- }
- })
- wrapper.find('input[type="text"]').setValue('foo')
- await flushPromises()
- expect(wrapper.vm.username).toEqual('foo')
- expect(wrapper.vm.formData).toEqual({ username: 'foo' })
- wrapper.vm.$refs.form.setValues({})
- await flushPromises()
- expect(wrapper.vm.formData).toEqual({ username: '' })
- })
-
- it('it allows resetting a form, hiding validation and clearing inputs.', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `,
- data () {
- return {
- formData: {}
- }
- }
- })
- const password = wrapper.find('input[type="password"]')
- password.setValue('foo')
- password.trigger('blur')
- wrapper.find('form').trigger('submit')
- wrapper.vm.$formulate.handle({
- inputErrors: { username: ['Failed'] }
- }, 'login')
- await flushPromises()
- // First make sure we showed the errors
- expect(wrapper.findAll('.formulate-input-error').length).toBe(3)
- wrapper.vm.$formulate.reset('login')
- await flushPromises()
- expect(wrapper.findAll('.formulate-input-error').length).toBe(0)
- expect(wrapper.vm.formData).toEqual({})
- })
-})
diff --git a/test/unit/FormulateInput.test.js b/test/unit/FormulateInput.test.js
deleted file mode 100644
index bd88c08..0000000
--- a/test/unit/FormulateInput.test.js
+++ /dev/null
@@ -1,319 +0,0 @@
-import Vue from 'vue'
-import flushPromises from 'flush-promises'
-import { mount, createLocalVue } from '@vue/test-utils'
-import Formulate from '@/Formulate.js'
-import FormulateForm from '@/FormulateForm.vue'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputBox from '@/inputs/FormulateInputBox.vue'
-
-const globalRule = jest.fn((context) => { return false })
-
-Vue.use(Formulate, {
- locales: {
- en: {
- email: ({ value }) => `Super invalid email: ${value}`
- }
- },
- rules: {
- globalRule
- },
- library: {
- special: {
- classification: 'box',
- component: 'FormulateInputBox'
- }
- }
-})
-
-describe('FormulateInput', () => {
- it('allows custom field-rule level validation strings', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|in:abcdef',
- validationMessages: {in: 'the value was different than expected'},
- errorBehavior: 'live',
- value: 'other value'
- } })
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').text()).toBe('the value was different than expected')
- })
-
- it('allows custom field-rule level validation functions', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|in:abcdef',
- validationMessages: { in: ({ value }) => `The string ${value} is not correct.` },
- errorBehavior: 'live',
- value: 'other value'
- } })
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').text()).toBe('The string other value is not correct.')
- })
-
- it('uses globally overridden validation messages', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|email',
- errorBehavior: 'live',
- value: 'wrong email'
- } })
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').text()).toBe('Super invalid email: wrong email')
- })
-
- it('uses custom async validation rules on defined on the field', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|foobar',
- validationMessages: {
- foobar: 'failed the foobar check'
- },
- validationRules: {
- foobar: async ({ value }) => value === 'foo'
- },
- errorBehavior: 'live',
- value: 'bar'
- } })
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').text()).toBe('failed the foobar check')
- })
-
- it('uses custom sync validation rules on defined on the field', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|foobar',
- validationMessages: {
- foobar: 'failed the foobar check'
- },
- validationRules: {
- foobar: ({ value }) => value === 'foo'
- },
- errorBehavior: 'live',
- value: 'bar'
- } })
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').text()).toBe('failed the foobar check')
- })
-
- it('uses global custom validation rules', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required|globalRule',
- errorBehavior: 'live',
- value: 'bar'
- } })
- await flushPromises()
- expect(globalRule.mock.calls.length).toBe(1)
- })
-
- it('can extend its standard library of inputs', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'special',
- validation: 'required',
- errorBehavior: 'live',
- value: 'bar'
- } })
- await flushPromises()
- expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
- })
-
- it('emits correct validation event', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'live',
- value: '',
- name: 'testinput',
- } })
- await flushPromises()
- const errorObject = wrapper.emitted('validation')[0][0]
- expect(errorObject).toEqual({
- name: 'testinput',
- errors: [
- expect.any(String)
- ],
- hasErrors: true
- })
- })
-
- it('emits a error-visibility event on blur', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'blur',
- value: '',
- name: 'testinput',
- } })
- await flushPromises()
- expect(wrapper.emitted('error-visibility')[0][0]).toBe(false)
- wrapper.find('input[type="text"]').trigger('blur')
- await flushPromises()
- expect(wrapper.emitted('error-visibility')[1][0]).toBe(true)
- })
-
- it('emits error-visibility event immediately when live', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'live',
- value: '',
- name: 'testinput',
- } })
- await flushPromises()
- expect(wrapper.emitted('error-visibility').length).toBe(1)
- })
-
- it('Does not emit an error-visibility event if visibility did not change', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'in:xyz',
- errorBehavior: 'live',
- value: 'bar',
- name: 'testinput',
- } })
- await flushPromises()
- expect(wrapper.emitted('error-visibility').length).toBe(1)
- wrapper.find('input[type="text"]').setValue('bar')
- await flushPromises()
- expect(wrapper.emitted('error-visibility').length).toBe(1)
- })
-
- it('allows overriding the label default slot component', async () => {
- const localVue = createLocalVue()
- localVue.component('CustomLabel', {
- render: function (h) {
- return h('div', { class: 'custom-label' }, [`custom: ${this.context.label}`])
- },
- props: ['context']
- })
- localVue.use(Formulate, { slotComponents: { label: 'CustomLabel' } })
- const wrapper = mount(FormulateInput, { localVue, propsData: { label: 'My label here' } })
- expect(wrapper.find('.custom-label').html()).toBe('custom: My label here
')
- })
-
- it('allows overriding the help default slot component', async () => {
- const localVue = createLocalVue()
- localVue.component('CustomHelp', {
- render: function (h) {
- return h('small', { class: 'custom-help' }, [`custom: ${this.context.help}`])
- },
- props: ['context']
- })
- localVue.use(Formulate, { slotComponents: { help: 'CustomHelp' } })
- const wrapper = mount(FormulateInput, { localVue, propsData: { help: 'My help here' } })
- expect(wrapper.find('.custom-help').html()).toBe('custom: My help here')
- })
-
- it('allows overriding the errors component', async () => {
- const localVue = createLocalVue()
- localVue.component('CustomErrors', {
- render: function (h) {
- return h('ul', { class: 'my-errors' }, this.context.visibleValidationErrors.map(message => h('li', message)))
- },
- props: ['context']
- })
- localVue.use(Formulate, { slotComponents: { errors: 'CustomErrors' } })
- const wrapper = mount(FormulateInput, { localVue, propsData: {
- help: 'My help here',
- errorBehavior: 'live',
- validation: 'required'
- } })
- await flushPromises()
- expect(wrapper.find('.my-errors').html())
- .toBe(``)
- // Clean up after this call — we should probably get rid of the singleton all together....
- Formulate.extend({ slotComponents: { errors: 'FormulateErrors' }})
- })
-
- it('links help text with `aria-describedby`', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'live',
- value: 'bar',
- help: 'Some help text'
- }
- })
- await flushPromises()
- const id = `${wrapper.vm.context.id}-help`
- expect(wrapper.find('input').attributes('aria-describedby')).toBe(id)
- expect(wrapper.find('.formulate-input-help').attributes().id).toBe(id)
- });
-
- it('it does not use aria-describedby if there is no help text', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'live',
- value: 'bar',
- }
- })
- await flushPromises()
- expect(wrapper.find('input').attributes('aria-describedby')).toBeFalsy()
- });
-
- it('can bail on validation when encountering the bail rule', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', validation: 'bail|required|in:xyz', errorBehavior: 'live' }
- })
- await flushPromises();
- expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
- })
-
- it('can show multiple validation errors if they occur before the bail rule', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', validation: 'required|in:xyz|bail', errorBehavior: 'live' }
- })
- await flushPromises();
- expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
- })
-
- it('can avoid bail behavior by using modifier', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live', value: '123' }
- })
- await flushPromises();
- expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
- })
-
- it('prevents later error messages when modified rule fails', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live' }
- })
- await flushPromises();
- expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
- })
-
- it('can bail in the middle of the rule set with a modifier', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', validation: 'required|^in:xyz|min:10,length', errorBehavior: 'live' }
- })
- await flushPromises();
- expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
- })
-
- it('does not show errors on blur when set error-behavior is submit', async () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'text',
- validation: 'required',
- errorBehavior: 'submit',
- } })
- wrapper.find('input').trigger('input')
- wrapper.find('input').trigger('blur')
- await flushPromises()
- expect(wrapper.find('.formulate-input-errors').exists()).toBe(false)
- })
-
- it('displays errors when error-behavior is submit and form is submitted', async () => {
- const wrapper = mount(FormulateForm, {
- slots: {
- default: ``
- }
- })
- wrapper.trigger('submit')
- await flushPromises()
- expect(wrapper.find('.formulate-input-errors').exists()).toBe(true)
- })
-})
diff --git a/test/unit/FormulateInputBox.test.js b/test/unit/FormulateInputBox.test.js
deleted file mode 100644
index 5b494fb..0000000
--- a/test/unit/FormulateInputBox.test.js
+++ /dev/null
@@ -1,228 +0,0 @@
-import Vue from 'vue'
-import flushPromises from 'flush-promises'
-import { mount } from '@vue/test-utils'
-import Formulate from '../../src/Formulate.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputBox from '@/inputs/FormulateInputBox.vue'
-import FormulateInputGroup from '@/inputs/FormulateInputGroup.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateInputBox', () => {
- it('renders a box element when type "checkbox" ', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } })
- expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
- })
-
- it('renders a box element when type "radio"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio' } })
- expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
- })
-
- it('passes an explicitly given name prop through to the root radio elements', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', name: 'foo', options: {a: '1', b: '2'} } })
- expect(wrapper.findAll('input[name="foo"]')).toHaveLength(2)
- })
-
- it('passes an explicitly given name prop through to the root checkbox elements', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', name: 'foo', options: {a: '1', b: '2'} } })
- expect(wrapper.findAll('input[name="foo"]')).toHaveLength(2)
- })
-
- it('box inputs properly process options object in context library', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'} } })
- expect(Array.isArray(wrapper.vm.context.options)).toBe(true)
- })
-
- it('renders a group when type "checkbox" with options', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'} } })
- expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
- })
-
- it('renders a group when type "radio" with options', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
- expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
- })
-
- it('defaults labelPosition to "after" when type "checkbox"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } })
- expect(wrapper.vm.context.labelPosition).toBe('after')
- })
-
- it('labelPosition of defaults to before when type "checkbox" with options', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'}}})
- expect(wrapper.vm.context.labelPosition).toBe('before')
- })
-
-
- it('renders multiple inputs with options when type "radio"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
- expect(wrapper.findAll('input[type="radio"]').length).toBe(2)
- })
-
- it('generates ids if not provided when type "radio"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
- expect(wrapper.find('input[type="radio"]').attributes().id).toBeTruthy()
- })
-
- it('additional context does not bleed through to attributes with type "radio" and options', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
- expect(Object.keys(wrapper.find('input[type="radio"]').attributes())).toEqual(["type", "id", "value"])
- })
-
- it('additional context does not bleed through to attributes with type "checkbox" and options', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'} } })
- expect(Object.keys(wrapper.find('input[type="checkbox"]').attributes())).toEqual(["type", "id", "value"])
- })
-
- it('allows external attributes to make it down to the inner box elements', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'}, readonly: 'true' } })
- expect(Object.keys(wrapper.find('input[type="radio"]').attributes()).includes('readonly')).toBe(true)
- })
-
- it('does not use the value attribute to be checked', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', value: '123' } })
- expect(wrapper.find('input').element.checked).toBe(false)
- })
-
- it('uses the checked attribute to be checked', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', checked: 'true' } })
- await flushPromises()
- await wrapper.vm.$nextTick()
- expect(wrapper.find('input').element.checked).toBe(true)
- })
-
- it('uses the value attribute to select "type" radio when using options', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'}, value: 'b' } })
- await flushPromises()
- expect(wrapper.findAll('input:checked').length).toBe(1)
- })
-
- it('uses the value attribute to select "type" checkbox when using options', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2', c: '123'}, value: ['b', 'c'] } })
- await flushPromises()
- expect(wrapper.findAll('input:checked').length).toBe(2)
- })
-
- /**
- * it data binding
- */
-
- it('sets array of values via v-model when type "checkbox"', async () => {
- const wrapper = mount({
- data () {
- return {
- checkboxValues: [],
- options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey'}
- }
- },
- template: `
-
-
-
- `
- })
- const fooInputs = wrapper.findAll('input[value^="foo"]')
- expect(fooInputs.length).toBe(2)
- fooInputs.at(0).setChecked()
- await flushPromises()
- fooInputs.at(1).setChecked()
- await flushPromises()
- expect(wrapper.vm.checkboxValues).toEqual(['foo', 'fooey'])
- })
-
- it('does not pre-set internal value when type "radio" with options', async () => {
- const wrapper = mount({
- data () {
- return {
- radioValue: '',
- options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey'}
- }
- },
- template: `
-
-
-
- `
- })
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.vm.radioValue).toBe('')
- })
-
- it('does not pre-set internal value of FormulateForm when type "radio" with options', async () => {
- const wrapper = mount({
- data () {
- return {
- radioValue: '',
- formValues: {},
- options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey'}
- }
- },
- template: `
-
-
-
- `
- })
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.vm.formValues.foobar).toBe('')
- })
-
- it('does precheck the correct input when radio with options', async () => {
- const wrapper = mount({
- data () {
- return {
- radioValue: 'fooey',
- options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey', gooey: 'Gooey'}
- }
- },
- template: `
-
-
-
- `
- })
- await flushPromises()
- const checkboxes = wrapper.findAll('input[type="radio"]:checked')
- expect(checkboxes.length).toBe(1)
- expect(checkboxes.at(0).element.value).toBe('fooey')
- })
-
- it('shows validation errors when blurred', async () => {
- const wrapper = mount({
- data () {
- return {
- radioValue: 'fooey',
- options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey', gooey: 'Gooey'}
- }
- },
- template: `
-
-
-
- `
- })
- wrapper.find('input[value="fooey"]').trigger('blur')
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.find('.formulate-input-error').exists()).toBe(true)
- })
-
- it('renders no boxes when options array is empty', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: [] } })
- expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
- expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
- })
-
- it('renders multiple labels both with correct id', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', label: 'VueFormulate FTW!'} })
- const id = wrapper.find('input[type="checkbox"]').attributes('id')
- const labelIds = wrapper.findAll('label').wrappers.map(label => label.attributes('for'));
- expect(labelIds.length).toBe(2);
- expect(labelIds.filter(labelId => labelId === id).length).toBe(2);
- })
-})
diff --git a/test/unit/FormulateInputButton.test.js b/test/unit/FormulateInputButton.test.js
deleted file mode 100644
index d073abb..0000000
--- a/test/unit/FormulateInputButton.test.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import Vue from 'vue'
-import flushPromises from 'flush-promises'
-import { mount } from '@vue/test-utils'
-import Formulate from '@/Formulate.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputButton from '@/inputs/FormulateInputButton.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateInputButton', () => {
-
- it('renders a button element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'button' } })
- expect(wrapper.findComponent(FormulateInputButton).exists()).toBe(true)
- })
-
- it('renders a button element when type submit', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'submit' } })
- expect(wrapper.findComponent(FormulateInputButton).exists()).toBe(true)
- })
-
- it('uses value as highest priority content', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'submit',
- value: 'Value content',
- label: 'Label content',
- name: 'Name content'
- }})
- expect(wrapper.find('button').text()).toBe('Value content')
- })
-
- it('uses label as second highest priority content', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'submit',
- label: 'Label content',
- name: 'Name content'
- }})
- expect(wrapper.find('button').text()).toBe('Label content')
- })
-
- it('uses name as lowest priority content', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'submit',
- name: 'Name content'
- }})
- expect(wrapper.find('button').text()).toBe('Name content')
- })
-
- it('uses "Submit" as default content', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'submit',
- }})
- expect(wrapper.find('button').text()).toBe('Submit')
- })
-
- it('with label does not render label element', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'button',
- label: 'my label'
- }})
- expect(wrapper.find('label').exists()).toBe(false)
- })
-
- it('does not render label element when type "submit"', () => {
- const wrapper = mount(FormulateInput, { propsData: {
- type: 'button',
- label: 'my label'
- }})
- expect(wrapper.find('label').exists()).toBe(false)
- })
-
- it('renders slot inside button when type "button"', () => {
- const wrapper = mount(FormulateInput, {
- propsData: {
- type: 'button',
- label: 'my label',
- },
- slots: {
- default: 'My custom slot'
- }
- })
- expect(wrapper.find('button > span').html()).toBe('My custom slot')
- })
-
- it('emits a click event when the button itself is clicked', async () => {
- const handle = jest.fn();
- const wrapper = mount({
- template: `
-
-
-
- `,
- methods: {
- handle
- }
- })
- wrapper.find('button[type="submit"]').trigger('click')
- await flushPromises();
- expect(handle.mock.calls.length).toBe(1)
- })
-
-})
-
-it('passes an explicitly given name prop through to the root element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'button', name: 'foo' } })
- expect(wrapper.find('button[name="foo"]').exists()).toBe(true)
-})
-
-it('additional context does not bleed through to button input attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'button' } } )
- expect(Object.keys(wrapper.find('button').attributes())).toEqual(["type", "id"])
- })
diff --git a/test/unit/FormulateInputFile.test.js b/test/unit/FormulateInputFile.test.js
deleted file mode 100644
index 3718327..0000000
--- a/test/unit/FormulateInputFile.test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import Vue from 'vue'
-import { mount } from '@vue/test-utils'
-import flushPromises from 'flush-promises'
-import Formulate from '@/Formulate.js'
-import FileUpload from '@/FileUpload.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputFile from '@/inputs/FormulateInputFile.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateInputFile', () => {
-
- it('type "file" renders a file element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'file' } })
- expect(wrapper.findComponent(FormulateInputFile).exists()).toBe(true)
- })
-
- it('type "image" renders a file element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image' } })
- expect(wrapper.findComponent(FormulateInputFile).exists()).toBe(true)
- })
-
- it('forces an error-behavior live mode when upload-behavior is live and it has content', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image', validation: 'mime:image/jpeg', value: [{ url: 'img.jpg' }] } })
- expect(wrapper.vm.showValidationErrors).toBe(true)
- })
-
- it('wont show errors when upload-behavior is live and it is required but empty', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image', validation: 'required|mime:image/jpeg' } })
- expect(wrapper.vm.showValidationErrors).toBe(false)
- })
-
- it('contains a data-has-preview attribute when showing a preview', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image', value: [ { url: 'https://www.example.com/image.png' } ], validation: 'required|mime:image/jpeg' } })
- const file = wrapper.find('[data-has-preview]')
- expect(file.exists()).toBe(true)
- expect(file.attributes('data-has-preview')).toBe('true')
- })
-
- it('passes an explicitly given name prop through to the root element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image', name: 'foo' } })
- expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
- })
-
- it('additional context does not bleed through to file input attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'image' } } )
- expect(Object.keys(wrapper.find('input[type="file"]').attributes())).toEqual(["type", "id"])
- })
-
- /**
- * ===========================================================================
- * Currently there appears to be no way to properly mock upload data in
- * vue-test-utils because JSDom doesn't implement DataTransfer:
- *
- * https://stackoverflow.com/questions/48993134/how-to-test-input-file-with-jest-and-vue-test-utils
- */
- // it('type "image" renders a file element', async () => {
-
- // })
-})
diff --git a/test/unit/FormulateInputGroup.test.js b/test/unit/FormulateInputGroup.test.js
deleted file mode 100644
index f4db652..0000000
--- a/test/unit/FormulateInputGroup.test.js
+++ /dev/null
@@ -1,482 +0,0 @@
-import Vue from 'vue'
-import { mount } from '@vue/test-utils'
-import flushPromises from 'flush-promises'
-import Formulate from '@/Formulate.js'
-import FileUpload from '@/FileUpload.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateForm from '@/FormulateForm.vue'
-import FormulateGrouping from '@/FormulateGrouping.vue'
-import FormulateRepeatableProvider from '@/FormulateRepeatableProvider.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateInputGroup', () => {
- it('allows nested fields to be sub-rendered', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group' },
- slots: {
- default: ''
- }
- })
- expect(wrapper.findAll('.formulate-input-group-repeatable input[type="text"]').length).toBe(1)
- })
-
- it('registers sub-fields with grouping', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group' },
- slots: {
- default: ''
- }
- })
- expect(wrapper.findComponent(FormulateRepeatableProvider).vm.registry.has('persona')).toBeTruthy()
- })
-
- it('is not repeatable by default', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group' },
- slots: {
- default: ''
- }
- })
- expect(wrapper.findAll('.formulate-input-group-add-more').length).toBe(0)
- })
-
- it('adds an add more button when repeatable', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true },
- slots: {
- default: ''
- }
- })
- expect(wrapper.findAll('.formulate-input-group-add-more').length).toBe(1)
- })
-
- it('repeats the default slot when adding more', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true },
- slots: {
- default: '
'
- }
- })
- wrapper.find('.formulate-input-group-add-more button').trigger('click')
- await flushPromises();
- expect(wrapper.findAll('.wrap').length).toBe(2)
- })
-
- it('re-hydrates a repeatable field', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true, value: [{email: 'jon@example.com'}, {email:'jane@example.com'}] },
- slots: {
- default: '
'
- }
- })
- await flushPromises()
- const fields = wrapper.findAll('input[type="text"]')
- expect(fields.length).toBe(2)
- expect(fields.at(0).element.value).toBe('jon@example.com')
- expect(fields.at(1).element.value).toBe('jane@example.com')
- })
-
- it('v-modeling a subfield changes all values', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `,
- data () {
- return {
- users: [{email: 'jon@example.com'}, {email:'jane@example.com'}],
- email: 'jim@example.com'
- }
- }
- })
- await flushPromises()
- const fields = wrapper.findAll('input[type="text"]')
- expect(fields.length).toBe(4)
- expect(fields.at(0).element.value).toBe('jim@example.com')
- expect(fields.at(2).element.value).toBe('jim@example.com')
- })
-
- it('v-modeling a subfield updates group v-model value', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
- `,
- data () {
- return {
- users: [{email: 'jon@example.com'}, {email:'jane@example.com'}],
- email: 'jim@example.com'
- }
- }
- })
- await flushPromises()
- expect(wrapper.vm.users).toEqual([{email: 'jim@example.com'}, {email:'jim@example.com'}])
- })
-
- it('prevents form submission when children have validation errors', async () => {
- const submit = jest.fn()
- const wrapper = mount({
- template: `
-
-
-
-
-
-
-
-
- `,
- data () {
- return {
- users: [{email: 'jon@example.com'}, {email:'jane@example.com'}],
- }
- },
- methods: {
- submit
- }
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- expect(submit.mock.calls.length).toBe(0);
- })
-
- it('allows form submission with children when there are no validation errors', async () => {
- const submit = jest.fn()
- const wrapper = mount({
- template: `
-
-
-
-
-
-
-
-
- `,
- methods: {
- submit
- }
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- expect(submit.mock.calls.length).toBe(1);
- })
-
- it('displays validation errors on group children when form is submitted', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
-
- `
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- await flushPromises()
- expect(wrapper.find('[data-classification="text"] .formulate-input-error').exists()).toBe(true);
- })
-
- it('displays error messages on newly registered fields when formShouldShowErrors is true', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
-
- `
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- // Click the add more button
- wrapper.find('button[type="button"]').trigger('click')
- await flushPromises()
- expect(wrapper.findAll('[data-classification="text"] .formulate-input-error').length).toBe(2)
- })
-
- it('displays error messages on newly registered fields when formShouldShowErrors is true', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
-
- `
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- // Click the add more button
- wrapper.find('button[type="button"]').trigger('click')
- await flushPromises()
- expect(wrapper.findAll('[data-classification="text"] .formulate-input-error').length).toBe(2)
- })
-
- it('allows the removal of groups', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
-
- `,
- data () {
- return {
- users: [{name: 'justin'}, {name: 'bill'}]
- }
- }
- })
- await flushPromises()
- wrapper.find('.formulate-input-group-repeatable-remove').trigger('click')
- await flushPromises()
- expect(wrapper.vm.users).toEqual([{name: 'bill'}])
- })
-
- it('can override the add more text', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { addLabel: '+ Add a user', type: 'group', repeatable: true },
- slots: {
- default: ''
- }
- })
- expect(wrapper.find('button').text()).toEqual('+ Add a user')
- })
-
- it('does not allow more than the limit', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { addLabel: '+ Add a user', type: 'group', repeatable: true, limit: 2, value: [{}, {}]},
- slots: {
- default: ''
- }
- })
- expect(wrapper.findAll('.repeated').length).toBe(2)
- expect(wrapper.find('button').exists()).toBeFalsy()
- })
-
- it('does not truncate the number of items if value is more than limit', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { addLabel: '+ Add a user', type: 'group', repeatable: true, limit: 2, value: [{}, {}, {}, {}]},
- slots: {
- default: ''
- }
- })
- expect(wrapper.findAll('.repeated').length).toBe(4)
- })
-
- it('allows a slot override of the add button and has addItem prop', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true, addLabel: '+ Name' },
- scopedSlots: {
- default: '',
- addmore: '{{ props.addLabel }}'
- }
- })
- expect(wrapper.find('.formulate-input-group-add-more').exists()).toBeFalsy()
- const addButton = wrapper.find('.add-name')
- expect(addButton.text()).toBe('+ Name')
- addButton.trigger('click')
- await flushPromises()
- expect(wrapper.findAll('.repeatable').length).toBe(2)
- })
-
- it('allows a slot override of the repeatable area', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true, value: [{}, {}]},
- scopedSlots: {
- repeatable: '',
- }
- })
- const repeats = wrapper.findAll('.repeat')
- expect(repeats.length).toBe(2)
- expect(repeats.at(1).text()).toBe("1")
- wrapper.find('.remove').trigger('click')
- await flushPromises()
- expect(wrapper.findAll('.repeat').length).toBe(1)
- })
-
- it('allows a slot override of the remove area', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true, value: [{phone: 'iPhone'}, {phone: 'Android'}]},
- scopedSlots: {
- default: '',
- remove: '',
- }
- })
- const repeats = wrapper.findAll('.remove-this')
- expect(repeats.length).toBe(2)
- const button = wrapper.find('.remove-this')
- expect(button.text()).toBe('Get outta here')
- button.trigger('click')
- await flushPromises()
- expect(wrapper.findAll('input').wrappers.map(w => w.element.value)).toEqual(['Android'])
- })
-
- it('removes the proper item from the group', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'group', repeatable: true },
- slots: {
- default: ''
- }
- })
- wrapper.find('input').setValue('first entry')
- wrapper.find('.formulate-input-group-add-more button').trigger('click')
- wrapper.find('.formulate-input-group-add-more button').trigger('click')
- await flushPromises();
- wrapper.findAll('input').at(1).setValue('second entry')
- wrapper.findAll('input').at(2).setValue('third entry')
- // First verify all the proper entries are where we expect
- expect(wrapper.findAll('input').wrappers.map(input => input.element.value)).toEqual(['first entry', 'second entry', 'third entry'])
- // Now remove the middle one
- wrapper.findAll('.formulate-input-group-repeatable-remove').at(1).trigger('click')
- await flushPromises()
- expect(wrapper.findAll('input').wrappers.map(input => input.element.value)).toEqual(['first entry', 'third entry'])
- })
-
- it('does not show an error message on group input when child has an error', async () => {
- const wrapper = mount({
- template: `
-
-
-
-
-
-
-
-
- `,
- data () {
- return {
- users: [{email: 'jon@example.com'}, {}],
- }
- }
- })
- const form = wrapper.findComponent(FormulateForm)
- await form.vm.formSubmitted()
- expect(wrapper.find('[data-classification="group"] > .formulate-input-errors').exists()).toBe(false)
- })
-
- it('exposes the index to the context object on default slot', async () => {
- const wrapper = mount({
- template: `
-
- {{ name }}-{{ index }}
-
- `,
- })
- const repeatables = wrapper.findAll('.repeatable')
- expect(repeatables.length).toBe(2)
- expect(repeatables.at(0).text()).toBe('test-0')
- expect(repeatables.at(1).text()).toBe('test-1')
- })
-
- it('forces non-repeatable groups to not initialize with an empty array', async () => {
- const wrapper = mount({
- template: `
-
-
-
- `,
- data () {
- return {
- model: []
- }
- }
- })
- await flushPromises();
- expect(wrapper.findComponent(FormulateGrouping).vm.items).toEqual([{}])
- })
-
- it('allows repeatable groups to initialize with an empty array', async () => {
- const wrapper = mount({
- template: `
-
-
-
- `,
- data () {
- return {
- model: []
- }
- }
- })
- await flushPromises();
- expect(wrapper.findComponent(FormulateGrouping).vm.items).toEqual([])
- })
-})
diff --git a/test/unit/FormulateInputSelect.test.js b/test/unit/FormulateInputSelect.test.js
deleted file mode 100644
index 2b6924d..0000000
--- a/test/unit/FormulateInputSelect.test.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import Vue from 'vue'
-import { mount } from '@vue/test-utils'
-import flushPromises from 'flush-promises'
-import Formulate from '@/Formulate.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputSelect from '@/inputs/FormulateInputSelect.vue'
-
-Vue.use(Formulate)
-
-describe('FormulateInputSelect', () => {
- it('renders select input when type is "select"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select' } })
- expect(wrapper.findComponent(FormulateInputSelect).exists()).toBe(true)
- })
-
- it('renders select options when options object is passed', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select', options: { first: 'First', second: 'Second' } } })
- const option = wrapper.find('option[value="second"]')
- expect(option.exists()).toBe(true)
- expect(option.text()).toBe('Second')
- })
-
- it('renders select options when options array is passed', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select', options: [
- { value: 13, label: 'Jane' },
- { value: 22, label: 'Jon' }
- ]} })
- const option = wrapper.find('option[value="22"]')
- expect(option.exists()).toBe(true)
- expect(option.text()).toBe('Jon')
- })
-
- it('renders select list with no options when empty array is passed.', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select', options: []} })
- const option = wrapper.find('option')
- expect(option.exists()).toBe(false)
- })
-
- it('renders select list placeholder option.', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select', placeholder: 'Select this', options: []} })
- const options = wrapper.findAll('option')
- expect(options.length).toBe(1)
- expect(options.at(0).attributes('disabled')).toBeTruthy()
- })
-
- it('passes an explicitly given name prop through to the root element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select', options: [], name: 'foo' } })
- expect(wrapper.find('select[name="foo"]').exists()).toBe(true)
- })
-
- it('additional context does not bleed through to text select attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'select' } } )
- expect(Object.keys(wrapper.find('select').attributes())).toEqual(["id"])
- })
-})
diff --git a/test/unit/FormulateInputSlider.test.js b/test/unit/FormulateInputSlider.test.js
deleted file mode 100644
index 7129b3f..0000000
--- a/test/unit/FormulateInputSlider.test.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import Vue from 'vue'
-import { mount } from '@vue/test-utils'
-import Formulate from '@/Formulate.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputSlider from '@/inputs/FormulateInputSlider.vue'
-
-Vue.use(Formulate)
-
-/**
- * Test each type of slider element
- */
-
-describe('FormulateInputSlider', () => {
- it('renders range input when type is "range"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'range' } })
- expect(wrapper.findComponent(FormulateInputSlider).exists()).toBe(true)
- })
-
- it('does not show value if the show-value prop is not set', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'range', value: '15', min: '0', max: '100' } })
- expect(wrapper.find('.formulate-input-element-range-value').exists()).toBe(false)
- })
-
- it('renders the value when type is "range" and show-value prop is set', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'range', showValue: 'true', value: '15', min: '0', max: '100' } })
- expect(wrapper.find('.formulate-input-element-range-value').text()).toBe('15')
- })
-
- it('passes an explicitly given name prop through to the root element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'range', name: 'foo' } })
- expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
- })
-
- it('additional context does not bleed through to range input attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'range' } } )
- expect(Object.keys(wrapper.find('input[type="range"]').attributes())).toEqual(["type", "id"])
- })
-})
diff --git a/test/unit/FormulateInputText.test.js b/test/unit/FormulateInputText.test.js
deleted file mode 100644
index 9b1f9e0..0000000
--- a/test/unit/FormulateInputText.test.js
+++ /dev/null
@@ -1,313 +0,0 @@
-import Vue from 'vue'
-import flushPromises from 'flush-promises'
-import { mount } from '@vue/test-utils'
-import Formulate from '../../src/Formulate.js'
-import FormulateInput from '@/FormulateInput.vue'
-import FormulateInputText from '@/inputs/FormulateInputText.vue'
-import { doesNotReject } from 'assert';
-
-Vue.use(Formulate)
-
-/**
- * Test each type of text element
- */
-
-describe('FormulateInputText', () => {
- it('renders text input when type is "text"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders search input when type is "search"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'search' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders email input when type is "email"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'email' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders number input when type is "number"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'number' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders color input when type is "color"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'color' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders date input when type is "date"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'date' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders month input when type is "month"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'month' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders password input when type is "password"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'password' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders tel input when type is "tel"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'tel' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders time input when type is "time"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'time' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders url input when type is "url"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'url' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- it('renders week input when type is "week"', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'week' } })
- expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
- })
-
- /**
- * Test rendering functionality to text inputs
- */
-
- it('automatically assigns an id', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.vm.context).toHaveProperty('id')
- expect(wrapper.find(`input[id="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true)
- })
-
- it('passes an explicitly given name prop through to the root text element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', name: 'foo' } })
- expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
- })
-
- it('passes an explicitly given name prop through to the root textarea element', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'textarea', name: 'foo' } })
- expect(wrapper.find('textarea[name="foo"]').exists()).toBe(true)
- })
-
- it('additional context does not bleed through to text input attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } } )
- expect(Object.keys(wrapper.find('input[type="text"]').attributes())).toEqual(["type", "id"])
- })
-
- it('additional context does not bleed through to textarea input attributes', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'textarea' } } )
- expect(Object.keys(wrapper.find('textarea').attributes())).toEqual(["id"])
- })
-
- it('doesn’t automatically add a label', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.find('label').exists()).toBe(false)
- })
-
- it('renders labels', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', label: 'Field label' } })
- expect(wrapper.find(`label[for="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true)
- })
-
- it('doesn’t automatically render help text', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.find(`.formulate-input-help`).exists()).toBe(false)
- })
-
- it('renders help text', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', help: 'This is some help text' } })
- expect(wrapper.find(`.formulate-input-help`).exists()).toBe(true)
- })
-
- /**
- * Test data binding
- */
- it('emits input (vmodel) event with value when edited', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- wrapper.find('input').setValue('Updated Value')
- expect(wrapper.emitted().input).toBeTruthy()
- expect(wrapper.emitted().input[0]).toEqual(['Updated Value'])
- })
-
-
- it('doesn’t re-context itself if there were no changes', async () => {
- const wrapper = mount({
- data () {
- return {
- valueA: 'first value',
- valueB: 'second value'
- }
- },
- template: `
-
-
-
-
- `
- })
- await flushPromises()
- const firstContext = wrapper.findComponent({ref: "first"}).vm.context
- const secondContext = wrapper.findComponent({ref: "second"}).vm.context
- wrapper.find('input').setValue('new value')
- await flushPromises()
- expect(firstContext).toBeTruthy()
- expect(wrapper.vm.valueA === 'new value').toBe(true)
- expect(wrapper.vm.valueB === 'second value').toBe(true)
- expect(wrapper.findComponent({ref: "first"}).vm.context === firstContext).toBe(false)
- expect(wrapper.findComponent({ref: "second"}).vm.context === secondContext).toBe(true)
- })
-
- it('uses the v-model value as the initial value', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', formulateValue: 'initial val' } })
- expect(wrapper.find('input').element.value).toBe('initial val')
- })
-
- it('uses the value as the initial value', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', value: 'initial val' } })
- expect(wrapper.find('input').element.value).toBe('initial val')
- })
-
- it('uses the value prop as the initial value when v-model provided', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', formulateValue: 'initial val', value: 'initial other val' } })
- expect(wrapper.find('input').element.value).toBe('initial other val')
- })
-
- it('uses a proxy model internally if it doesnt have a v-model', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'textarea' } })
- const input = wrapper.find('textarea')
- input.setValue('changed value')
- expect(wrapper.vm.proxy).toBe('changed value')
- })
-
-
- /**
- * Test error handling
- */
- it('doesn’t automatically render errors', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.find('.formulate-input-errors').exists()).toBe(false)
- })
-
- it('accepts a single string as an error prop', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', error: 'This is an error' } })
- expect(wrapper.find('.formulate-input-errors').exists()).toBe(true)
- })
-
- it('accepts an array as errors prop', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'live', errors: ['This is an error', 'this is another'] } })
- expect(wrapper.findAll('.formulate-input-error').length).toBe(2)
- })
-
- it('removes any duplicate errors', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'live', errors: ['This is an error', 'This is an error'] } })
- expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
- })
-
- it('adds data-has-errors when there are errors', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'live', errors: ['This is an error', 'This is an error'] } })
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- })
-
- it('Should always show explicitly set errors, but not validation errors', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', validation: 'required', errorBehavior: 'blur', errors: ['Bad input'] } })
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(true)
- expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
- })
-
- it('Should show no errors when there are no errors', () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
- expect(wrapper.find('[data-has-errors]').exists()).toBe(false)
- expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(false)
- expect(wrapper.findAll('.formulate-input-error').exists()).toBe(false)
- })
-
- it('allows error-behavior blur to be overridden with show-errors', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'blur', showErrors: true, validation: 'required' } })
- await flushPromises()
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(true)
- expect(wrapper.findAll('.formulate-input-errors').exists()).toBe(true)
- expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
- })
-
- it('shows errors on blur with error-behavior blur', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'blur', validation: 'required' } })
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(false)
- expect(wrapper.findAll('.formulate-input-error').exists()).toBe(false)
- expect(wrapper.vm.showValidationErrors).toBe(false)
- wrapper.find('input').trigger('blur')
- await flushPromises()
- expect(wrapper.vm.showValidationErrors).toBe(true)
- expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(true)
- // expect(wrapper.findAll('.formulate-input-errors').exists()).toBe(true)
- // expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
- })
-
- it('continues to show errors if validation fires more than one time', async () => {
- const wrapper = mount(FormulateInput, { propsData: { type: 'date', errorBehavior: 'live', validation: [['after', '01/01/2021']] , value: '01/01/1999' } })
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- wrapper.find('input').trigger('input')
- await wrapper.vm.$nextTick()
- await flushPromises()
- expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
- })
-
-
- it('allows label-before override with scoped slot', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', label: 'flavor' },
- scopedSlots: {
- label: ''
- }
- })
- expect(wrapper.find('label').text()).toBe('flavor town')
- })
-
- it('allows label-after override with scoped slot', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', label: 'flavor', labelPosition: 'after' },
- scopedSlots: {
- label: ''
- }
- })
- expect(wrapper.find('label').text()).toBe('flavor town')
- })
-
- it('allows help-before override', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', label: 'flavor', help: 'I love this next field...', helpPosition: 'before' },
- })
- expect(wrapper.find('label + *').classes('formulate-input-help')).toBe(true)
- })
-
- it('Allow help text override with scoped slot', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', name: 'soda', help: 'Do you want some'},
- scopedSlots: {
- help: '{{ props.help }} {{ props.name }}?'
- }
- })
- expect(wrapper.find('small').text()).toBe('Do you want some soda?')
- })
-
- it('Allow errors override with scoped slot', async () => {
- const wrapper = mount(FormulateInput, {
- propsData: { type: 'text', name: 'soda', validation: 'required|in:foo,bar', errorBehavior: 'live' },
- scopedSlots: {
- errors: ''
- }
- })
- await flushPromises();
- expect(wrapper.findAll('.my-errors li').length).toBe(2)
- })
-})