How to access Vue-test-util console error - unit-testing

I am using Jest to test this Vue component:
import { mount } from '#vue/test-utils'
import ExampleComponent from '../Components/Example.vue'
describe("Test", () => {
it('shows no errors', () => {
jest.spyOn(global.console, 'error');
jest.spyOn(global.console, 'warn');
mount(ExampleComponent)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
})
})
I am expecting this test to Fail since I have this component:
Example.vue
<template>
<div>
{{ number }}
</div>
</template>
<script>
export default {}
</script>
as you can see number is not defined, and if opened this component using the browser it will show me:
[Vue warn]: Property or method "number" is not defined on the instance but referenced during render.
but if I test it, the test will pass. How can If the Vue component has warnings or errors?

Related

Vuex not working with different components

I have this structure for a Vue app:
App.vue
-> Router View(with two child components)
App.vue
<template>
<div id="app" #click="mutateStore(null)">
<router-view #storeUpdate="mutateStore"/>
</div>
</template>
<script>
export default {
name: 'app',
methods:{
mutateStore(){
this.$store.commit('increment')
}
},
mounted(){
this.$store.commit('increment')
}
}
</script>
<style>
...styles
</style>
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
type: 1
},
mutations: {
increment (state) {
state.type++
}
}
})
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '#/components/Childcomponent1'
import Display from '#/components/Childcomponent2'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Childcomponent1',
component: Main
},
{
path: '/display',
name: 'Childcomponent2',
component: Display
}
]
})
In Child Component 1, I have a button where if clicked would do:
this.$emit("storeUpdate")
which triggers the event handler mutateStore() in App.vue
In Vue Dev Tools, it also shows the state.type with the incremented value
In Child Component 2, I display directly the state.type value as computed:
<template>
<div class="main">
{{type}}
</div>
</template>
<script>
export default {
name: 'Childcomponent2',
computed: {
type () {
return this.$store.state.type
}
}
}
</script>
<style scoped>
..Styles
</style>
But the value never got updated in Child Component 2, not even when viewed in Vue Dev Tools.
And one curious observation, when the same commit in App.vue is called in mounted(), it increments the state.type all across the two child components, but when via method mutateStore(), it does update in Child Component 1, but the change is not detected in Child Component 2.
Note that before I did the emit/event handler part in the App.vue, I already tried mutating the store directly from within the Child Component but to no effect that's why I tried the emit event handler instead.
Did I do anything incorrectly?
Please help!
Found it. Turns out I had wrongly assumed that Vuex standard has built in support for shared states across multiple browser windows using localStorage. Apparently it only does share state across multiple components in the SAME browser tab/window. To allow for multiple browser support, a plugin must be added: vuex-shared-mutations
npm install vuex-shared-mutations

Vue warn: property or method 'processLogout' is not defined on the instance but defined during render

I'm having an issue with Vue. At this point I've read things like this detailing this error occurring when you try to define a method on the root instance, then reference it in local scope.
My issue is slightly different because it is defined in local scope, so I'm not sure what to make of this error. I've also looked at this, and this.
Here is my App.vue:
<template>
<div id="app">
<router-link to="/Home">home</router-link>
<router-link to="/Login">login</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
console.log('app.vue loading')
</script>
My main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from './store'
import router from './router'
import Home from './components/Home'
import Login from './components/Login'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App, Home, Login },
template: '<App/>'
})
console.log('main js loading');
The component the issue is coming from:
<template>
<div class="login">
<head>
<title>{{title}}</title>
</head>
<form for="login" id="loginMain">
<label for="username">Username:</label>
<input type="text" id="username"></input>
<label for="password">Password:</label>
<input type="password"></input><br/><br/>
<button for="login" #click="processLogin">LOGIN</button>
<button for="logout" #click="processLogout">LOGOUT</button>
</form>
<p> Your login status: {{ loginStatus }} </login></p>
</div>
</template>
<script>
import Vue from 'vue'
import { mapGetters, mapActions, Vuex } from 'vuex'
import store from '#/store'
const Login = {
delimiters: [ '[{','}]'],
data () {
title: 'Foo',
msg: 'Bar'
}
name: 'Login',
props: {
// worry about this later
},
methods: {
...mapActions({
processLogin : 'LOGIN_ACTION',
processLogout : 'LOGOUT_ACTION'
})
},
computed: {
...mapGetters({
title: 'GET_TITLE',
loginStatus: 'GET_LOGIN_STATUS'
}),
}
}
console.log('Login loading');
export default Login
And although I'm unsure if it is related, but my store:
import Vue from 'vue'
import Vuex from 'vuex'
import Home from './components/Home'
import Login from './components/Login'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
title: 'My Custom Title',
loggedIn: false
},
mutations: {
MUT_LOGIN: (state) => {
state.loggedIn = true
},
MUT_LOGOUT: (state) => {
state.loggedIn = false
}
},
actions: {
LOGIN_ACTION ({ commit }){
store.commit('MUT_LOGIN')
},
LOGOUT_ACTION ({ commit, state }) {
store.commit('MUT_LOGOUT')
}
},
getters: {
GET_LOGIN_STATUS: state => {
return state.loggedIn
},
GET_TITLE: state => {
return state.title
}
}
})
console.log('store loading');
export default store
I know that I had some of these errors at one point and got rid of them by revisiting each import statement and making some corrections to how I had things hooked up. The only other difference between now and then is that I am running all of these files through Webpack and serving them with Django in a template. (The reason for all the consoles, making sure all the files got in there via Webpack.)
The image below is a couple of the specific things it is barking about.
Because of the link from the devtools error, I have looked at this and also experimented with adding local data () properties to the component itself.
Changing
data () {
title: 'Foo',
msg: 'Bar',
},
to
data () {
return {
title: 'Foo',
msg: 'Bar',
}
},
will fix the "title" error.
As for the issue with the actions nothing is jumping out at me. Can you try mapGetters and see if you have access to the getters?

