How to test the layout method in Nuxt (Vue) with jest? - vue.js

I want to test the "layout" method in a Nuxt page. The structure is as below. I've simplified the code to better serve the question.
<template>
<!-- insert any dom here -->
</template>
<script>
export default {
layout({ isDesktop }) {
return `default/${isDesktop ? 'desktop' : 'mobile'}/index`;
},
head() {
return {};
},
name: 'PageName',
meta: {
anyMetaProperty: true
},
data() {
let component = import( `~/path/to/the/child/component`);
return {
component,
};
},
};
</script>
I've successfully tested other parts of the code since they are similar to typical Vue component, but I had no luck with the "layout" method. I've tried accessing wrapper.vm.layout in the test, but it returns undefined. I also have tried mocking the method with jest.fn() mockReturnedValue function, but the method stays uncovered when I checked the coverage by running jest --coverage.
Any help would be appreciated. Thank you!

Related

The object sent as prop is undefined while testing a Vue SFC with Jest

I want to test a Vue single file component which receives a prop as input. When I mock the prop, which is an object, I get an error that the object is undefined, The error comes from the HTML where the values of the object are used. If I make the prop to be a string for example (and I remove answer.value and :class="{'active': answer.selected}" from HTML), everything works fine.
Component:
<template>
<div class="answer-container" #click="setActiveAnswer()" :class="{'active': answer.selected}">
<div class="answer">
<p>{{answer.value}}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Answer',
props: {
answer: Object,
},
methods: {
setActiveAnswer() {
this.$emit('selectedAnswer', this.answer);
}
}
}
</script>
Test file:
import { mount } from '#vue/test-utils'
import Answer from './../../src/components/Answer'
describe('Answer', () => {
it('should receive "answer" as prop', () => {
const answer = {
value: 'testAnswer',
selected: true
};
const wrapper = mount(Answer, {
propsData: {
answer: answer
}
});
expect(wrapper.props().answer.value).toBe('testAnswer');
})
})
The error I get is:
TypeError: Cannot read property 'selected' of undefined
Please advise what am I doing wrong. Thanks!
I managed to fix this by adding a v-if="answer" on <div class="answer-container" ..., which is quite strange (as this is not async data) since the code works fine when checking the application in the browser - the problem only appeared while unit testing the component. I suppose there is also a fix in a Jest/Unit testing way, something like declaring the prop after the component finished rendering/mounting...

Undefined variable - While api fetch | Axios | Props

My main component - Home
A really simple component, I pass the fetch variable to another component.
<template>
<Page actionBarHidden="true">
<ComponentA :api="api.somevariable"></ComponentA>
</Page>
</template>
<script>
import axios from "axios";
import ComponentA from "./ComponentA.vue";
export default {
data() {
return {
isLoading: false,
result: []
};
},
components: {
ComponentA,
},
created() {
this.loadData();
},
methods: {
async loadData() {
let self = this;
console.log("fetch");
self.isLoading = true;
const { data } = await Endpoints.get();
self.isLoading = false;
self.api = data;
console.log(data); // returns the data as intended
}
}
</script>
The componentA is also simple
<template>
<Label :text="somevariable"></Label>
</template>
<script>
export default {
data() {
return {
somevariable: 0
};
},
props: {
api: {
type: Number,
required: true
}
},
mounted() {
this.somevariable = this.api;
}
};
</script>
The error I am getting is [Vue warn]: Invalid prop: type check failed for prop "api". Expected Number with value NaN, got Undefined in the componentA, after some quoting and requoting of console.logs it actually picks up the value. I am not sure why is that, is my approach wrong? This frustrates me, can't figure it out for some hours already.
api isn't defined in the data for the first component, so it won't be reactive. That should be giving you a warning message in the console.
data () {
return {
api: null,
isLoading: false,
result: []
};
}
The second problem is that when the component first renders it won't yet have loaded api from the server. Using await won't help with this, rendering the template will happen before the asynchronous request has completed.
Given the way componentA is currently written it won't be able to cope with api being missing when it is first created. So you'll need to use a v-if to defer creation until that data is available:
<ComponentA v-if="api" :api="api.somevariable"></ComponentA>
Without the v-if check it'll just be passing the initial value of api, which in your original code is undefined. That is what caused the warning mentioned in the question.
When you talk about 'quoting and requoting of console.logs', I would assume that those changes are just triggering hot reloading, which could easily cause components to re-render with the new data. That wouldn't happen otherwise because of the lack of reactivity caused by api not being included in the original data.

Testing Vue Component: Failed to mount component: template or render function not defined

I'm writing a test for a Vue component using Mocha and I can't seem to resolve this warning:
[Vue warn]: Failed to mount component: template or render function not defined.
After a bit of research, it seems like most other instances of this warning appearing involve using vue-router. However, I'm not using that library.
I am seeing the warning in even a very basic component like the one below. It roughly follows this example: Testing Single-File Components with Mocha + webpack.
<template>
<div>
<ul>
<li v-for="item in items">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
items: Array,
required: true,
},
data () {
return {}
},
}
<script>
<style>
</style>
Everything works fine in the browser, but the tests demonstrate some unusual behavior. Here are two tests, both are very basic. The first passes and the second fails. Both tests trigger the warning about the template or render function not being defined.
import { mount } from '#vue/test-utils';
import Foo from '../../foo.vue';
describe('Foo', () => {
it('works', () => {
const wrapper = mount(Foo, {
propsData: {
items: ['a','b','c']
}
});
expect(wrapper.isVueInstance()).toBeTruthy()
})
it('contains a property', () => {
const wrapper = mount(Foo, {
propsData: {
items: ['a','b','c']
}
});
expect(wrapper.props()).toEqual({items: ['a','b','c']})
})
});
The second test fails because it seems like the props have not been set. The test failure says:
Expected: ["a", "b", "c"]
Received: {}
Based on the ue-test-utils documentation, I figured this would be a straightforward test, so I'm confused about why it's failing at all.
I'm not sure if there's some pre-compilation piece that I'm missing or something else. I've tried running these tests with both mocha-webpack and the more up-to-date mochapack test runner but the result is the same.
Any advice on how to remove that warning and get these tests to pass?
I am testing my vue components with jest and i am no expert in this topic but i could imagine that you havent configured your enviroment properly.
At least for jest i had to configure a transform Object like this.
"transform": {
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"^.+\\.js$": "<rootDir>/node_modules/babel-jest"
},
The point is, that this transformation changes a *.vue file into javascript.
And in this step the render function is created.
Perhaps this is missing and thats why you get that error.
But i can't explain why the first test succeeded.
Hopefully this could help. Otherwise i delete this post.
;)

