How to test vue-js-modal component? - vue.js

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.

Related

is it available to call the methods where in the vue component from the plugin?

I wanted to access the vue.data or methods in the plugin.
no matter what I tried several times, it didn't work.
such as eventBus, Mixin etc...
so I'm curious about the possibility to call the methods like that.
thank you for reading this question.
here is the custom component.
<template>
<div>
<v-overlay :value="isProcessing">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ProgressCircular extends Vue {
private isProcessing: boolean;
startProcess() {
this.isProcessing = true;
}
}
</script>
and this is the plugin source.
import ProgressCircular from '#/components/ProgressCircular.vue';
import { VueConstructor } from 'vue';
import Vuetify from 'vuetify/lib';
import vuetify from './vuetify';
export default {
install(Vue: VueConstructor, options: any = {}) {
Vue.use(Vuetify);
options.vuetify = vuetify;
Vue.component('progress-circular', ProgressCircular);
Vue.prototype.$fireProgressing = function () {
// it didn't work
// I just wanted to access the method where in the Vue Component
// ProgressCircular.startProcess();
};
},
};
use the plugin syntax to extend vue like:
Vue.use({
install: Vue => {
Vue.prototype.$fireProgressing = () => {
};
}
});
or
Vue.use(YOURPLUGIN);
before you mount vue

Composition api use this

I am using Vue 3 with Composition API, and I want to use a third-party package (for example #meforma/vue-toaster), and it should be used like this (in Options API):
import Toaster from '#meforma/vue-toaster';
createApp(App).use(Toaster).mount('#app')
and then in the component:
this.$toast.show(`Hey! I'm here`);
this.$toast.success(`Hey! I'm here`);
this.$toast.error(`Hey! I'm here`);
this.$toast.warning(`Hey! I'm here`);
this.$toast.info(`Hey! I'm here`);
But this is not working inside the Composition API's setup() function.
#meforma/vue-toaster installs $toast on the application context, which can be accessed from getCurrentInstance().appContext.globalProperties in setup():
<template>
<button #click="showToast">Show toast</button>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
setup() {
const $toast = getCurrentInstance().appContext.globalProperties.$toast
return {
showToast() {
$toast.show(`Hey! I'm here`)
$toast.success(`Hey! I'm here`)
$toast.error(`Hey! I'm here`)
$toast.warning(`Hey! I'm here`)
$toast.info(`Hey! I'm here`)
setTimeout($toast.clear, 3000)
}
}
}
}
</script>
i've the same issue.
So i've found and easy way to do:
I'm using Vite BTW.
my main.js
import { createApp } from 'vue'
import App from './App.vue'
import Toaster from '#meforma/vue-toaster';
let app = createApp(App)
app.use(Toaster, {
position: 'top-right'
}).provide('toast', app.config.globalProperties.$toast)
app.mount('#app')
my component:
import { inject } from 'vue'
export default {
name: 'table-line',
setup(props) {
const toast = inject('toast');
toast.success(`it works !`)
return {toast}
}
}
Hope it could be helpful
The setup function runs one-time before the component is created. It lacks the this context and may not be where you would want to place these anyway. You could try putting it into a method that you can call via a button.

How to select a checkbox by its id and call setChecked?

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.

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.