TypeError: Cannot read property 'contextTypes' of undefined - unit-testing

I'm trying to test a React-app with Jest. I use Enzyme's shallow to render my App.js component in App-test-js but I'm getting this error: TypeError: Cannot read property 'contextTypes' of undefined
This is my App.js:
/* global google */
import React, { Component } from 'react';
import Geosuggest from 'react-geosuggest';
import { getAirQuality } from './Client'
import DataTable from './DataTable'
import Errors from 'react-errors'
class App extends Component {
.
.
.
render() {
return (
<div className="App">
<form onSubmit={this.searchAirQuality.bind(this)}>
<Geosuggest
placeholder="Type a location and press SEARCH button!"
onSuggestSelect={this.onSuggestSelect.bind(this)}
initialValue={this.state.place}
location={new google.maps.LatLng(53.558572, 9.9278215)}
radius="20"/>
<button className="my-button" type="submit" disabled={!this.state.place}>Button</button>
</form>
<DataTable items={this.state.items} />
<Errors
errors={this.state.errors}
onErrorClose={this.handleErrorClose}
/>
</div>
)
}
}
export default App;
and this is my App-test.js:
import React from 'react'
import { shallow } from 'enzyme'
import App from '../components/App.js'
describe( '<App />', () => {
it('Button disable when input is empty', () => {
const App = shallow(<App />);
expect(App.find('.my-button').hasClass('disabled')).to.equal(true);
});
});
And this the error when I run npm test:
Terminal screenshot
This is my first time with testing in jest, please could someone help me with any idea about this error?

This would be the same error TypeError: Cannot read property 'contextTypes' of undefined when you are importing something that does not exist.
Here is an example:
AccountFormComponent.jsx (incorrect class name):
export class FoeFormComponent extends React.Component { .... }
AccountFormComponent.test.jsx:
import { shallow } from 'enzyme'
import { expect, assert } from 'chai'
import { AccountFormComponent } from '../../src/components/AccountFormComponent';
describe('', function () {
it('', function () {
const enzymeWrapper = shallow(<AccountFormComponent {...props} />);
});
});
Just add the following to your test file to be sure the component exists:
it('should exists', function () {
assert.isDefined(AccountFormComponent);
});
which prints AssertionError: expected undefined to not equal undefined instead

The problem here is that you are redefining the the app component with the result of the shallow call
// Redefining
// ↓
const App = shallow(<App />);
The solution would be to use a different name:
// Use a different name
// ↓
const app = shallow(<App />);

In my case the error occurred when imported a module that has only one default export, but I was using single import.
So instead of:
import { Foo } from './Foo'
use:
import Foo from './Foo'
where Foo has default export:
class Foo extends Component {
render() {
return (<div>foo</div>)
}
}
export default Foo;

As #Ser mentioned could be a import issue.
If you are using eslint rules this could give you a hint if any of the imports might fail
"import/no-unresolved": 1,
I got that error on trying to import a component from a jsx file
import {Header} from './Header';
this fixed it
import {Header} from './Header.jsx';
Also because I used webpack I realised that I should have added '.jsx' to resolve.extensions option. This way I can ignore the extensions when importing.

In my case I was importing a default component with named component syntax.
For example, something like:
import {TestComponent} from "../../components/TestComponent";
instead of
import TestComponent from "../../components/TestComponent";
Updating the import to use the right syntax fixed the issue. Silly one though.

Related

React: Enzyme shallow: "Expected: 1; Received: undefined

I am using React-typescript for my app. I am really new in testing world. I have created two components. Then I am imported the components to the App component. I am using Enzyme's shallow for testing the component. I am watching one tutorial from udemy. I followed the test code exactly as it is in video. But my test failed.
This is my app component
import React from 'react';
import CommentBox from './components/commentbox';
import CommentList from './components/commentlist'
function App() {
return (
<div className="App">
<CommentBox />
<CommentList />
</div>
);
}
export default App;
This is my test
import { cleanup } from '#testing-library/react';
import App from '../App';
import CommentBox from '../components/commentbox';
describe('whole app component', () => {
afterEach(cleanup)
it('renders Comment Box component', () => {
const wrapped = shallow(<App />);
expect(wrapped.find(CommentBox).lenght).toEqual(1) //I Received Undefined
});
});

Mocha Chai vue testing a vue component : this.$notify is not a function

