Vue Utils invalid prop - vue.js

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)

Related

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 this component?

I have a Page level component which implements a component BookingInformation with slots. In the Page component, it's got another component BookingInformationHeader with slots. header and default.
My question is, how should I set up my test so that I can test that the GoogleConversionTrackingImage is visible when #Reservation.State wasBookingJustMade changes to true?
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { Reservation } from "#/store/vuex-decorators";
import { BookingInformation, BookingInformationHeader } from "#/components";
import GoogleConversionTrackingImage from './components/GoogleConversionTrackingImage.vue';
#Component({
components: {
BookingInformation,
BookingInformationHeader,
GoogleConversionTrackingImage
}
})
export default class ConfirmationPage extends Vue {
renderTrackingImage: boolean = false;
#Reservation.State wasBookingJustMade: boolean;
}
</script>
<template>
<booking-information page-type="confirmation-page" class="confirmation-page">
<template slot="header" slot-scope="bookingInfo">
<booking-information-header>
<template slot="buttons">
// some buttons
</template>
</booking-information-header>
<google-conversion-tracking-image v-if="wasBookingJustMade" />
</template>
</booking-information>
</template>
By using vue test utils https://vue-test-utils.vuejs.org/ and chai https://www.chaijs.com/ in your test file you can do something like:
import mount from "#vue/test-utils";
import expect from "chai";
const wrapper = mount(BookingInformation,<inner components you want to test>);
expect(googleImage.exists()).to.be.true;
wrapper.setData({
wasBookingJustMade: true,
});
const googleImage = wrapper.find("google-conversion-tracking-image");
expect(googleImage.exists()).to.be.false;
You'll probably need to import the page level component as well.
You can give an id to the component you want to find and then search by id.

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.

Uncaught TypeError: Cannot read property 'firstname' of undefined. on loading a component that has an inner component

create.vue
<template>
<div class="content">
<base-input type="text" id="firstname" v-model="firstname"/>
</div>
</template>
<script>
import BaseInput from 'BaseInput.vue'
export default {
data () {
return {
firstname: ''
}
}
}
</script>
create.spec.js
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Create from '/Create.vue'
import VueI18n from 'vue-i18n'
const wrapper = shallowMount(Create)
const vm = wrapper.vm
console.log(vm)
As you see, while loading the template using shallow Mount.
it throws the error,
Uncaught TypeError: Cannot read property 'firstname' of undefined at
webpack:///node_modules/#vue/test-utils/dist/vue-test-utils.js:5652:0
<- index.6fc62f9fc7f0f4a43161.js:9693
You need to add your BaseInput component to your export default. Now vue don't know what BaseInput is (info, if you are using such as Babel or Webpack) . I also changed the data line because there need to be a : after data and function before the () (example). If you do it like this it should work:
<script>
import BaseInput from 'BaseInput.vue'
export default {
components: {
BaseInput
}
data: function() {
return {
firstname: ''
}
}
}
</script>

vuejs unit test throwing log message because of inner component

I have a component like this
<template>
<p>{{ msg }}</p>
<inner-comp></inner-comp>
</template>
<script>
import InnerComp from './components/InnerComp .vue';
export default {
props: ['msg'],
components: {
innerComp : InnerComp
}
}
</script>
and the unit test
import Vue from 'vue'
import MyComponent from './MyComponent.vue'
// helper function that mounts and returns the rendered text
function getRenderedText (Component, propsData) {
const Ctor = Vue.extend(Component)
const vm = new Ctor({ propsData }).$mount()
return vm.$el.textContent
}
describe('MyComponent', () => {
it('renders correctly with different props', () => {
expect(getRenderedText(MyComponent, {
msg: 'Hello'
})).toBe('Hello')
})
})
Test is passing, but it is throwing LOG ERROR like this
'[Vue warn]: Failed to mount component: template or render function not defined.
(found in <InnerComp>)'
Assuming you are using webpack, you should import component like
import Vue from 'vue'
import MyComponent from 'src/components/MyComponent.vue'
in your unit test file