element-plus/packages/input/__tests__/input.spec.ts

557 lines
15 KiB
TypeScript

import { nextTick, ref } from 'vue'
import { mount } from '@vue/test-utils'
import { sleep, defineGetter } from '@element-plus/test-utils'
import Input from '../src/index.vue'
const _mount = options => mount({
components: {
'el-input': Input,
},
...options,
})
describe('Input.vue', () => {
test('create', async () => {
const handleFocus = jest.fn()
const wrapper = _mount({
template: `
<el-input
:minlength="3"
:maxlength="5"
placeholder="请输入内容"
@focus="handleFocus"
:model-value="input">
</el-input>
`,
setup() {
const input = ref('input')
return {
input,
handleFocus,
}
},
})
const inputElm = wrapper.find('input')
const vm = wrapper.vm as any
const nativeInput = inputElm.element
await inputElm.trigger('focus')
expect(inputElm.exists()).toBe(true)
expect(handleFocus).toHaveBeenCalled()
expect(nativeInput.placeholder).toBe('请输入内容')
expect(nativeInput.value).toBe('input')
expect(nativeInput.minLength).toBe(3)
// expect(nativeInput.maxLength).toBe(5) // The maxlength attribute is no longer a native attribute
vm.input = 'text'
await sleep()
expect(inputElm.element.value).toBe('text')
})
test('default to empty', () => {
const wrapper = _mount({
template: '<el-input />',
})
const inputElm = wrapper.find('input')
expect(inputElm.element.value).toBe('')
})
test('disabled', () => {
const wrapper = _mount({
template: `<el-input disabled />`,
})
const inputElm = wrapper.find('input')
expect(inputElm.element.disabled).not.toBeNull()
})
describe('test emoji',()=>{
test('el-input should minimize value between emoji length and maxLength', async () => {
const wrapper = _mount({
template: `<el-input class="test-exceed" maxlength="4" show-word-limit v-model="inputVal" />`,
setup() {
const inputVal = ref('12🌚')
return { inputVal }
},
})
const vm = wrapper.vm
const inputElm = wrapper.find('input')
const nativeInput = inputElm.element
expect(nativeInput.value).toBe('12🌚')
const elCount = wrapper.find('.el-input__count-inner')
expect(elCount.exists()).toBe(true)
expect(elCount.text()).toBe('3/4')
vm.inputVal = '1👌3😄'
await sleep()
expect(nativeInput.value).toBe('1👌3😄')
expect(elCount.text()).toBe('4/4')
vm.inputVal = '哈哈1👌3😄'
await sleep()
expect(nativeInput.value).toBe('哈哈1👌3😄')
expect(elCount.text()).toBe('6/4')
expect(vm.$el.classList.contains('is-exceed')).toBe(true)
})
test('textarea should minimize value between emoji length and maxLength', async () => {
const wrapper = _mount({
template: `<el-input type="textarea" maxlength="4" show-word-limit v-model="inputVal" />`,
setup() {
const inputVal = ref('啊好😄')
return { inputVal }
},
})
const vm = wrapper.vm
const inputElm = wrapper.find('textarea')
const nativeInput = inputElm.element
expect(nativeInput.value).toBe('啊好😄')
const elCount = wrapper.find('.el-input__count')
expect(elCount.exists()).toBe(true)
expect(elCount.text()).toBe('3/4')
vm.inputVal = '哈哈1👌3😄'
await sleep()
expect(nativeInput.value).toBe('哈哈1👌3😄')
expect(elCount.text()).toBe('6/4')
expect(vm.$el.classList.contains('is-exceed')).toBe(true)
})
})
test('suffixIcon', () => {
const wrapper = _mount({
template: `<el-input suffix-icon="time" />`,
})
const icon = wrapper.find('.el-input__icon')
expect(icon.exists()).toBe(true)
})
test('prefixIcon', () => {
const wrapper = _mount({
template: `<el-input prefix-icon="time" />`,
})
const icon = wrapper.find('.el-input__icon')
expect(icon.exists()).toBe(true)
})
test('size', () => {
const wrapper = _mount({
template: `<el-input size="large" />`,
})
expect(wrapper.classes('el-input--large')).toBe(true)
})
test('type', () => {
const wrapper = _mount({
template: `<el-input type="textarea" />`,
})
expect(wrapper.classes('el-textarea')).toBe(true)
})
test('rows', () => {
const wrapper = _mount({
template: `<el-input type="textarea" :rows="3" />`,
})
expect(wrapper.find('textarea').element.rows).toEqual(3)
})
test('resize', async() => {
const wrapper = _mount({
template: `
<div>
<el-input type="textarea" :resize="resize" />
</div>
`,
data() {
return {
resize: 'none',
}
},
})
const vm = wrapper.vm as any
const textarea = wrapper.find('textarea').element
await sleep()
expect(textarea.style.resize).toEqual(vm.resize)
vm.resize = 'horizontal'
await sleep()
expect(textarea.style.resize).toEqual(vm.resize)
})
// TODO: Due to jsdom's reason this case cannot run well, may be fixed later using headlesschrome or puppeteer
// test('autosize', async() => {
// const wrapper = _mount({
// template: `<div>
// <el-input
// ref="limitSize"
// type="textarea"
// :autosize="{minRows: 3, maxRows: 5}"
// v-model="textareaValue"
// />
// </div>`,
// data() {
// return {
// textareaValue: 'sda\ndasd\nddasdsda\ndasd\nddasdsda\ndasd\nddasdsda\ndasd\nddasd',
// }
// },
// })
// const limitSizeInput = wrapper.vm.$refs.limitSize
// const limitlessSizeInput = wrapper.vm.$refs.limitlessSize
// await sleep()
// expect(limitSizeInput.computedTextareaStyle.height).toEqual('117px')
// expect(limitlessSizeInput.computedTextareaStyle.height).toEqual('201px')
// wrapper.vm.textareaValue = ''
// await sleep()
// expect(limitSizeInput.computedTextareaStyle.height).toEqual('75px')
// expect(limitlessSizeInput.computedTextareaStyle.height).toEqual('33px')
// })
test('sets value on textarea / input type change', async () => {
const wrapper = _mount({
template: `<el-input :type="type" v-model="val" />`,
data() {
return {
type: 'text',
val: '123',
}
},
})
const vm = wrapper.vm as any
expect(vm.$el.querySelector('input').value).toEqual('123')
vm.type = 'textarea'
await sleep()
expect(vm.$el.querySelector('textarea').value).toEqual('123')
vm.type = 'password'
await sleep()
expect(vm.$el.querySelector('input').value).toEqual('123')
})
test('limit input and show word count', async () => {
const wrapper = _mount({
template: `
<div>
<el-input
class="test-text"
type="text"
v-model="input1"
maxlength="10"
:show-word-limit="show">
</el-input>
<el-input
class="test-textarea"
type="textarea"
v-model="input2"
maxlength="10"
show-word-limit>
</el-input>
<el-input
class="test-password"
type="password"
v-model="input3"
maxlength="10"
show-word-limit>
</el-input>
<el-input
class="test-initial-exceed"
type="text"
v-model="input4"
maxlength="2"
show-word-limit>
</el-input>
</div>
`,
data() {
return {
input1: '',
input2: '',
input3: '',
input4: 'exceed',
show: false,
}
},
})
const inputElm1 = wrapper.vm.$el.querySelector('.test-text')
const inputElm2 = wrapper.vm.$el.querySelector('.test-textarea')
const inputElm3 = wrapper.vm.$el.querySelector('.test-password')
const inputElm4 = wrapper.vm.$el.querySelector('.test-initial-exceed')
expect(inputElm1.querySelectorAll('.el-input__count').length).toEqual(0)
expect(inputElm2.querySelectorAll('.el-input__count').length).toEqual(1)
expect(inputElm3.querySelectorAll('.el-input__count').length).toEqual(0)
expect(inputElm4.classList.contains('is-exceed')).toBe(true)
const vm = wrapper.vm as any
vm.show = true
await sleep()
expect(inputElm1.querySelectorAll('.el-input__count').length).toEqual(1)
vm.input4 = '1'
await sleep()
expect(inputElm4.classList.contains('is-exceed')).toBe(false)
})
describe('Input Methods', () => {
test('method:select', async () => {
const testContent = 'test'
const wrapper = _mount({
template: `<el-input v-model="text" />`,
data() {
return {
text: testContent,
}
},
})
const input = wrapper.find('input').element
// mock selectionRange behaviour, due to jsdom's reason this case cannot run well, may be fixed later using headlesschrome or puppeteer
let selected = false
defineGetter(input, 'selectionStart', function() {
return selected ? 0 : this.value.length
})
defineGetter(input, 'selectionEnd', function() {
return this.value.length
})
expect(input.selectionStart).toEqual(testContent.length)
expect(input.selectionEnd).toEqual(testContent.length)
input.select()
selected = true
await sleep()
expect(input.selectionStart).toEqual(0)
expect(input.selectionEnd).toEqual(testContent.length)
})
test('method:resizeTextarea', async () => {
const testContent = 'TEXT:resizeTextarea'
const wrapper = _mount({
template: `<el-input ref="textarea" :autosize="{ minRows: 1, maxRows: 1 }" type="textarea" v-model="text" />`,
data() {
return {
text: testContent,
}
},
})
const ref = wrapper.vm.$refs.textarea
const originMinHeight = ref.computedTextareaStyle.minHeight
ref.autosize.minRows = 5
ref.resizeTextarea()
// Atfer this textarea min-height (style) will change
const nowMinHeight = ref.computedTextareaStyle.minHeight
expect(originMinHeight).not.toEqual(nowMinHeight)
})
})
describe('Input Events', () => {
const handleFocus = jest.fn()
const handleBlur = jest.fn()
test('event:focus & blur', async () => {
const wrapper = _mount({
template: `<el-input
placeholder="请输入内容"
:model-value="input"
@focus="handleFocus"
@blur="handleBlur"
/>`,
setup() {
const input = ref('')
return {
input,
handleFocus,
handleBlur,
}
},
})
const input = wrapper.find('input')
await input.trigger('focus')
expect(handleFocus).toBeCalled()
await input.trigger('blur')
expect(handleBlur).toBeCalled()
})
test('event:change', async() => {
// NOTE: should be same as native's change behavior
const wrapper = _mount({
template: `
<el-input
placeholder="请输入内容"
:model-value="input"
@change="handleChange"
/>
`,
data() {
return {
input: 'a',
val: '',
}
},
methods: {
handleChange(val) {
this.val = val
},
},
})
const el = wrapper.find('input').element
const vm = wrapper.vm as any
const simulateEvent = (text, event) => {
el.value = text
el.dispatchEvent(new Event(event))
}
// simplified test, component should emit change when native does
simulateEvent('2', 'change')
await sleep()
expect(vm.val).toBe('2')
simulateEvent('1', 'input')
await sleep()
expect(vm.val).toBe('2')
})
test('event:clear', async() => {
const handleClear = jest.fn()
const handleInput = jest.fn()
const wrapper = _mount({
template: `
<el-input
placeholder="请输入内容"
clearable
v-model="input"
@clear="handleClear"
@input="handleInput"
/>
`,
setup() {
const input = ref('a')
return {
input,
handleClear,
handleInput,
}
},
})
const input = wrapper.find('input')
const vm = wrapper.vm as any
// focus to show clear button
await input.trigger('focus')
await sleep()
vm.$el.querySelector('.el-input__clear').click()
await sleep()
expect(vm.input).toEqual('')
expect(handleClear).toBeCalled()
expect(handleInput).toBeCalled()
})
test('event:input', async() => {
const handleInput = jest.fn()
const wrapper = _mount({
template: `
<el-input
placeholder="请输入内容"
clearable
:model-value="input"
@input="handleInput"
/>
`,
setup() {
const input = ref('a')
return {
input,
handleInput,
}
},
})
const vm = wrapper.vm as any
const inputWrapper = wrapper.find('input')
const nativeInput = inputWrapper.element
nativeInput.value = '1'
await inputWrapper.trigger('compositionstart')
await inputWrapper.trigger('input')
nativeInput.value = '2'
await inputWrapper.trigger('compositionupdate')
await inputWrapper.trigger('input')
await inputWrapper.trigger('compositionend')
expect(handleInput).toBeCalledTimes(1)
// native input value is controlled
expect(vm.input).toEqual('a')
expect(nativeInput.value).toEqual('a')
})
})
test('non-emit event such as keyup should work', async () => {
const handleKeyup = jest.fn()
const wrapper = mount(Input, {
attrs: {
onKeyup: handleKeyup,
},
})
await wrapper.find('input').trigger('keyup')
expect(handleKeyup).toBeCalledTimes(1)
})
test('input-style', async () => {
const wrapper = _mount({
template: `
<el-input
placeholder="请输入内容"
:input-style="{color: 'red'}"
/>
<el-input
placeholder="请输入内容"
:input-style="{color: 'red'}"
type="textarea"
/>
`,
})
const input = wrapper.find('input')
const textarea = wrapper.find('textarea')
await nextTick()
expect(input.element.style.color === 'red').toBeTruthy()
expect(textarea.element.style.color === 'red').toBeTruthy()
})
describe('Textarea Events', () => {
test('event:keydown', async () => {
const handleKeydown = jest.fn()
const wrapper = _mount({
template: `<el-input
type="textarea"
:model-value="val"
@keydown="handleKeydown"
/>`,
setup() {
const val = ref('')
return {
val,
handleKeydown,
}
},
})
await wrapper.find('textarea').trigger('keydown')
expect(handleKeydown).toBeCalledTimes(1)
})
})
// TODO: validateEvent & input containes select cases should be added after the rest components finished
// ...
})