I'm trying to stub a tel input plugin I'm using in one of my Vue component.
This is what I've tried, I think I almost got it but I'm missing one small detail.
This is my spec file:
import { mount, createLocalVue } from '#vue/test-utils';
import ModalNewCustomer from "../../../resources/js/subviews/customers/components/ModalNewCustomer";
// Stubs
const telInput = {
template: `<input type="tel">`
}
import moment from 'moment';
describe('Modal New Customer',() => {
let wrapper,mockCheckNameDuplicate;
let districts = [
{"id":1,"name":"Aberdeen"},
{"id":2,"name":"Admiralty"},
{"id":3,"name":"Ap Lei Chau"},
{"id":4,"name":"Beacon Hill"},
{"id":5,"name":"Braemar Hill"},
{"id":6,"name":"Causeway Bay"},
{"id":8,"name":"Central"}
];
let newCustomerData ={
firstName: 'Loulou',
lastName: 'Le Loup',
emailAddress: 'email#google.com',
sex: 'M',
primaryPhoneNumber: '+85200001111',
secondaryPhoneNumber: '+85233334444',
birthday: "2020-11-30",
wayOfContact: 'email',
electroOrNot: 1,
asiaMilesMembership: '00001111'
};
beforeEach(() => {
mockCheckNameDuplicate = jest.spyOn(ModalNewCustomer.methods, 'checkNameDuplicate');
wrapper = mount(ModalNewCustomer,{
localVue,
propsData:{
districts: districts
},
data(){
return{
COMPANY_NAME: 'HK'
}
},
router,
stubs:{
"vue-tel-input": telInput
}
});
flushPromises();
jest.resetModules();
jest.clearAllMocks();
});
afterEach(() => {
flushPromises();
jest.resetModules();
jest.clearAllMocks();
})
it('can input new customer data and submit it',async() => {
const {vm} = wrapper;
let primaryPhoneNumberInput = wrapper.find('#primaryPhoneNumber');
await primaryPhoneNumberInput.setValue(newCustomerData.primaryPhoneNumber)
expect(primaryPhoneNumberInput.element.value).toBe(newCustomerData.primaryPhoneNumber);
console.log(primaryPhoneNumberInput.html())
console.log(vm.newCustomer.primaryPhoneNumber)
});
});
In my vue component, the vue tel input looks like this:
<vue-tel-input
id="primaryPhoneNumber"
:default-country="COMPANY_NAME"
validCharactersOnly
v-model="newCustomer.primaryPhoneNumber"
:inputOptions="inputOptions">
</vue-tel-input>
The problem is when I set the value of my stub component, the data of the Vue component I'm testing is not being updated, when it should be rerendered thanks to v-model.
Thanks for your help.
Related
Im geting this error:
[Vue warn]: Unknown custom element: <b-taginput> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
on this test:
import MultipleChoice from '#/components/MultipleChoice';
import Buefy from 'buefy';
const localVue = createLocalVue();
localVue.use(Buefy);
describe('MultipleChoice.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(MultipleChoice, {
propsData: {
choices: ['en', 'de', 'it'],
}
});
});
test('renders correctly', () => {
expect(wrapper).toMatchSnapshot();
});
});
the test itself passes, but this warning keeps showing up.
This is my component:
<b-taginput
v-model="value"
:data="filteredData"
expanded
autocomplete
open-on-focus
clear-on-select
field="display_name"
icon="label"
#input="update()" />
does anybody knows what is going on?
I figured it out. What was missing:
import MultipleChoice from '#/components/MultipleChoice';
import Buefy from 'buefy';
const localVue = createLocalVue();
localVue.use(Buefy);
describe('MultipleChoice.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(MultipleChoice, {
propsData: {
choices: ['en', 'de', 'it'],
},
stubs: {
transition: false,
'b-taginput': true
}
});
});
test('renders correctly', () => {
expect(wrapper).toMatchSnapshot();
});
});
I am testing though Jest on the Vue 2.x, nuxtjs and #nuxtjs/composition-api.
However, the state value in the components has undefined value when testing though jest
List.spec.js
import Vue from 'vue';
import Vuetify from 'vuetify';
import { createLocalVue, shallowMount } from '#vue/test-utils';
import List from '#/components/home/list.vue';
Vue.use(Vuetify);
describe('List.vue', () => {
const localVue = createLocalVue();
let vuetify;
const $t = () => {};
const localePath = () => {};
beforeEach(() => {
vuetify = new Vuetify();
localVue.use(vuetify);
});
const mockOrder = [
{
coardshare: {
cs_id: 123,
},
},
{
talkboard: {
cs_id: 123,
},
},
];
it('11111', () => {
const wrapper = shallowMount(List, {
localVue,
vuetify,
propsData: { data: mockOrder },
mocks: { $t, localePath },
data() {
return {
data: mockOrder,
};
},
});
expect(wrapper.html()).toMatchSnapshot();
const title = wrapper.find('.v-card__title > span');
expect(title.text()).toBe('Foobar');
});
});
List.vue
<template>
...
<div v-for="item in state.data.talkboard" :key="item.cs_id">
<ListItem :item="item"></ListItem>
</div>
...
</template>
<script>
import { reactive, onMounted, useContext } from '#nuxtjs/composition-api';
import axios from 'axios';
import Header from './header';
import ListItem from './list-item.vue';
export default {
name: 'ListHome',
components: {
Header,
ListItem,
},
setup() {
const state = reactive({
data: [],
});
const { store } = useContext();
const fatch = async () => {
....
};
onMounted(fatch);
return {
state,
fatch,
};
},
};
</script>
error message
TypeError: Cannot read property 'data' of undefined
I am testing though Jest on the Vue 2.x, nuxtjs and #nuxtjs/composition-api.
However, the state value in the components has undefined value when testing though jest
why error on this ?? because of composition API that define the state with reactive() function ??
In your test file maybe you can try something like this:
it('11111', () => {
const wrapper = shallowMount(List, {
localVue,
vuetify,
propsData: { data: mockOrder },
mocks: { $t, localePath },
data: () => {
return {
data: mockOrder,
};
},
});
Somehow I'm not able to see the items of the v-autocomplete inside the html during the test and also setting an item does not work. Below the v-autocomplete (which is loading the items on mount) and the test.
// v-autocomplete.vue
<template>
<v-autocomplete
:items="items"
item-text="name"
item-value="id"
></v-autocomplete>
</template>
<script>
export default {
data() {
return {
items: [],
};
},
mounted() {
service.getItems().then((response) => {
this.items = response;
});
},
};
</script>
The test looks like this:
//v-autocomplete.spec.js
import Vue from 'vue';
import Vuetify from 'vuetify';
import VAutocomplete from '#/components/v-autocomplete.vue';
import '#testing-library/jest-dom';
import { createLocalVue, mount } from '#vue/test-utils';
import service from '#/service/service';
Vue.use(Vuetify);
const items= [
{ name: 'Item 1', id: 1 },
{ name: 'Item 2', id: 2 },
];
const getItemsSpy = jest
.spyOn(service, 'getItems')
.mockImplementation(() => {
return new Promise((resolve) => {
resolve(items);
});
});
describe('v-autocomplete.vue', () => {
let localVue;
let vuetify;
let wrapper;
beforeEach(() => {
jest.clearAllMocks();
localVue = createLocalVue();
vuetify = new Vuetify();
});
it('should load the items', async () => {
wrapper = mount(VAutocomplete, {
localVue,
vuetify,
propsData: {},
});
expect(getItemsSpy).toBeCalledTimes(1);
for (const item of items) {
expect(getByText(wrapper.element, item.name)).toBeTruthy(); // Will fail -> Unable to find an element with the text
}
});
});
The test expect(getByText(wrapper.element, item.name)).toBeTruthy(); will fail with the message Unable to find an element with the text. Also printing the html with console.log(wrapper.html()) does not show the items of the v-autocomplete. So my question would be:
How could I test, if the items were loaded?
How could I set one of the loaded as the selected item of the v-autocomplete?
You can open autocomplete menu on click .v-input__slot. I do no think, it is best practice to test library functionality.
Unit test:
it("should load the items", async () => {
wrapper = mount(VAutocomplete, {
localVue,
vuetify,
propsData: {}
});
const autocomplete = wrapper.element;
const autocompleteControls = autocomplete.find(".v-input__slot");
autocompleteControls.trigger("click");
await wrapper.vm.$nextTick();
expect(getItemsSpy).toBeCalledTimes(1);
for (const item of items) {
expect(getByText(wrapper.element, item.name)).toBeTruthy();
}
});
Source:
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VSelect/\_\_tests__/VSelect.spec.ts
I have a simple Vue component that uses Quasar button
<template>
<div>
<span class="count">{{ count }}</span>
<q-btn #click="increment">Increment</q-btn>
</div>
</template>
<script>
export default {
name: 'TestComponent',
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count += 1;
},
},
};
</script>
I create a unit test for it
import { mount, createLocalVue } from '#vue/test-utils';
import { Quasar, QBtn } from 'quasar';
import TestComponent from '../TestComponent';
describe('TestComponent', () => {
let wrapper;
beforeEach(() => {
const localVue = createLocalVue();
localVue.use(Quasar, { components: { QBtn } });
wrapper = mount(TestComponent, { localVue });
});
it('renders the correct markup', () => {
expect(wrapper.html()).toContain('<span class="count">0</span>');
});
// it's also easy to check for the existence of elements
it('has a button', () => {
expect(wrapper.contains('button')).toBe(true);
});
});
My problem:
If I run the test cases (it function) one by one at a time the test will pass. For example, remove the second it('has a button'...) then run the test. It'll pass. It's the same when removing the first it('renders the correct markup'...)
However, If I keep all test cases then run the test. The second test case will fail with an error
console.error node_modules/vue/dist/vue.common.dev.js:630
[Vue warn]: Unknown custom element: <q-btn> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <TestComponent>
<Root>
What am I doing wrong?
Try removing the before-each. I saw this problem too. Can't remember what exactly fixed it but this is how I have my describe block.
describe('Mount Quasar', () => {
const localVue = createLocalVue()
localVue.use(Quasar, { components })
const wrapper = shallowMount(Register, {
localVue,
stubs: ['router-link', 'router-view']
})
const vm = wrapper.vm
it('passes the sanity check and creates a wrapper', () => {
expect(wrapper.isVueInstance()).toBe(true)
})
})
You will need to import quasar into either webpack, babel, or jest.
In the jest.config.js file
Add
moduleNameMapper: {
quasar: "quasar-framework/dist/umd/quasar.mat.umd.min.js"
},
I unfortunately can't attach all code or create a gist because the project I'm working on is related to work but I can give enough detail that I think it will work.
I'm trying to mock a call to an action that is stored in a different module but for the life of me I can't figure out how to. I'm able to create a Jest spy on the store.dispatch method but I want to be able to resolve the promise and make sure that the subsequent steps are taken.
The method in the SFC is
doSomething(data) {
this.$store.dispatch('moduleA/moduleDoSomething',{data: data})
.then(() => {
this.$router.push({name: 'RouteName'})
})
.catch(err => {
console.log(err)
alert('There was an error. Please try again.')
})
},
This is what my test looks like:
import { mount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import Vuetify from 'vuetify'
import Component from '#/components/Component'
import moduleA from '#/store/modules/moduleA'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Vuetify)
describe('Component.vue', () => {
let actions
let store
const $router = []
beforeEach(() => {
actions = {
moduleDoSomething: jest.fn((payload) => {
return Promise.resolve({
status: 200,
})
})
}
store = new Vuex.Store({
state: {},
modules: {
moduleA: {
actions
}
},
})
})
it('does something', () => {
const wrapper = mount(Component, {
store,
localVue,
mocks: {
$router,
},
})
let button = wrapper.find('button that calls doSomething')
button.trigger('click')
expect(actions.moduleDoSomething).toHaveBeenCalled()
expect(wrapper.vm.$router[0].name).toBe('RouteName')
})
})
The following test passes, but I don't want to just test that the action was dispatched; I also want to test things in the "then" block.
it('does something', () => {
const dispatchSpy = jest.spyOn(store, 'dispatch')
const wrapper = mount(Component, {
store,
localVue,
mocks: {
$router,
},
})
let button = wrapper.find('button that calls doSomething')
button.trigger('click')
expect(dispatchSpy).toHaveBeenCalledWith('moduleA/moduleDoSomething',{data: data})
})
})
I managed to solve this problem by simply making the module namespaced in the mocked store.
store = new Vuex.Store({
state: {},
modules: {
moduleA: {
actions,
namespaced: true
}
},
})
I'll delete the question in a little bit