I'm using this component : https://github.com/euvl/vue-notification
Since then, all of my Mocha chai test units are failing .
this.$notify is not a function
This is my login spec :
// Importing The testing library
import { expect } from "chai";
import { mount } from '#vue/test-utils'
// Importing The component I need to test
import Login from "#/components/Login.vue";
// Mounting the component as in real life
const wrapper = mount(Login);
describe("Login test", () => {
it("getAuth() to be a function", () => {
expect(wrapper.vm.getAuth).to.be.a("function");
});
});
I've tried out mount, shallowMount, render with no luck .
Any workaround ?
I'm calling vue-notification in main.js like this :
import Notifications from "vue-notification";
Vue.use(Notifications);
Thank you !
EDIT :
Ive tried to add
const $notify = require('vue-notification')
To my Login.vue component with no luck
EDIT 2 : Tried to call the function like this with no luck :
this.$root.$notify({
group: 'foo',
title: 'Hello ',
text: 'Cool'
});
[Vue warn]: Error in mounted hook: "TypeError: this.$root.$notify is not a function"
*EDIT : ***** Resolved by me ****** *
I was badly importing vue . Please see my working login.spec.js testing file there :
// THE ASSERTION LIBRARY
import { expect } from "chai";
// THE TESTING LIBRARY
import { mount } from "#vue/test-utils";
// THE COMPONENT THAT I WANT TO TEST
import Login from "#/components/Login.vue";
// THE EXTERNAL COMPONENTS LINKED TO MY LOGIN COMPONENT THAT I NEED TO JOIN
import Vue from 'vue';
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
import {
required,
minLength,
between
} from "vuelidate/lib/validators";
import Notifications from "vue-notification";
import velocity from 'velocity-animate'
Vue.use(Notifications, { velocity });
// THE WRAPPER CONTAIN MY LOGIN MOUNTED COMPONENT, JUST LIKE IN THE REAL LIFE
const wrapper = mount(Login)
describe("Login test", () => {
it("getAuth() to be a function", () => {
expect(wrapper.vm.getAuth).to.be.a("function");
});
});

Mock this.$parent.$on with jest