mixin method is undefined in created hook of component

<template>
<section>
Top
</section>
</template>
<script>
import api from '../../server/api.ts';
export default {
name: 'Questions',
mixins: [api],
data() {
return {
templates : getTemplates(),
};
},
created() {
// **[Vue warn]: Error in created hook: "ReferenceError: getTemplates is not defined"**
this.templates = getTemplates();
},
};
</script>
getTemplates function is working fine if i click on link but getting error in all life hooks of Vue js component.
Thanks for help in advance !
You forgot this for the function. Loading mixins has the same effect as if the content in it would be in your actual component. You need to call the methods the same way as you would call a local function. Always with this.
So change it to:
created() {
this.templates = this.getTemplates();
},

Call a VueJS method inside a component outside 'export default'

I'm trying to call a function inside 'method' from outside. However, it isn't working.
Github issue reporting the same: https://github.com/vuejs/vue/issues/329
vm.test(); // call a function in method, not working
this.vue.test() // not working
export default {
methods: {
test: function() {
alert('test fuction called');
}
}
}
It is not very clear what the actual goal of the original poster is, however this is how you can call a method on a Vue instance, after creating it:
var viewModel = new Vue({
el: "#app",
data: {
msg: "Hello there"
},
methods: {
test: function() {
alert('test fuction called');
}
}
});
viewModel.test();
Working example: https://jsfiddle.net/Daryn/Lja7pake/3/
If you are exporting a single file component then try this:
example.js
<script>
export default {
methods: {
test: function() {
alert('test fuction called');
}
}
}
</script>
main.js
<script>
import Thing from './example.js';
Thing.test();
</script>
Reference: https://v2.vuejs.org/v2/guide/single-file-components.html
What you are trying to achieve is fundamentally flawed. You can't call a method of a component unless you have a reference to an instance of that particular component. In your code, which particular component is vm referring to?
All you're doing is exporting a Vue component definition from your module; there's no component being instantiated here.
We'll need to see more of your code or a complete explanation of what exactly you're trying to achieve so we can provide an alternative solution. (Why are you trying to call the component's method outside of its definition?)
export default {
...
methods: {
...
},
mounted () {
EventBus.$on(‘EVENT_NAME’, function (payLoad) {
...
});
}
}
This is the way I solved that problem.
For the purpose of this demonstration, we create a new project using Vue/CLI. After installation finished, we make the vm exposed to global. Open src/main.js and edit like so:
src/main.js
import Vue from 'vue';
import App from './App.vue';
var vm = new Vue({
router,
render: h => h(App)
}).$mount('#app');
// Add this line (tambahkan baris berikut):
window.vm = vm;
Leave the generated App.vue like it is. So the first child of vm (vm.$children[0]) is App.vue.
We see that App.vue have a child. That makes HelloWorld.vue component as a grand children of vm (vm.$children[0].$children[0]). Knowing this, we can call the methods from outside 'export default' like this:
src/components/HelloWorld.vue
<template>
<div class="hello">
<button
id="sebuahButton"
class="btn btn-outline-secondary btn-sm"
type="button"
>Click Me, Jose!</button>
<h1>{{ msg }}</h1>
<!-- and some stuff, vue cli default generated code -->
<div>
</template>
<script>
(function() {
// wait for the DOM ready event in plain JavaScript
document.addEventListener("DOMContentLoaded", event => {
document.getElementById("sebuahButton").onclick = function() {
vm.$children[0].$children[0].someAction();
};
});
})();
export default {
name: "HelloWorld",
props: {
msg: String
}
methods: {
someAction () {
// do something (lakukan sesuatu masbro!)
console.log("It's been called from outer space, Luke!");
}
}
}
</script>