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
Related
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)
I have the following code:
<!-- App.vue -->
<template>
<button #click="login">Login</button>
</template>
<script lang="ts">
import { loggedIn } from '../state'
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
loggedIn,
}
},
methods: {
login() {
this.loggedIn = true
}
}
})
</script>
// state.ts
import { reactive, ref } from 'vue'
export const loggedIn = ref(true)
The code above has a compilation error (which shows both in VS Code and from vue-cli-service serve)
TS2322: Type 'true' is not assignable to type 'Ref<boolean>'.
> | this.loggedIn = true
I'm pretty sure that's how I'm supposed to do it, so I'm not sure why I'm getting an error. I can change the code to this and the error goes away: this.loggedIn.value = true But I'm pretty sure that's not how its supposed to work, and I get this runtime error:
Cannot create property 'value' on boolean 'false'
Why am I getting this compilation error in my original code?
Source: https://codesandbox.io/s/sad-cdn-ok40d
App.vue
<!-- App.vue -->
<template>
<div>
<button #click="login">Login</button>
<div>{{ data }}</div>
</div>
</template>
<script lang="ts">
import { loggedIn } from "./state";
export default {
name: "App",
setup() {
const data = loggedIn();
const login = () => (data.value = !data.value);
return { data, login };
},
};
</script>
State.ts
import { ref } from "vue";
export const loggedIn = () => ref(false);
Main.js
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
So the thing is, if we use the composition API, we should use the setup() method to setup out data and methods.
Since ref uses the .value to change the value, we don't need reactive.
Reactive is used for object values - which it will add a Proxy to watch over the key/values of the object.
In this case, we should use ref.
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.
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>
I tried to use vue-router inside actions of vuex, which is working fine at localhost.
However, i got errors when I tried to prepare store(for mock) by importing "actions" from store file.
Could you help me out in this issue?
versions
vue-test-utils: 1.0.0-beta.16
yarn: 1.5.1
vuejs: 2.5.13
vue-jest: 1.4.0
error msg
FAIL test/components/main.test.js
● Test suite failed to run
/Users/gulliver/Desktop/test/vue-test-utils-jest-example/node_modules/vue/dist/vue.esm.js:10809
export default Vue$3;
^^^^^^
SyntaxError: Unexpected token export
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:305:17)
at Object.<anonymous> (src/router/main.js:1:203)
at Object.<anonymous> (src/store/main.js:3:13)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 2.354s
Ran all test suites matching /test\/components\/main.test.js/i.
error An unexpected error occurred: "Command failed.
Exit code: 1
app/src/main.js
import Vue from "vue/dist/vue.esm";
import App from './App.vue'
import store from './store/main.js';
import router from './router/main.js';
new Vue({
el: '#app',
render: h => h(App),
store,
router,
})
app/src/store/main.js
import Vue from "vue";
import Vuex from 'vuex';
import router from '../router/main.js'
Vue.use(Vuex)
export const actions = {
locationTo(context, url){
router.push(url)
}
}
export default new Vuex.Store({
actions,
})
app/src/router/main.js
import Vue from "vue/dist/vue.esm";
import VueRouter from 'vue-router';
import root from '../components/root.vue';
import hoge from '../components/hoge.vue';
Vue.use(VueRouter)
export const routes = [
{ path: '/', component: root},
{ path: '/hoge', component: hoge},
];
export default new VueRouter({
mode: 'history',
routes
})
app/test/components/main.test.js
import Vue from "vue";
import Vuex from "vuex";
import { shallowMount, createLocalVue } from "#vue/test-utils";
import { actions } from "#/store/main"; //NOTE: this causes error
import _ from "lodash";
const localVue = createLocalVue();
import root from '#/components/root.vue'
import hoge from '#/components/hoge.vue'
describe('increment.vue', () => {
let propsData;
let store;
let wrapper;
beforeEach(() => {
propsData = _.cloneDeep(personObject)
store = new Vuex.Store(_.cloneDeep({
actions,
}))
const $route = {
path: '/hoge', components: hoge
}
wrapper = shallowMount(root, {
localVue,
propsData,
store,
use: ['Vuex'],
stubs: ['router-view'],
mocks: {
$route
}
})
});
it('test:router in store', () => {
// check if URL changed after action executed
});
})
components
// App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
// root.vue
<template>
<div>
<p>root component</p>
</div>
</template>
<script>
export default {
mounted () {
this.$store.dispatch('locationTo', '/hoge')
},
}
</script>
// hoge.vue
<template>
<div>
<p>hoge template</p>
</div>
</template>
This error is related to the fact that Jest runs in a Node.js environment, and you are using export default, which does not work by default in Node.js (Node uses module.exports). I guess you are using webpack for your dev/production build, but not for the test environment.
Are you using Jest? If so, you will need to set up babel-jest so Jest knows how to read ES Modules syntax (import/export).
Read more here: https://vue-test-utils.vuejs.org/guides/#testing-single-file-components-with-jest
This can be annoying to set up, let me know if you need more help to get it working.