vuetify
Version:
Vue Material Component Framework
410 lines (314 loc) • 11.2 kB
text/typescript
// Components
import VCombobox from '../VCombobox'
// Utilities
import {
mount,
Wrapper,
} from '@vue/test-utils'
import { keyCodes } from '../../../util/helpers'
describe('VCombobox.ts', () => {
type Instance = InstanceType<typeof VCombobox>
let mountFunction: (options?: object) => Wrapper<Instance>
beforeEach(() => {
document.body.setAttribute('data-app', 'true')
mountFunction = (options = {}) => {
return mount(VCombobox, {
// https://github.com/vuejs/vue-test-utils/issues/1130
sync: false,
mocks: {
$vuetify: {
lang: {
t: (val: string) => val,
},
theme: {
dark: false,
},
},
},
...options,
})
}
})
function createMultipleCombobox (propsData) {
const change = jest.fn()
const wrapper = mountFunction({
attachToDocument: true,
propsData: Object.assign({
multiple: true,
value: [],
}, propsData),
})
wrapper.vm.$on('input', change)
return { wrapper, change }
}
it('should create new values when tagging', async () => {
const { wrapper, change } = createMultipleCombobox({})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
element.value = 'foo'
input.trigger('input')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['foo'])
})
it('should change selectedIndex with keyboard', async () => {
const { wrapper } = createMultipleCombobox({
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
input.trigger('focus')
await wrapper.vm.$nextTick()
for (const index of [1, 0, -1]) {
input.trigger('keydown.left')
await wrapper.vm.$nextTick()
expect(wrapper.vm.selectedIndex).toBe(index)
}
})
it('should delete a tagged item when selected and backspace/delete is pressed', async () => {
const { wrapper, change } = createMultipleCombobox({
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
input.trigger('focus')
input.trigger('keydown.left')
expect(wrapper.vm.selectedIndex).toBe(1)
input.trigger('keydown.delete')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['foo'])
expect(wrapper.vm.selectedIndex).toBe(0)
const backspace = new Event('keydown')
backspace.keyCode = keyCodes.delete
input.element.dispatchEvent(backspace) // Avoriaz doesn't wrap keydown.backspace
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith([])
expect(wrapper.vm.selectedIndex).toBe(-1)
})
it('should add a tag on enter using the current searchValue', async () => {
const { wrapper, change } = createMultipleCombobox({
items: ['bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
await wrapper.vm.$nextTick()
element.value = 'ba'
input.trigger('input')
await wrapper.vm.$nextTick()
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['ba'])
})
it.skip('should add a tag on left arrow and select the previous tag', async () => {
const { wrapper, change } = createMultipleCombobox({
value: ['foo'],
items: ['foo', 'bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
element.value = 'b'
input.trigger('input')
input.trigger('keydown.left')
expect(change).toHaveBeenCalledWith(['foo', 'b'])
expect(wrapper.vm.selectedIndex).toBe(0)
})
it('should remove a duplicate tag and add it to the end', async () => {
const { wrapper, change } = createMultipleCombobox({
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
await wrapper.vm.$nextTick()
element.value = 'foo'
input.trigger('input')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['bar', 'foo'])
})
it('should add tag with valid search value on blur', async () => {
const { wrapper, change } = createMultipleCombobox({})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
element.value = 'bar'
input.trigger('input')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['bar'])
})
it('should be able to add a tag from user input after deleting a tag with delete', async () => {
const { wrapper, change } = createMultipleCombobox({
multiple: true,
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
input.trigger('keydown.left')
expect(wrapper.vm.selectedIndex).toBe(1)
input.trigger('keydown.delete')
expect(change).toHaveBeenCalledWith(['foo'])
expect(wrapper.vm.selectedIndex).toBe(0)
// Must be reset for input to update
wrapper.vm.selectedIndex = -1
await wrapper.vm.$nextTick()
element.value = 'baz'
input.trigger('input')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['foo', 'baz'])
expect(wrapper.vm.selectedIndex).toBe(-1)
})
it('should be able to add a tag from user input after clicking a deletable chip', async () => {
const { wrapper, change } = createMultipleCombobox({
chips: true,
clearable: true,
deletableChips: true,
multiple: true,
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
const chip = wrapper.findAll('.v-chip').at(1)
const close = chip.find('.v-chip__close')
input.trigger('focus')
chip.trigger('click')
close.trigger('click')
expect(change).toHaveBeenCalledWith(['foo'])
expect(wrapper.vm.selectedIndex).toBe(-1)
element.value = 'baz'
input.trigger('input')
expect(wrapper.vm.internalSearch).toBe('baz')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['foo', 'baz'])
expect(wrapper.vm.selectedIndex).toBe(-1)
})
// This test is actually almost useless
it('should not change search when selecting an index', () => {
const { wrapper } = createMultipleCombobox({
chips: true,
multiple: true,
value: ['foo', 'bar'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
expect(wrapper.vm.selectedIndex).toBe(-1)
input.trigger('keydown.left')
expect(wrapper.vm.selectedIndex).toBe(1)
expect(wrapper.vm.internalSearch).toBeUndefined()
input.trigger('keydown.right')
element.value = 'fizz'
input.trigger('input')
expect(wrapper.vm.internalSearch).toBe('fizz')
expect(wrapper.vm.selectedIndex).toBe(-1)
})
// eslint-disable-next-line max-statements
it('should create new items when a delimiter is entered', async () => {
const { wrapper, change } = createMultipleCombobox({
delimiters: [', ', 'baz'],
})
await wrapper.vm.$nextTick()
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
element.value = 'foo,'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledTimes(0)
element.value += ' '
input.trigger('input')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledTimes(1)
expect(change).toHaveBeenCalledWith(['foo'])
expect(element.value).toBe('')
element.value = 'foo,barba'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledTimes(1)
element.value += 'z'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledTimes(2)
expect(change).toHaveBeenCalledWith(['foo', 'foo,bar'])
expect(element.value).toBe('')
})
it('should allow the editing of an existing value', async () => {
const { wrapper } = createMultipleCombobox({
chips: true,
value: ['foo'],
})
const change = jest.fn()
const internal = jest.fn()
const chip = wrapper.find('.v-chip')
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
wrapper.vm.$on('change', change)
wrapper.vm.$watch('internalValue', internal)
expect(wrapper.vm.editingIndex).toBe(-1)
expect(wrapper.vm.internalSearch).toBeUndefined()
chip.trigger('dblclick')
expect(wrapper.vm.editingIndex).toBe(0)
expect(wrapper.vm.internalSearch).toBe('foo')
element.value = 'foobar'
input.trigger('input')
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['foobar'])
expect(internal).toHaveBeenCalledWith(['foobar'], ['foo'])
})
it('should paste as item if source of pasted text is item in another v-combobox/v-autocomplete', async () => {
const { wrapper, change } = createMultipleCombobox({
items: ['aaa', 'bbb'],
})
const input = wrapper.find('input')
const getData = jest.fn(mimeType => 'ccc')
const event = {
clipboardData: {
getData,
},
}
input.trigger('focus')
input.trigger('paste', event)
expect(getData).toHaveBeenCalledTimes(1)
expect(getData).toHaveBeenCalledWith('text/vnd.vuetify.autocomplete.item+plain')
expect(change).toHaveBeenCalledWith(['ccc'])
})
it('should paste as text if source of pasted text is not item in another v-combobox/v-autocomplete', async () => {
const { wrapper, change } = createMultipleCombobox({
items: ['aaa', 'bbb'],
})
const input = wrapper.find('input')
const getData = jest.fn(mimeType => mimeType === 'text/plain' ? 'ccc' : '')
const event = {
clipboardData: {
getData,
},
}
input.trigger('focus')
input.trigger('paste', event)
expect(change).not.toHaveBeenCalled()
// expect(input.element.value).toBe('ccc') // can be checked only in browser environment
})
it('should not add search to list when selecting items with keyboard', async () => {
const { wrapper, change } = createMultipleCombobox({
chips: true,
multiple: true,
items: ['aaa', 'bbb'],
})
const input = wrapper.find('input')
const element = input.element as HTMLInputElement
input.trigger('focus')
element.value = 'a'
input.trigger('input')
input.trigger('keydown.down')
await wrapper.vm.$nextTick()
input.trigger('keydown.enter')
await wrapper.vm.$nextTick()
expect(wrapper.vm.internalSearch).toBe('a')
expect(change).toHaveBeenCalledWith(['aaa'])
})
})