Testing two different click events with vue-test-utils - vue.js

Trying to test whether two methods are called on a click event on two different buttons. The result returns that the mock method on the second event is not called.
The template is the following (JobsSearch.vue):
<template>
<b-input-group class="sm-2 mb-2 mt-2">
<b-form-input
:value="this.searchConfig.Keyword"
#input="this.updateJobsSearchConfig"
class="mr-2 rounded-0"
placeholder="Enter Search term..."
/>
<b-button
#click="searchJobs"
class="rounded-0 search-button"
variant="primary"
>
Search
</b-button>
<b-button
#click="resetFilter"
class="rounded-0 ml-2 reset-button"
variant="primary"
>
Reset
</b-button>
</b-input-group>
</template>
This is my JobsSearch.spec.js
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
// BootstrapVue is necessary to recognize the custom HTML elements
import BootstrapVue from 'bootstrap-vue'
import JobsSearch from '#/components/jobs/JobsSearch.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(BootstrapVue)
describe('JobsSearch.vue', () => {
let state
let store
let wrapper
let searchJobs
let resetFilter
let emitEvents
beforeEach(() => {
state = {
jobs: {
paged: {
size: 100,
page: 1
},
search: {
Keyword: '',
status: [],
ucode: []
}
}
}
store = new Vuex.Store({
state
})
searchJobs = jest.fn()
resetFilter = jest.fn()
emitEvents = jest.fn()
wrapper = shallowMount(JobsSearch, {
methods: {
searchJobs,
resetFilter,
emitEvents
},
localVue,
store })
})
afterEach(() => {
wrapper.destroy()
})
// START: Method tests
it('should call jobsSearch method on search button click event', () => {
wrapper.find('.search-button').trigger('click')
expect(searchJobs).toHaveBeenCalled()
})
it('should call resetFilter method on reset button click event', () => {
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})
// END: Method tests
})
I expect both searchJobs and resetFilter to be called, however, only the first test passes
Any ideas, please?

It seems like that the resetFilter() method is not stubbed correctly.
Adapt the second test as follows:
it('should call resetFilter method on reset button click event', () => {
const resetFilterStub = jest.fn();
wrapper.setMethods({ resetFilter: resetFilterStub });
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})

Related

Testing Vuetify with Vue-test-utils - Cannot set value to v-text-field

I want to test simple login form with two v-text-fields and one v-btn. I'm testing with Jest and Vue test utils.
Code looks like that:
<v-text-field
v-model="form.email"
test-id="input-email">
</v-text-field>
My test looks like that:
import Login from './login.vue'
import { shallowMount } from '#vue/test-utils'
describe('login', () => {
it('set value', () => {
const wrapper = shallowMount(Login)
const emailInput = wrapper.findComponent('[test-id="input-email"]')
emailInput.setValue('john#example.com')
})
})
And I'm getting error:
[vue-test-utils]: wrapper.setValue() cannot be called on this element
and points here:
emailInput.setValue('john#example.com')
How can I set value to v-text-field?
I tried to repeat your example and it works
Component:
<template>
<v-text-field
v-model="form.email"
test-id="input-email"
/>
</template>
<script>
export default {
data () {
return {
form: { email: '' },
}
},
}
</script>
Test:
import Input from './input.vue'
import { mount } from '#vue/test-utils'
describe('input', () => {
it('should set v-model', () => {
const wrapper = mount(Input)
const input = wrapper.findComponent('[test-id="input-email"]')
input.setValue('test')
})
})

Vue 3 Vitest - Test v-model on input

I am trying to create a test for a BaseInput with a v-model prop. The expectation is the component will emit the changed input. When I update the input in the Vitest framework, there does not seem to be an emit triggered.
Component
<template>
<label v-if="label">{{ label }}</label>
<input
v-bind="$attrs"
:value="modelValue"
:placeholder="label"
#input="$emit('update:modelValue', $event.target.value)"
class="field"
/>
</template>
<script>
export default {
props: {
label: {
type: String,
default: "",
},
modelValue: {
type: [String, Number],
default: "",
},
},
};
</script>
Test
import { describe, it, expect, beforeEach } from "vitest";
import { mount } from "#vue/test-utils";
import BaseInput from "#/components/base/BaseInput.vue";
describe("BaseInput", () => {
let wrapper
beforeEach(() => {
wrapper = mount(BaseInput, {
propsData: {
label: 'Test Label',
modelValue: 'Test Value'
}
})
})
it('emits input value when changed', async () => {
// Assert input value gets the new value
await wrapper.find('input').setValue('New Test Value')
expect(wrapper.find('input').element.value).toBe('New Test Value')
// Assert input event is emitted
await wrapper.vm.$nextTick()
expect(wrapper.emitted().input).toBeTruthy() //this fails
})
});
Result: there is nothing emitted from the input when the value is set.
How can the input be set to prove the component emits the new value of the input component?
This is actually discussed as an example in the Vue Test Utils examples: https://test-utils.vuejs.org/guide/advanced/v-model.html#a-simple-example
Here is how you test v-model in Vue 3
import { describe, it, expect, beforeEach } from "vitest";
import { mount } from "#vue/test-utils";
import BaseInput from "#/components/base/BaseInput.vue";
describe("BaseInput", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(BaseInput, {
propsData: {
label: "Test Label",
modelValue: "Test Value",
"onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
},
});
});
it("emits input value when changed", async () => {
// Assert input value gets the new value
await wrapper.find("input").setValue("New Test Value");
expect(wrapper.props("modelValue")).toBe("New Test Value");
});
});

How to test a vue vuetify v-autocomplete

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

Quasar Unknown custom element error in unit test

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"
},

Triggered event on b-button doesn't show on wrapper.emitted()

I'm new to testing vue components and right now I'm trying to test that clicking a b-button component trigger an event and calls a method.
The test seems to fail because the 'click' event doesn't get triggered.
Also note that i'm using Nuxt.js
The component containing b-button is the following:
<template>
<div>
<b-form-input name="username" type="text" v-model="username"></b-form-input>
<b-form-input name="password" type="password" v-model="password"></b-form-input>
<b-button #click="login">Login</b-button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
data() {
return {
username: "",
password: ""
};
},
methods: {
...mapActions({
dispatchLogin: "auth/login"
}),
login: () => {
const response = this.dispatchLogin(this.username, this.password);
response.then(this.$router.push("/dashboard"));
}
}
};
</script>
And the test is written like that:
test('call login when Login button gets clicked', async () => {
expect.assertions(2);
moxios.stubRequest('/api/auth/login', {
response: 200
});
const store = new Vuex.Store(storeConfig);
const mocks = {
login: jest.fn()
}
const wrapper = shallowMount(login, { localVue, store, mocks });
debugger;
// Note that b-button is stubbed as <button></button>
wrapper.find('button').trigger('click');
await flushPromises()
expect(wrapper.emitted('click')).toHaveLength(1);
expect(auth.actions.login).toHaveBeenCalledTimes(1);
});
The problem is that I can't find any click event in wrapper.emitted().