Combine vue-gettext with unit tests (Vuejs)

Combining unit-tests and vue-gettext I get this error when running the tests:
ERROR LOG: '[Vue warn]: Error in render function: "TypeError:
undefined is not a constructor (evaluating
'Number.isNaN(parseInt(n))')"
found in
---> < Translate >
< Root >'
I am not sure how to combine these two to work together.
Reduced example looks like this:
Home.vue
<template>
<div>
<h1>
<translate tag="h1">Homepage</translate>
</h1>
</div>
</template>
<script>
export default {
name: 'Home'
}
</script>
Home.vue.spec.js
import Vue from 'vue'
import Home from '#views/Home'
describe('Home.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(Home)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('div h1').textContent)
.to.equal('Homepage')
})
})

Using Jest to mock a component which has other components as properties

I'm trying to mock react-bootstrap <Modal> component with jest. <Modal> contains some "sub-components" as properties, for example <Modal.Header>. I'm trying to find out the correct way to mock this kind of components using Jest.
Here's a simple component using <Modal>:
// mymodal.js
import React from 'react'
import {Modal, Button} from 'react-bootstrap'
const MyModal = ({ visible, hide, title, onOk }) =>
<Modal show={visible} onHide={hide}>
<div className='simple-modal'>
<Modal.Header closeButton>{title}</Modal.Header>
<Modal.Body>
<div>I'm body</div>
</Modal.Body>
<Modal.Footer>
<Button className='invert-primary' onClick={hide}>
Cancel
</Button>
<Button bsStyle='primary' onClick={onOk}>
Ok
</Button>
</Modal.Footer>
</div>
</Modal>
export default MyModal
And here's basic snapshot test for it:
// mymodal.test.js
import renderer from 'react-test-renderer'
import * as React from 'react'
import MyModal from './mymodal'
jest.mock('react-bootstrap', () => {
function Modal(props) {
return <div>{props.children}</div>
}
Modal.Header = 'Modal.Header'
Modal.Body = 'Modal.Body'
Modal.Footer = 'Modal.Footer'
return({
Modal: Modal,
Button: 'Button',
})
})
describe('MyModal component', () => {
test('should render a modal', () => {
const modal = renderer.create(<MyModal
visible={true}
hide={() => ''}
onOk={() => ''}
title='Title' />)
expect(modal.toJSON()).toMatchSnapshot()
})
})
And here's snapshot:
// Jest Snapshot v1
exports[`MyModal component should render a modal 1`] = `
<div>
<div
className="simple-modal"
>
<Modal.Header
closeButton={true}
>
Title
</Modal.Header>
<Modal.Body>
<div>
I'm body
</div>
</Modal.Body>
<Modal.Footer>
<Button
className="invert-primary"
onClick={[Function]}
>
Cancel
</Button>
<Button
bsStyle="primary"
onClick={[Function]}
>
Ok
</Button>
</Modal.Footer>
</div>
</div>
`;
I'm quite happy with the snapshot result, but I'd like to get better output for the <Modal> component itself so that the snapshot would contain also component's name (currenlty <div>) and props (currently no props shown).
How should the mocking be done to achieve this?
I couldn't find way to achieve this with jest mocking. Finally I went with enzyme shallow rendering, which handles the basic mocking out of box. To do spanshot matching, I serialized the enzyme wrappers using enzyme-to-json npm package.

"Failed to resolve directive: transition" when trying to use a transition with vue-router

I've got a vue app in which I'm using the vue-router.
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
let router = new VueRouter()
// Components
import App from './App.vue'
import Mapper from './components/Mapper/mapper.vue'
import ToDos from './components/Todos/ToDoApp.vue'
import Punchlist from './components/Punchlist/punchlist.vue'
// Transitions
Vue.transition('slide',{
enterClass: 'slideInRight',
leaveClass: 'slideOutRight'
})
// Redirects
router.redirect({
'*': 'punchlist'
})
// Mappings
router.map({
'/mapper': {
component: Mapper
},
'/todos': {
component: ToDos
},
'/punchlist': {
component: Punchlist
}
})
router.start(App, '#app')
I have a specific transition registered called slide that I would like to use when navigating between routes. In my App component I added the v-transition and transition-mode directives to the route-view:
<template>
<div class="container">
<h1>Component Gallery</h1>
<p>
<a class='btn btn-primary' v-link="{ path: '/punchlist' }">Punchlist</a>
<a class='btn btn-primary' v-link="{ path: '/todos' }">Todos</a>
<a class='btn btn-primary' v-link="{ path: '/mapper' }">Mapper</a>
</p>
<router-view v-transition="slide" transition-mode="out-in" :google-maps-api-key="googleMapsApiKey"></router-view>
</div>
</template>
When I try to run it, I get the console error:
[Vue warn]: Failed to resolve directive: transition (found in component: )
I've been reading through the docs and looking at examples but I can't figure out why it's erroring out when trying to resolve the binding.
Any ideas?
Transition is an attribute, not a directive. No v-:
<router-view transition="slide">