How to select a checkbox by its id and call setChecked? - vue.js

I'm writing a unit test to check that a method is called when a checkbox is clicked.
I'm using ElementUI and here is an extract from the component template:
<template lang="pug">
el-card
el-checkbox#checkAllCheckbox(
:indeterminate="isCheckAllIndeterminate"
v-model="checkAll"
#change="handleCheckAllChange"
) {{ $t('check all') }}
</template>
And here is my test:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import ElementUI from 'element-ui'
import MyComponent from '#/components/MyComponent'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(ElementUI)
it.only('Should check all', () => {
const wrapper = shallowMount(MyComponent, { localVue })
let checkAllCheckbox = wrapper.find('#checkAllCheckbox')
checkAllCheckbox.setChecked()
})
})
The test fails with then following error:
wrapper.setChecked() cannot be called on this element
How can I select the checkbox by its id and trigger the change event with the wrapper?
EDIT
Following #tao recommendations, I tried using mount instead of shallowMount in order to avoid stubbing the ElCheckbox component:
import { mount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import ElementUI from 'element-ui'
import MyComponent from '#/components/MyComponent'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(ElementUI)
it.only('Should check all', () => {
const wrapper = mount(MyComponent, { localVue })
let checkAllCheckbox = wrapper.find('#checkAllCheckbox')
checkAllCheckbox.setChecked()
})
})
But I still get the same error: wrapper.setChecked() cannot be called on this element. I also tried importing the ElCheckbox component and use it as a stub:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import ElementUI from 'element-ui'
import MyComponent from '#/components/MyComponent'
import ElCheckbox from 'element-ui/lib/checkbox'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(ElementUI)
it.only('Should check all', () => {
const wrapper = shallowMount(MyComponent, { localVue, stubs: {
'el-checkbox': ElCheckbox
}})
let checkAllCheckbox = wrapper.find('#checkAllCheckbox')
checkAllCheckbox.setChecked()
})
})
But still got the same error: wrapper.setChecked() cannot be called on this element.
After checking the resulting HTML as suggested, here is what it looks like:
<label id="checkAllCheckbox" class="el-checkbox is-checked" aria-controls="undefined">
<span class="el-checkbox__input is-checked"><span class="el-checkbox__inner"></span>
<input type="checkbox" aria-hidden="false" class="el-checkbox__original" value=""></span>
<span class="el-checkbox__label">Check all<!----></span>
</label>
ElementUI doesn't attribute the id to the checkbox itself but to the enclosing label. So I ended up using wrapper.find('#checkAllCheckbox input[type="checkbox"]') and it works.

