I have a vue.js SearchTest component with a v-autocomplete
. It has watchers for select
and search
:
// SearchTest.vue
<template>
<v-autocomplete
v-model="select"
:items="items"
:loading="isLoading"
:search-input.sync="search"
hide-no-data
hide-selected
placeholder="Type search query"
return-object
></v-autocomplete>
</template>
<script>
export default {
data: () => ({
select: null,
isLoading: false,
search: null,
items: [],
}),
watch: {
select(val) {
console.log('select: ', val);
}
search(val) {
console.log('search: ', val);
},
},
};
</script>
<style></style>
I'm testing it as following. I expect both watchers should fire. In fact, only the select
watcher fired. The search
watcher did not.
// SearchTest.spec.js
import { createLocalVue, mount } from '@vue/test-utils'
import SearchTest from '@/components/SearchTest.vue'
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
describe('SearchTest.vue', () => {
const localVue = createLocalVue();
const vuetify = new Vuetify({});
let spy;
beforeAll(() => {
spy = jest.spyOn(console, 'log');
})
afterEach(() => {
spy.mockClear();
})
it('Select', () => {
const wrapper = mount(SearchTest, { localVue, vuetify });
// Triggers select.
wrapper.vm.select = 'select';
wrapper.vm.$nextTick(() => {
expect(spy).toBeCalled(); // OK
});
})
it('Search', () => {
const wrapper = mount(SearchTest, { localVue, vuetify });
// Should trigger search, but fails.
wrapper.vm.search = 'search';
wrapper.vm.$nextTick(() => {
expect(spy).toBeCalled(); // Fail
});
})
})
Any idea for passing the Search
test?
I don't know exactly what is happening, but I found that the v-autocomplete
internally sets the search property to null
on creation if no item is selected (source). It does so by using this.$nextTick
, which I suppose causes problems when setting the search value inside the test. I found two ways to mitigate this:
Nesting the change of search
and the assertion into another $nextTick
seems to the trick.
it('Search', () => {
const wrapper = mount(SearchTest, { localVue, vuetify });
wrapper.vm.$nextTick(() => {
wrapper.vm.search = 'search';
wrapper.vm.$nextTick(() => {
expect(spy).toBeCalled();
});
});
})
For this solution I took the initialisation of wrapper
and put it in beforeEach
. It does basically the same but I guess the timing of the mounting of the component is different, so it doesn't collide with the $nextTick
of v-autocomplete
.
describe('SearchTest.vue', () => {
const localVue = createLocalVue();
const vuetify = new Vuetify({});
let spy;
let wrapper;
beforeEach(() => {
wrapper = mount(SearchTest, { localVue, vuetify });
})
beforeAll(() => {
spy = jest.spyOn(console, 'log');
})
afterEach(() => {
spy.mockClear();
})
it('Select', () => {
// Triggers select.
wrapper.vm.select = 'select';
wrapper.vm.$nextTick(() => {
expect(spy).toBeCalled(); // OK
});
})
it('Search', () => {
// Should trigger search, but fails.
wrapper.vm.search = 'search';
wrapper.vm.$nextTick(() => {
expect(spy).toBeCalled(); // Fail
});
})
})