Reputation: 2553
I'm trying to trigger the "input" event of a Vuetify v-autocomplete
component inside of a unit test. From what I can tell, the unit test is quite similar to the one the Vuetify guys are using, but for some reason the mock method is never called.
The factory method simply returns a component instance made via mount
and adds a local store, vue, and vueLoading instance.
Would love a bit of insight here, already lost several hours to this and I'm starting to go a bit mad...
Vuetify version: 1.4.3
Component
<template>
<v-autocomplete
:items="options"
:value="selectedId"
:label="label || $t('user.filter')"
:dark="dark"
:class="fieldClass"
:menu-props="{ contentClass }"
:loading="$loading.isLoading('fetch users')"
item-text="email"
item-value="id"
name="search_by_user"
hide-details
single-line
clearable
@input="updateValue($event)"
@click.native="fetchUsers"
/>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
value: {
type: String,
required: false,
default: null
},
label: {
type: String,
required: false,
default: null
},
dark: {
type: Boolean,
required: false,
default: true
},
fieldClass: {
type: String,
required: false,
default: 'select-user-search'
},
contentClass: {
type: String,
required: false,
default: 'user-search'
},
blankItem: {
type: Object,
required: false,
default: null
}
},
data () {
return {
selectedId: this.value
}
},
computed: {
...mapGetters(['users']),
options () { return this.blankItem ? [this.blankItem].concat(this.users) : this.users }
},
created () {
if (this.value) this.fetchUsers()
},
methods: {
fetchUsers () {
if (this.users.length) return
this.$store.dispatch('FETCH_USERS', {
fields: ['id', 'email'],
filter: { active: 'true' }
})
},
updateValue (value) {
this.selectedId = value
this.$emit('input', value)
}
}
}
</script>
<style>
.select-user-search {
overflow: hidden;
padding-bottom: 1px;
}
.select-user-search .v-select__selections {
overflow: hidden;
}
.select-user-search .v-select__selection {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
Unit Test
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import VueLoading from 'vuex-loading'
import Vuetify from 'vuetify'
import UserSearch from 'manager/components/user/search.vue'
const factory = (values, shallow = true) => {
if (!shallow) {
return mount(UserSearch, { ...values })
}
return shallowMount(UserSearch, { ...values })
}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueLoading)
localVue.use(Vuetify)
describe('UserSearch', () => {
let actions, getters, store, vueLoading
beforeEach(() => {
actions = {
FETCH_USERS: jest.fn()
}
getters = {
users: () => []
}
vueLoading = new VueLoading({ useVuex: true })
store = new Vuex.Store({
actions,
getters
})
})
it('calls "updateValue" method when input triggered', async () => {
const methods = {
updateValue: jest.fn()
}
const wrapper = factory({ methods, store, localVue, vueLoading }, false)
const input = wrapper.find('input')
input.element.value = 'value'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(methods['updateValue']).toBeCalledWith('value')
})
})
Upvotes: 2
Views: 5008
Reputation: 21
I use the following variant
Component
<template>
<span> <!-- span (or the other HTML element) around <v-autocomplete> is important -->
<v-autocomplete
:items="options"
:value="selectedId"
:label="$t('user.filter')"
:menu-props="{ contentClass }"
:loading="false"
item-text="email"
item-value="id"
name="search_by_user"
hide-details
single-line
clearable
@input="updateValue($event)"
@click.native="fetchUsers"
/>
</span>
</template>
<script lang="ts">
import { Component } from 'vue-property-decorator';
import Vue from 'vue';
@Component
export default class AppAutocomplete extends Vue {
private options: any[] = [{email: '[email protected]', id: 1}];
private selectedId: string|undefined = undefined;
private contentClass = 'user-search';
private fetchUsers() {
}
public updateValue(event: any) {
}
}
</script>
Unit test
import { Wrapper, createLocalVue, mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuetify from 'vuetify';
// @ts-ignore
import AppAutocomplete from '@/components/AppAutocomplete.vue';
describe('AppAutocomplete', () => {
Vue.use(Vuetify);
let wrapper: Wrapper<Vue>;
let vm: any;
beforeEach(() => {
wrapper = mount(AppAutocomplete, {
localVue: createLocalVue(),
vuetify: new Vuetify({
mocks: { $vuetify: { lang: { t: (val: string) => val } } },
}),
mocks: { $t: () => { /** */ } },
});
vm = wrapper.vm;
});
afterEach(() => wrapper.destroy());
it(`calls "updateValue" method when input triggered`, () => {
jest.spyOn(vm, 'updateValue');
const autocompleteElem = wrapper.find('.v-autocomplete');
autocompleteElem.vm.$emit('input', 'box1');
expect(vm.updateValue).toBeCalledTimes(1);
expect(vm.updateValue).toHaveBeenCalledWith('box1');
});
});
To surround v-autocomplete by some other HTML element is important. Without it a test fails.
Vuetify version: 2.1.12
Upvotes: 2