shallowmount is a declaration: in this test I nor need neither want any of the subcomponents rendered. They can be safely replaced with a stub and they're irrelevant as far as this test is concerned.
Which means <el-checkbox> is replaced by an empty <el-checkbox-stub> and so are all the other sub-components in your template. But you can't call .setChecked() on a stub. You can only call it on an <input type="checkbox">.
One way to fix this would be to replace shallowMount in this test with mount, which would render <el-checkbox>'s markup properly.
Another way to fix it is to tell VueTestUtils not to stub this particular component, by using stubs. (To be exact, you're not telling it not to stub, you're telling it to stub it using something - it's just that the "something" is the actual component - all other sub-components will still be stubbed). Obviously, you'd have to import elCheckbox from the library and specify it in the shallowMount's stubs option, as shown in documentation.

Related

Vue.js - Element Plus - How to test el-dropdown component

I have a problem that I can't trigger el-dropdown menu. I've followed the testing approach done in element-plus repository but couldn't able to simulate mouseenter event and see whether dropdown menu is opened.
my code can be found below.
<template>
<el-dropdown>
<el-icon>
<MoreFilled/>
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>Send a message</el-dropdown-item>
<el-dropdown-item>Report</el-dropdown-item>
<el-dropdown-item>Block</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script lang="ts" setup>
import { MoreFilled } from '#element-plus/icons-vue';
</script>
and my test code can be found here
import { mount } from "#vue/test-utils";
import { nextTick } from "vue";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import EntryCardFooterDropdown from "../EntryCardFooterDropdown.vue";
import { ElTooltip } from "element-plus";
describe('EntryCardFooterDropdown', () => {
it('render', async () => {
const wrapper = mount(EntryCardFooterDropdown)
await nextTick()
const content = wrapper.findComponent(ElTooltip).vm as InstanceType<typeof ElTooltip>
vi.useFakeTimers();
const triggerElm = wrapper.find('.el-tooltip__trigger');
expect(content.open).toBe(false);
await triggerElm.trigger('mouseenter');
vi.runAllTimers();
expect(content.open).toBe(true);
})
})

Vue Utils invalid prop

Vue Util throws a console log error:
console.error
[Vue warn]: Invalid prop: type check failed for prop "icon". Expected Object, Array, String, got Undefined
I got a parent component in this case 'myComp' and inside I have another global component which is here Font Awesome, which is already imported. the error that I get is that prop icon is undefined, in 'myComp' prop icon doesn't exist, so I assume vue utils reads 'fa' component and looks for a prop icon in 'myComp'. All of my test pass, I just get this warning and want to get rid of it.
I Import my global components in an extra file called componentsbind.js, which already is tested and works.
import { library } from '#fortawesome/fontawesome-svg-core'
import { faUserSecret } from '#fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'
library.add(faUserSecret)
my component: this is a reusable component that I am testing
<template>
<button #click="handleClick">
<template v-if="loading">
<fa :icon="faChevronRight " class="fa-spin text-xl" />
</template>
<template v-else>
<slot />
</template>
</button>
</template>
inside my component I already Imported with
<script>
import { faChevronRight } from '#fortawesome/free-solid-svg-icons'
...
my test
import { shallowMount, RouterLinkStub } from '#vue/test-utils'
import MyComp from '#/components/MyComp.vue'
let wrapper
beforeEach(() => {
wrapper = shallowMount(MyComp, {
slots: {},
stubs: {}
})
})
afterEach(() => {
wrapper.destroy()
})
describe('MyComp Test', () => {
//here are my tests...
})
So my problem was that I wasn't importing vue use composition api.
And because I return the icon on setup function it was always undefined.
so in the file where I import all my global components I just added:
import VueCompositionAPI from '#vue/composition-api'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'
Vue.use(VueCompositionAPI)

How to test for the existance of a bootstrap vue component in unit tests with jest?

So I have some code that has a b-form-input component and I am testing whether that component renders. I am using wrapper.find({name: "b-form-input"}).exists() to determine whether that bootstrap vue component exists. However this function continually returns false when I know that the component is rendering. Could I have some help on how to do this correctly?
Looking at the bootstrap-vue source code, it looks like the name of the element is BFormInput and not b-form-input (it was registered using kebab-case):
https://github.com/bootstrap-vue/bootstrap-vue/blob/2fb5ce823a577fcc2414d78bd43ed9e5351cb1c0/src/components/form-input/form-input.js#L33
...
export const BFormInput = /*#__PURE__*/ Vue.extend({
name: 'BFormInput',
...
You have two options to locate the component; using the name, or the component constructor. For example:
import BootstrapVue, { BFormInput } from 'bootstrap-vue';
import { shallowMount, createLocalVue } from '#vue/test-utils';
import HelloWorld from '#/components/HelloWorld.vue';
const localVue = createLocalVue();
localVue.use(BootstrapVue);
describe('HelloWorld.vue', () => {
it('BFormInput exists', () => {
const wrapper = shallowMount(HelloWorld, { localVue })
expect(wrapper.find({ name: 'BFormInput' }).exists()).toBe(true);
expect(wrapper.find(BFormInput).exists()).toBe(true);
});
});

Getting [Vue warn]: Unknown custom element: <b-modal> even though bootstrap-vue is registered

A BootstrapVue b-modal component in a custom Vue component loads correctly in the browser. However, when testing using mocha+mochapack, it generates a Vue warning that the b-modal element is not registered. The test is using a localVue object that has BootstrapVue registered. All other bootstrap custom elements seem to be loading correctly, and do not generate any warnings.
I tried various things, including importing BModal from 'bootstrap-vue' and registering it as a component directly, but still got the same error.
import {mount, createLocalVue} from "#vue/test-utils"
import MyCustomModal from '../js/MyCustomModal';
const localVue = createLocalVue();
import BootstrapVue from 'bootstrap-vue'
localVue.use(BootstrapVue);
describe('MyCustomModal', () => {
let wrapper = mount(MyCustomModal,{
localVue
});
it('the content is "this is the content"', () => {
expect(wrapper.find(".modal-content").text()).toEqual('this is the content');
});
});
The custom Vue component:
<template>
<b-modal>
<div class="modal-content">this is the content</div>
<b-form>
my form
</b-form>
</b-modal>
</template>
<script>
export default {
data(){
return {};
}
}
</script>
The tests run correctly and pass, but it outputs the Vue warning for the b-modal element. It doesn't output the warning for b-form.
If only shallowMount not work.
You can try stub your bootstrap's components individually.
Like this:
import {shallowMount} from "#vue/test-utils";
import { BModal, BForm } from 'bootstrap-vue';
import MyCustomModal from '../js/MyCustomModal';
describe('MyCustomModal', () => {
let wrapper = shallowMount(MyCustomModal,{
stubs: {
"b-modal": BModal,
"b-form": BForm
}
});
it('the content is "this is the content"', () => {
expect(wrapper.find(".modal-content").text()).toEqual('this is the content');
});
});
You need to set the attachToDocument: true flag when you mount b-modal (or your test component/app). It needs reference to the document/body in order for it to open (needs to add classes, etc to <body> as well as a few listeners.
import Vue from 'vue';
import {mount} from "#vue/test-utils"
import MyCustomModal from '../js/MyCustomModal';
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
describe('MyCustomModal', () => {
let wrapper = mount(MyCustomModal);
it('the content is "this is the content"', () => {
expect(wrapper.find(".modal-content").text()).toEqual('this is the content');
});
});
Try that.

How to test vue-js-modal component?

In my app I using vue-js-modal.
My modal-test component:
<template>
<modal name="modal-test">
<div class="modal-test__content">I am modal</div>
</modal>
</template>
<script>
export default {
name: 'modalTest',
};
</script>
My unit-test:
import { expect } from 'chai';
import { mount, createLocalVue } from '#vue/test-utils';
import ModalTest from '#/modal/modalTest.vue';
import VModal from 'vue-js-modal';
const localVue = createLocalVue();
localVue.use(VModal);
describe('ModalTest.vue', () => {
it('modal', () => {
const wrapper = mount(ModalTest, {
localVue,
});
expect(wrapper.find('.modal-test__content').exists()).eq(true);
});
});
Tell me pls, how can I test exists div.'modal-test__content' inside modal?
I'm not familiar with vue-js-modal but in my case for custom modal I had to nest the use in the components prop. I think it may be because I'm using Vuetify, however.
import { expect } from 'chai';
import { mount, createLocalVue } from '#vue/test-utils';
import sinon from 'sinon';
import FileItem from '#/components/fileItem.vue';
import Modal from '#/components/common/Modal.vue';
import Vue from 'vue';
import Vuetify from 'vuetify'; // eslint-disable-line
Vue.use(Vuetify, {
components: {
Modal
}
});
Then Modal will be rendered on the page.
In the case of testing you have to remember that when you're testing the UI you have to think "What's the public interface", "Which one is the input and the output". In the example you provided there are no input. Everything is static so you don't need to test it.
On the other hand, you don't need to import the local vue, and instead of using mount you would prefer in most of the cases use shallowMount.
For finding the div you can do: expect(wrapper.find('div').exists()).toBe(true);
I know that probably you don't need this answer anymore, but may be useful for someone else.
Regards.