import { nextTick } from 'vue' import { mount } from '@vue/test-utils' import installStyle from '@element-plus/test-utils/style-plugin' import Checkbox from '@element-plus/components/checkbox/src/checkbox.vue' import CheckboxGroup from '@element-plus/components/checkbox/src/checkbox-group.vue' import Input from '@element-plus/components/input' import Form from '../src/form.vue' import FormItem from '../src/form-item.vue' import type { VueWrapper } from '@vue/test-utils' type Methods = Record any> function mountForm( config: C & { data?(): D; methods?: M } ) { return mount({ components: { [Form.name]: Form, [FormItem.name]: FormItem, [Input.name]: Input, [Checkbox.name]: Checkbox, [CheckboxGroup.name]: CheckboxGroup, }, ...config, }) } function findStyle(wrapper: VueWrapper, selector: string) { return wrapper.find(selector).element.style } describe('Form', () => { beforeAll(installStyle) test('label width', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', }, } }, }) expect(findStyle(wrapper, '.el-form-item__label').width).toBe('80px') }) test('auto label width', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', intro: '', }, labelPosition: 'right', } }, }) await nextTick() const formItems = wrapper.findAll('.el-form-item__content') const marginLeft = parseInt(formItems[0].element.style.marginLeft, 10) const marginLeft1 = parseInt(formItems[1].element.style.marginLeft, 10) expect(marginLeft).toEqual(marginLeft1) wrapper.vm.labelPosition = 'left' await nextTick() const formItems1 = wrapper.findAll('.el-form-item__content') const marginRight = parseInt(formItems1[0].element.style.marginRight, 10) const marginRight1 = parseInt(formItems1[1].element.style.marginRight, 10) expect(marginRight).toEqual(marginRight1) }) test('form-item auto label width', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', region: '', type: '', }, } }, }) await nextTick() const formItemLabels = wrapper.findAll('.el-form-item__label') const formItemLabelWraps = wrapper.findAll( '.el-form-item__label-wrap' ) const labelWrapMarginLeft1 = formItemLabelWraps[0].element.style.marginLeft const labelWrapMarginLeft2 = formItemLabelWraps[1].element.style.marginLeft expect(labelWrapMarginLeft1).toEqual(labelWrapMarginLeft2) expect(labelWrapMarginLeft2).toEqual('') const labelWidth0 = parseInt(formItemLabels[0].element.style.width, 10) expect(labelWidth0).toEqual(150) const labelWidth1 = formItemLabels[1].element.style.width const labelWidth2 = formItemLabels[2].element.style.width expect(labelWidth1).toEqual(labelWidth2) expect(labelWidth2).toEqual('auto') }) test('inline form', () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', address: '', }, } }, }) expect(wrapper.classes()).toContain('el-form--inline') }) test('label position', () => { const wrapper = mountForm({ template: `
`, data() { return { form: { name: '', address: '', }, } }, }) expect(wrapper.findComponent({ ref: 'labelTop' }).classes()).toContain( 'el-form--label-top' ) expect(wrapper.findComponent({ ref: 'labelLeft' }).classes()).toContain( 'el-form--label-left' ) }) test('label size', () => { const wrapper = mountForm({ template: `
`, data() { return { form: { name: '', }, } }, }) expect(wrapper.findComponent(FormItem).classes()).toContain( 'el-form-item--mini' ) }) test('show message', (done) => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', }, } }, }) const form: any = wrapper.findComponent(Form).vm form.validate(async (valid) => { expect(valid).toBe(false) await nextTick() expect(wrapper.find('.el-form-item__error').exists()).toBe(false) done() }) }) test('reset field', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', address: '', type: [], }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'blur' }, ], address: [ { required: true, message: 'Please input address', trigger: 'change', }, ], type: [ { type: 'array', required: true, message: 'Please input type', trigger: 'change', }, ], }, } }, methods: { setValue() { this.form.name = 'jack' this.form.address = 'aaaa' this.form.type.push('type1') }, }, }) const vm = wrapper.vm vm.setValue() const form: any = wrapper.findComponent({ ref: 'form' }).vm form.resetFields() await nextTick() expect(vm.form.name).toBe('') expect(vm.form.address).toBe('') expect(vm.form.type.length).toBe(0) }) test('clear validate', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', address: '', type: [], }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'blur' }, ], address: [ { required: true, message: 'Please input address', trigger: 'change', }, ], type: [ { type: 'array', required: true, message: 'Please input type', trigger: 'change', }, ], }, } }, }) const form: any = wrapper.findComponent({ ref: 'form' }).vm const nameField: any = wrapper.findComponent({ ref: 'name' }).vm const addressField: any = wrapper.findComponent({ ref: 'address' }).vm await form.validate().catch(() => undefined) await nextTick() expect(nameField.validateMessage).toBe('Please input name') expect(addressField.validateMessage).toBe('Please input address') form.clearValidate(['name']) await nextTick() expect(nameField.validateMessage).toBe('') expect(addressField.validateMessage).toBe('Please input address') form.clearValidate() await nextTick() expect(addressField.validateMessage).toBe('') }) test('scroll to field', () => { const wrapper = mountForm({ template: `
`, data() { return { form: { name: '', }, } }, }) const oldScrollIntoView = window.HTMLElement.prototype.scrollIntoView const scrollIntoViewMock = jest.fn() window.HTMLElement.prototype.scrollIntoView = function () { scrollIntoViewMock(this) } const form: any = wrapper.findComponent({ ref: 'form' }).vm form.scrollToField('name') expect(scrollIntoViewMock).toHaveBeenCalledWith( wrapper.findComponent({ ref: 'formItem' }).element ) window.HTMLElement.prototype.scrollIntoView = oldScrollIntoView }) test('validate return parameters', async () => { const wrapper = mountForm({ template: ` `, data() { return { rules: { name: [ { required: true, message: 'Please input name', trigger: 'blur' }, ], age: [ { required: true, message: 'Please input age', trigger: 'blur' }, ], }, form: { name: 'test', age: '', }, } }, }) async function validate() { const [valid, fields] = await vm.$refs.formRef .validate() .then((fields) => [true, fields]) .catch((fields) => [false, fields]) return { valid, fields, } } const vm = wrapper.vm as any let res = await validate() expect(res.valid).toBeFalsy() expect(Object.keys(res.fields).length).toBe(1) vm.form.name = '' await nextTick() res = await validate() expect(res.valid).toBeFalsy() expect(Object.keys(res.fields).length).toBe(2) vm.form.name = 'test' vm.form.age = 'age' await nextTick() res = await validate() expect(res.valid).toBeTruthy() expect(Object.keys(res.fields).length).toBe(0) }) /* test('form item nest', done => { const wrapper = mountForm({ template: ` - `, data() { return { form: { date1: '', date2: '' }, rules: { date1: [ { type: 'date', required: true, message: '请选择日期', trigger: 'change' } ] } } }, methods: { setValue() { this.name = 'jack' this.address = 'aaaa' } } }) vm.$refs.form.validate(valid => { expect(valid).to.not.true done() }) }) */ /* test('validate event', async done => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '', addr: '' }, valid: { name: null, addr: null }, error: { name: null, addr: null }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'change', min: 3, max: 6 } ], addr: [ { required: true, message: 'Please input name', trigger: 'change' } ] } } }, methods: { onValidate(prop, valid, msg) { this.valid[prop] = valid this.error[prop] = msg }, setValue(prop, value) { this.form[prop] = value } } }) const vm = wrapper.vm vm.setValue('name', '1') await nextTick() expect(vm.valid.name).toBe(false) expect(vm.error.name).toBe('Please input name') vm.setValue('addr', '1') await nextTick() expect(vm.valid.addr).toBe(true) expect(vm.error.addr).toBe(null) vm.setValue('name', '111') await nextTick() expect(vm.valid.name).toBe(true) expect(vm.error.name).toBe(null) done() }) */ }) /* awaiting other components complete describe('validate', () => { test('input', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'change', min: 3, max: 6 } ] } } }, methods: { setValue(value) { this.form.name = value } } }) const vm = wrapper.vm const form: any = wrapper.findComponent({ref: 'form'}).vm const valid = await form.validate() .then(() => true) .catch(e => false) expect(valid).toBe(false) const field: any = wrapper.findComponent({ref: 'field'}).vm expect(field.validateMessage).toBe('Please input name') vm.setValue('aaaaa') await nextTick() expect(field.validateMessage).toBe('') vm.setValue('aa') await nextTick() expect(field.validateMessage).toBe('Please input name') }) test('textarea', async () => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'change', min: 3, max: 6 } ] } } }, methods: { setValue(value) { this.form.name = value } } }) const vm = wrapper.vm const form: any = wrapper.findComponent({ref: 'form'}).vm const valid = await form.validate() .then(() => true) .catch(e => false) expect(valid).toBe(false) const field: any = wrapper.findComponent({ref: 'field'}).vm await nextTick() expect(field.validateMessage).toBe('Please input name') vm.setValue('aaaaa') await nextTick() expect(field.validateMessage).toBe('') vm.setValue('aa') await nextTick() expect(field.validateMessage).toBe('Please input name') }) test('selector', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { region: '' }, rules: { region: [ {required: true, message: '请选择活动区域', trigger: 'change' } ] } } } }) vm.$refs.form.validate(valid => { let field = vm.$refs.field expect(valid).to.false setTimeout(_ => { expect(field.validateMessage).toBe('请选择活动区域') // programatic modification triggers change validation vm.form.region = 'shanghai' setTimeout(_ => { expect(field.validateMessage).toBe('') vm.form.region = '' setTimeout(_ => { expect(field.validateMessage).toBe('请选择活动区域') // user modification of bound value triggers change validation vm.$refs.opt.$el.click() setTimeout(_ => { expect(field.validateMessage).toBe('') done() }, 100) }, 100) }, 100) }, 100) }) }) test('datepicker', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { date: '' }, rules: { date: [ {type: 'date', required: true, message: '请选择日期', trigger: 'change' } ] } } } }) vm.$refs.form.validate(valid => { let field = vm.$refs.field expect(valid).to.not.true setTimeout(_ => { expect(field.validateMessage).toBe('请选择日期') // programatic modification triggers change validation vm.form.date = new Date() setTimeout(_ => { expect(field.validateMessage).toBe('') vm.form.date = '' // user modification triggers change const input = vm.$refs.picker.$el.querySelector('input') input.blur() input.focus() setTimeout(_ => { const keyDown = (el, keyCode) => { const evt = document.createEvent('Events') evt.initEvent('keydown') evt.keyCode = keyCode el.dispatchEvent(evt) } keyDown(input, 37) setTimeout(_ => { keyDown(input, 13) setTimeout(_ => { expect(field.validateMessage).toBe('') done() }, DELAY) }, DELAY) }, DELAY) }, DELAY) }, DELAY) }) }) test('timepicker', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { date: '' }, rules: { date: [ {type: 'date', required: true, message: '请选择时间', trigger: 'change' } ] } } } }) vm.$refs.form.validate(valid => { let field = vm.$refs.field expect(valid).to.not.true setTimeout(_ => { expect(field.validateMessage).toBe('请选择时间') // programatic modification does not trigger change vm.value = new Date() setTimeout(_ => { expect(field.validateMessage).toBe('请选择时间') vm.value = '' // user modification triggers change const input = vm.$refs.picker.$el.querySelector('input') input.blur() input.focus() setTimeout(_ => { vm.$refs.picker.picker.$el.querySelector('.confirm').click() setTimeout(_ => { expect(field.validateMessage).toBe('') done() }, DELAY) }, DELAY) }, DELAY) }, DELAY) }) }) test('checkbox', async done => { const wrapper = mountForm({ template: ` Accept `, data() { return { form: { accept: true }, rules: { accept: [ { validator: (rule, value, callback) => { value ? callback() : callback(new Error('You need accept terms')) }, trigger: 'change' } ] } } }, methods: { setValue(value) { this.form.accept = value } } }) const vm = wrapper.vm vm.form.accept = false await nextTick() expect(vm.$refs.field.validateMessage).toBe('You need accept terms') const valid = await vm.$refs.form.validate() .then(() => true, () => false) expect(valid).toBe(false) let field: any = vm.$refs.field expect(field.validateMessage).toBe('You need accept terms') await nextTick() vm.setValue(true) await nextTick() expect(field.validateMessage).toBe('') }) test('checkbox group', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { type: [] }, rules: { type: [ { type: 'array', required: true, message: 'Please select type', trigger: 'change' } ] } } }, methods: { setValue(value) { this.form.type = value } } }) const vm = wrapper.vm const form: any = wrapper.findComponent({ref: 'form'}).vm form.validate(async valid => { let field: any = vm.$refs.field expect(valid).toBe(false) await nextTick() expect(field.validateMessage).toBe('Please select type') vm.setValue(['type4']) await nextTick() expect(field.validateMessage).toBe('') done() }) }) test('radio group', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { type: '' }, rules: { type: [ { required: true, message: 'Please select type', trigger: 'change' } ] } } }, methods: { setValue(value) { this.form.type = value } } }) const vm = wrapper.vm const form: any = wrapper.findComponent({ref: 'form'}).vm form.validate(async valid => { let field: any = vm.$refs.field expect(valid).toBe(false) await nextTick() expect(field.validateMessage).toBe('Please select type') vm.setValue(['type2']) await nextTick() expect(field.validateMessage).toBe('') done() }) }) test('validate field', done => { const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'change', min: 3, max: 6 } ] } } }, methods: { setValue(value) { this.form.name = value } } }) const vm: any = wrapper.vm vm.$refs.form.validateField('name', valid => { expect(valid).toBe(false) done() }) }) test('custom validate', done => { var checkName = (rule, value, callback) => { if (value.length < 5) { callback(new Error('length must be at least 5')) } else { callback() } } const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { validator: checkName, trigger: 'change' } ] } } }, methods: { setValue(value) { this.form.name = value } } }) const vm: any = wrapper.vm vm.$refs.form.validate(async valid => { let field = vm.$refs.field expect(valid).toBe(false) await nextTick() expect(field.validateMessage).toBe('length must be at least 5') vm.setValue('aaaaaa') await nextTick() expect(field.validateMessage).toBe('') done() }) }) test('error', done => { const wrapper = mountForm({ template: ` `, data() { return { error: 'dsad', form: { name: 'sada' }, rules: { name: [ { required: true, message: 'Please input name', trigger: 'change', min: 3, max: 6 } ] } } }, methods: { setValue(value) { this.form.name = value } } }) const vm: any = wrapper.vm vm.$refs.form.validate(async valid => { let field = vm.$refs.field expect(valid).toBe(true) vm.error = 'illegal input' await nextTick() expect(field.validateState).toBe('error') expect(field.validateMessage).toBe('illegal input') done() }) }) test('invalid fields', done => { var checkName = (rule, value, callback) => { if (value.length < 5) { callback(new Error('length must be at least 5')) } else { callback() } } const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { validator: checkName, trigger: 'change' } ] } } } }) const vm: any = wrapper.vm vm.$refs.form.validate((valid, invalidFields) => { expect(invalidFields.name.length).toBe(1) done() }) }) test('validate return promise', async done => { var checkName = (rule, value, callback) => { if (value.length < 5) { callback(new Error('length must be at least 5')) } else { callback() } } const wrapper = mountForm({ template: ` `, data() { return { form: { name: '' }, rules: { name: [ { validator: checkName, trigger: 'change' } ] } } } }) const vm: any = wrapper.vm try { vm.$refs.form.validate() } catch (e) { expect(e).toBeDefined() done() } }) }) */