We are using vuejs, typescript, vuex and jest. We are currently using test-utils to mock the store.
But I cannot find out how to mock a call to this.$parent.$on
Here is one of our components (very simplified):
AnalysisList.ts:
import Component from 'vue-class-component'
import {Getter} from 'vuex-class'
import {UserVO} from '#/valueObjects/UserVO'
import {Vue} from 'vue-property-decorator'
#Component
export default class AnalysisList extends Vue {
#Getter('getCurrentUser') private currentUser: UserVO
private searchString = ''
public mounted() {
this.$parent.$on('resetAnalyses', this.reset)
}
public reset() {
this.searchString = ''
}
}
AnalysisList.vue:
<template lang="pug">
text test
</template>
<script lang="ts" src="./AnalysisList.ts">
</script>
AnalysisList.spec.ts:
import {shallowMount} from '#vue/test-utils'
import AnalysisList from '#/components/analysis/AnalysisList'
import Vuex from 'vuex'
import {Vue} from 'vue-property-decorator'
import VueRouter from 'vue-router'
Vue.use(Vuex)
Vue.use(VueRouter)
describe('AnalysisList.vue', () => {
const store = new Vuex.Store( {
modules: {
user: {
state: {currentUser: 'test'},
getters: {
getCurrentUser: (state: any) => state.currentUser,
},
},
},
})
it('minimum test', (done) => {
const wrapper = shallowMount(AnalysisList, {store})
done()
})
})
When I run the test, I have the following error message, because $parent is not mocked:
TypeError: Cannot read property '$on' of undefined
at VueComponent.mounted (src/components/analysis/AnalysisList/AnalysisList.vue:73:20)
at callHook (node_modules/vue/dist/vue.runtime.common.js:2919:21)
at mountComponent (node_modules/vue/dist/vue.runtime.common.js:2800:5)
at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.js:7997:10)
at mount (node_modules/#vue/test-utils/dist/vue-test-utils.js:5381:8)
at shallowMount (node_modules/#vue/test-utils/dist/vue-test-utils.js:5414:10)
at Object.done (tests/unit/AnalysisList.spec.ts:20:53)
If I try to add a new property to shallowMount parameter:
const wrapper = shallowMount(AnalysisList, {store, parent: {$on: ()=>{}}})
I obtain a type error:
TS2345: Argument of type 'VueConstructor<Vue>' is not assignable to parameter of type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.   Property 'functional' is missing in type 'VueConstructor<Vue>'.
Do you have any clue to help me mock this.$parent.$on ? Thanks.
I got the same issue with vue-test-utils and Jest (under the Vue, Vuex and Typescript environment)
For me, createLocalVue() of vue-test-utils library fixed the issue. This function creates a local copy of Vue to use when mounting the component. Installing plugins on this copy of Vue prevents polluting the original Vue copy. (https://vue-test-utils.vuejs.org/api/options.html#localvue)
Adding this to my test file fixed the issue:
const EventBus = new Vue();
const GlobalPlugins = {
install(v) {
// Event bus
v.prototype.$bus = EventBus;
},
};
// create a local instance of the global bus
const localVue = createLocalVue();
localVue.use(GlobalPlugins);
Hope this helps others, thanks :)

Vue component register warning in unit tests with element-ui

By element-ui documentation, it can be imported "entirely, or just import what you need". I have imported it entirely in application entry point main.js.
main.js
import Vue from 'vue'
import ElementUI from 'element-
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
const Component = () => import(/* webpackChunkName: "component" */ './views/Component.vue')
// eslint-disable-next-line no-new
new Vue({
el: '#app',
components: {Component}
})
Like that it is possible to use all element-ui components in my custom components. For example I've used button component in Component.vue. That looks fine and button is rendered in App.vue.
Component.vue
<template>
<div>
<el-button>{{ buttonText }}</el-button>
</div>
</template>
<script>
export default {
name: 'component',
data () {
return {
buttonText: 'Button Text'
}
},
components: {}
}
</script>
Now I have created test against this component using vue-jest and #vue/test-utils where I am testing is text in the button is rendered correctly. The test passed but I have Vue warning that button component is not registered:
[Vue warn]: Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.
This is probably happening because I have imported all element-ui components directly in main.js file (as they propose in documentation) and not in Component.vue. Does anybody know how can I avoid this warning or how can I import component in the test?
Component.spec.js
import { shallow } from '#vue/test-utils'
import Component from '../Component.vue'
describe('Component.vue', () => {
test('sanity test', () => {
expect(true).toBe(true)
})
test('renders button title', () => {
const wrapper = shallow(Component)
wrapper.setData({buttonText: 'This is text'})
expect(wrapper.text()).toEqual('This is text')
})
})
In your tests, create a local Vue, call .use in it and pass it to shallow:
import { shallow , createLocalVue} from '#vue/test-utils'; // changed
import Vue from 'vue'; // added
import ElementUI from 'element-ui'; // added
import Component from '../Component.vue'
const localVue = createLocalVue(); // added
localVue.use(ElementUI); // added
describe('Component.vue', () => {
test('sanity test', () => {
expect(true).toBe(true)
})
test('renders button title', () => {
const wrapper = shallow(Component, {localVue}) // changed
Try to import the required module using Vue.use(Module) in your .spec file.
// + Vue.use(ElementUI)
describe('Component.vue', () => {
...
})
You might get an error stating that you cannot import entire module because preventFullImport setting is true. To prevent it, modify your .babelrc or equivalent file and change your settings accordingly. All I did was preventFullImport: false (personally for test cases only).
TESTED
Hello, after many hours of looking I find an another answer, here you dont need to manually set each custom component, as in the first answer.
add a components.js file where you register all of your global vue components:
componentsbind.js
import Vue from 'vue'
//this are the components I want to import
import MyHeader from '#/components/MyHeader'
import MyNav from '#/components/MyNav'
Vue.component('MyHeader', MyHeader)
Vue.component('MyNav', MyNav)
In jest.config.js add
modules.exports = {
moduleNameMapper:{
"~(.*)$": "<rootDir>/resources/js/$1",
},
setupFilesAfterEnv: ['<rootDir>resources/src/tests/setup.js']
}
add a setup.js file in test folder
import '../componentsbind.js'
structure
└── src
├── assets
├── components
└── tests
└─ unit
└─ example.spec.js
└─ setup.js
└── etc
└──index.js //here in my case I register all of my global vue components
└──componentsbind.js //that is why I put my components.js file in this place
and last run! this works for me!
npm run test:unit
for more info:
https://github.com/vuejs/vue-test-utils/issues/1116

How to test required props in React

I'm trying to write simple test with React and Jest.
Component:
import React from "react";
class Task extends React.Component {
render() {
let onDelete = this.props.onDelete;
return (
<li>
<div className="collapsible-header"><i className="material-icons" onClick={() => onDelete(this.props.taskId)}>delete</i>{this.props.title}</div>
<div className="collapsible-body"><p>{this.props.description}</p></div>
</li>
);
}
};
Task.propTypes = {
title: React.PropTypes.string.isRequired,
taskId: React.PropTypes.number.isRequired,
onDelete: React.PropTypes.func.isRequired,
description: React.PropTypes.string
};
Task.defaultProps = {
description: ''
};
export default Task;
Test
import React from 'react';
import Task from '../src/components/Task';
import renderer from 'react-test-renderer';
test('Task should require properties', () => {
const component = renderer.create( //this will give me React warnings which I would like to assert
<Task></Task>
);
});
Now I would like to assert that title, taskId and onDelete is required for Task component. That I will get React warning about not specifying them (or passing different types).
You can use a spy to find out if any kind of exception was thrown from react. Many people use a library called Sinon.js. From the documentation "A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls".
There is a great solution described in more detail here:
How to test React PropTypes through Jest?