Jest - Cannot read properties of undefined (reading 'map') when using i18next - unit-testing

I'm quite new to testing and I'm using i18next in my app but when I try to test components where I use .map() I receive an error of cannot read properties of undefined. How should I write the test so I don't get this error?
Component.js - the myArr is from a json file.
MyComponent = () => {
return(
<div data-testid="comp">
{t("myArr", { returnObjects: true}).map(({ name, href, target }) => (
<div key={name} >
<Link href={href}>
<a
target={target}
onClick={() => handleClick()}>
{name}
</a>
</Link>
</div>
))}
)
}
Component.test.js
import React from "react";
import {render } from "#testing-library/react";
import "#testing-library/jest-dom/extend-expect";
import MyComp from ".";
jest.mock("next/router", () => ({
useRouter() {
return {
local: "",
asPath: "",
};
},
}));
describe("MyComp", () => {
it("should render myComponent", () => {
const { getByTestId } = render(
<myComponent menuOpen={false} setBurgerOpen={jest.fn()} t={jest.fn()}/>
);
const myComp = getByTestId("comp");
expect(myComp).toBeVisible();
});
});

Related

Unit Testing VueJS - Checking for an element with a certain class name

I'm struggling to get a simple unit test working in a VueJS app.
I'm basically trying to test the template and check if it contains a div element with a class name of "version" but the test keeps failing with an undefined is not a constructor (evaluating 'expect(component.$el.querySelector('div')).to.have.className('version')'). error.
It's a simple component with this as the template:
<template>
<div>
<div class="version">
<label>Version {{ version }}</label>
</div>
<div class="activityBanner">
<label>{{ user }}</label>
<router-link id="logout" :to="{name: 'logout' }">
<label>Logout</label>
</router-link>
</div>
</div>
</template>
Here is the unit test I'm working with:
import Vue from 'vue';
import router from '#/router';
import VueRouter from 'vue-router';
import Navigation from '#/components/Navigation';
describe('Navigation.vue', () => {
// Nice little helper to return our component within a div
const getComponent = data => {
const Constructor = Vue.extend(Navigation);
return new Constructor({
router
}).$mount();
};
describe('Component', () => {
it('should have a property "name"', () => expect(Navigation.name).to.be.a('string'));
it('should have a property "name" set to "Navigation"', () => expect(Navigation.name).to.equal('Navigation'));
it('should have a data hook', () => expect(Navigation.data).to.be.a('function'));
it('should have a default "currentView" set to "profileTable"', () => {
const defaultData = Navigation.data();
expect(defaultData.currentView).to.equal('profileTable');
});
it('should have a default "version" set to "0.5"', () => {
const defaultData = Navigation.data();
expect(defaultData.version).to.equal(0.5);
});
it('should have a default "user" set to "Bob Barker"', () => {
const defaultData = Navigation.data();
expect(defaultData.user).to.equal('Bob Barker');
});
});
describe('Template', () => {
Vue.use(VueRouter);
it('should render correctly', () => {
const component = getComponent();
expect(component.$el);
});
it('should have a "div" element', () => {
const component = getComponent();
expect(component.$el.querySelectorAll('div').length);
});
it('should have a element with a "className" set to "version"', () => {
const component = getComponent();
expect(component.$el.querySelector('div')).to.have.className('version');
});
});
});
What are I doing wrong?
Checkout the Vue test utils:
Vue Test Utils
It makes very light work of testing templates.
Here is there example:
import { mount } from '#vue/test-utils'
import Counter from './counter'
describe('Counter', () => {
// Now mount the component and you have the wrapper
const wrapper = mount(Counter)
it('renders the correct markup', () => {
expect(wrapper.html()).toContain('<span class="count">0</span>')
})
// it's also easy to check for the existence of elements
it('has a button', () => {
expect(wrapper.contains('button')).toBe(true)
})
})

Vue.js Vuex unit test failing , [vuex] unknown getter:

I am testing my App.vue , and I am stuck with a Vuex error on getters...
I guess it's related to a badly define getters property , but I don't see how to solve it ..
feeedback welcome
Console.log
ERROR LOG: '[vuex] unknown getter: getLists'
App.vue
✗ calls store action addShoppingList when a click event is fired from the plus-sign icon
AssertionError: expected false to equal true
at Context.<anonymous> (webpack:///test/unit/specs/App.spec.js:33:50 <- index.js:24490:51)
App.spec.js
import App from '#/App'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('App.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
addShoppingList: sinon.stub(),
populateShoppingLists: sinon.stub()
}
getters = {
shoppinglists: () => 'getLists'
}
store = new Vuex.Store({
state: {},
actions,
getters
})
})
it('calls store action addShoppingList when a click event is fired from the plus-sign icon', (done) => {
const wrapper = mount(App, { store })
wrapper.find('a')[0].trigger('click')
wrapper.vm.$nextTick(() => {
expect(actions.createShoppingList.calledOnce).to.equal(true)
done()
})
})
App.vue
<template>
<div id="app" class="container">
<ul class="nav nav-tabs" role="tablist">
<li :class="index===shoppinglists.length-1 ? 'active' : ''" v-for="(list, index) in shoppinglists" :key="list.id" role="presentation">
<shopping-list-title-component :id="list.id" :title="list.title"></shopping-list-title-component>
</li>
<li>
<a href="#" #click="addShoppingList">
<i class="glyphicon glyphicon-plus-sign"></i>
</a>
</li>
</ul>
<div class="tab-content">
<div :class="index===shoppinglists.length-1 ? 'active' : ''" v-for="(list, index) in shoppinglists" :key="list.id" class="tab-pane" role="tabpanel" :id="list.id">
<shopping-list-component :id="list.id" :title="list.title" :items="list.items"></shopping-list-component>
</div>
</div>
</div>
</template>
<script>
import ShoppingListComponent from './components/ShoppingListComponent'
import ShoppingListTitleComponent from './components/ShoppingListTitleComponent'
import store from './vuex/store'
import { mapGetters, mapActions } from 'vuex'
import _ from 'underscore'
export default {
components: {
ShoppingListComponent,
ShoppingListTitleComponent
},
computed: {
...mapGetters({ shoppinglists: 'getLists' })
},
methods: _.extend({}, mapActions(['populateShoppingLists', 'createShoppingList']), {
addShoppingList () {
let list = { title: 'New Shopping List', items: [] }
this.createShoppingList(list)
}
}),
store,
mounted: function () {
this.$nextTick(function () {
this.populateShoppingLists()
})
}
}
</script>
UPDATE
here are my getters.js anf store.js files
store.js is imported in App.vue
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters' // import getters !
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
const state = {
shoppinglists: []
}
export default new Vuex.Store({
state,
mutations,
getters,
actions
})
getters.js
import _ from 'underscore'
export default {
getLists: state => state.shoppinglists,
getListById: (state, id) => {
return _.findWhere(state.shoppinglists, { id: id })
}
}
You don't define a getLists getter in your store. You're defining a shoppinglists getter that returns 'getLists'.
You need to either change your mapGetters line to be:
...mapGetters(['shoppinglists'])
Or change the name of the getter to getLists:
getters = {
getLists: () => 'getLists'
}
(Although I'm not sure if you are really meaning to return a string value in that getter or not)
#thanksd put me on tracks... see my comment
so I need to define the getters in my Vur.spec.js as following
getters = {
getLists: () => {
// console.log('WE ARE S TEST')
state => state.shoppinglists
}
}

React + Chai + Enzyme

I'm trying to run some unit test with Chai&Enzyme to my React app.
Enzyme seems to have an issue with props I passed to components. They got undefined in tests (not in app).
main file:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import TaskList from './components/task_list';
import AddingInput from './components/input';
const titleHeader = <h1 className="app__title">Simple to-do list:</h1>;
class App extends Component {
constructor(props) {
super(props);
this.state = {
tasks: [
{id: 0, name: 'Complete task'},
{id: 1, name: 'Add task'},
{id: 2, name: 'Delete task'}
],
id: 3,
};
}
(...)
render () {
return (
<div className="app">
{titleHeader}
<TaskList
tasks={this.state.tasks}
deleteMe={this.deleteMe.bind(this)} />
<AddingInput addTodo={this.addTodo.bind(this)} />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Task_list - first component I want to test:
import React from 'react';
import TaskItem from './task_item';
const TaskList = (props) => {
const taskItems = props.tasks.map((task) => {
return (
<TaskItem
key={task.id}
task={task}
deleteMe={props.deleteMe}
/>
);
});
return (
<ol className="ordered-list">
{taskItems}
</ol>
);
};
export default TaskList;
And the second:
import React from 'react';
const TaskItem = React.createClass({
onCompleted: function(e) {
e.target.className += " ordered-list__item--completed"
},
render: function() {
return (
<li><span className="ordered-list__item" onClick={this.onCompleted}>{this.props.task.name}</span>
<span onClick={() => this.props.deleteMe(this.props.task)}
className='btn btn--delete'>Delete</span>
</li>
);
}
});
export default TaskItem;
An here are my tests. Two of them are passed (both component exist) but in other two got undefined on props.tasks:
import React from 'react';
import { expect, assert } from 'chai';
import { shallow, mount } from 'enzyme';
import TaskList from './src/components/task_list';
import TaskItem from './src/components/task_item';
describe('TaskList', () => {
it('should render TaskItem', () => {
const wrapper = shallow(<TaskList />);
expect(wrapper.containsAllMatchingElements([
<TaskItem />,
])).to.equal(true);
});
it('should exists', () => {
assert.isDefined(TaskList)
})
});
describe('TaskItem', () => {
it('should render one item of unordered list', () => {
const item = mount(<TaskItem />);
expect(item).to.contain('li');
});
it('should exists', () => {
assert.isDefined(TaskList)
})
});
Problems:
1) TaskList should render TaskItem:
TypeError: Cannot read property 'map' of undefined
2) TaskItem should render one item of unordered list:
TypeError: Cannot read property 'name' of undefined
Your App component passes a tasks prop:
<TaskList
tasks={this.state.tasks}
deleteMe={this.deleteMe.bind(this)} />
And TaskList expects it to be always present. Similarly, TaskItem expects to always have certain props available (from TaskList).
If you add the necessary props in your unit tests, it should work. For TaskList, this might look something like the following (untested):
const tasks = [
{id: 0, name: 'Complete task'},
{id: 1, name: 'Add task'},
{id: 2, name: 'Delete task'}
];
const wrapper = shallow(<TaskList tasks={tasks}/>);

How to stop redux-form or React from changing htmlFor and id when creating Jest snapshots?

I've got a wizard form made with redux-forms v6 and it looks something like:
--
index.js - Holds page number in local state, is connected to application level state
PageOne - wrapped with reduxForm decorator (form: 'wizForm')
PageTwo - wrapped with reduxForm decorator (form: 'wizForm')
--
PageOne and PageTwo both contain additional components that render sections of the form (initial fields, vehicle information, driver information...), and each of those sections render their own components for each question in that section.
Since there's a lot of nested components and I want to test that PageOne and PageTwo call the props passed from index.js, I've resorted to using Enzyme's mount() function with a fake store. I want to MatchSnapshot() with Jest to compare whether index.js is rendering PageOne or PageTwo, after certain buttons are clicked to go back and forth from pages.
The problem is when I do create snapshots, other than creating a 16,000 line snapshot, the snapshot will NEVER match the previous one even if I don't change anything. I'm not sure if it's redux-form that's doing it or React, but the htmlFor and the id keep changing between snapshots, test after test after test.
We use css-modules too, but I don't think that's causing the problem, and we did configure Jest to work with css-modules too, modifying "moduleNameWrapper" to mock .css files. Does anyone know how to fix this or where I should look?
tests:
describe('<VehicleAddition />', () => {
let props;
beforeEach(() => {
props = {
...,
};
});
it('Renders initially', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
});
it('Renders <PageTwo> when <PageOne> form is submitted', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
component.find('ReduxForm') // reduxForm HOC wraps the <form> in a <ReduxForm> component
.first()
.simulate('submit');
expect(toJson(component)).toMatchSnapshot();
expect(component.state().page).toEqual(2);
});
it('PageTwoStuffs', () => {
// Render the form, click 'next', assert it's page two
// click 'previous'
jest.enableAutomock();
const store = createStore(
combineReducers({
route: jest.fn(() => Immutable.fromJS({})),
language: jest.fn(() => Immutable.fromJS({})),
global: jest.fn(() => Immutable.fromJS({})),
form: formReducer,
}),
Immutable.fromJS({}),
);
const component = mount(
<Provider store={store}>
<VehicleAddition {...props} />
</Provider>
);
// CAN'T check the state of <VehicleAddition /> because it can only be done on root component, says the error message.
expect(toJson(component)).toMatchSnapshot();
index.js:
export class VehicleAddition extends React.Component { // eslint-disable-line
constructor(props) {
super(props);
this.state = {
page: 1,
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage = () => {
this.setState({ page: this.state.page - 1 });
}
render() {
return (
<div>
{page === 1 &&
<PageOne
{...this.props}
/>
}
{page === 2 &&
<PageTwo
{...this.props}
/>
}
</div>
);
}
}
PageOne.js
class PageOne extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<InitialFields
autoPolicies={autoPolicies}
changeField={this.changeField}
getFormValues={getFormValues}
policies={policies}
primary={primary}
/>
<VehicleBeingAddedFields
changeField={this.changeField}
getFormValues={getFormValues}
fetchVehMakes={fetchVehMakes}
fetchVehModels={fetchVehModels}
policies={policies}
vehMakes={vehMakes}
vehModels={vehModels}
/>
...
<div className="btn-group btn-group-float-right">
<button
type="submit"
onClick={this.handleClick}
disabled={pristine || submitting}
className="btn-primary"
>
Next
</button>
</div>
</form>
);
}
}
PageTwo.js:
class PageTwo extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
...
<div className="btn-group btn-group-float-right">
<button type="button" className="btn" onClick={previousPage}>Previous</button>{' '}
<button type="submit" disabled={pristine || submitting} className="btn-primary">Submit</button>
</div>
</form>
);
}
}
Example of the parts of the snapshot that constantly changes:
I solved it by passing a hardcoded id value from the test cases
import React from 'react';
import renderer from 'react-test-renderer';
import { reduxForm } from 'redux-form';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { mount } from 'enzyme'
import TodoItem from './TodoItem';
import injectTapEventPlugin from 'react-tap-event-plugin';
function setup() {
const spy = jest.fn();
const store = createStore(() => ({}));
const Decorated = reduxForm({ form: 'testForm' })(TodoItem);
const props = {
remove: jest.fn(),
TodoItemReduxFormInitialName: "fullName",
snapshotTestId:"4"
}
const mockedComponent = <Provider store={store}>
<Decorated {...props} />
</Provider>;
const enzymeWrapper = mount(mockedComponent)
injectTapEventPlugin();
return {
props,
mockedComponent,
enzymeWrapper
}
}
describe('TodoItem Component', () => {
it('should render the snapshot', () => {
const {mockedComponent} = setup()
const tree = renderer.create(
mockedComponent
).toJSON();
expect(tree).toMatchSnapshot();
});
//not required as snapshot testing covers it
it('should render Number', () => {
const {enzymeWrapper} = setup()
const fieldProps = enzymeWrapper.find('Field').at(0).props();
expect(fieldProps.hintText).toEqual('Item Number');
expect(fieldProps.name).toEqual('fullName.itemNumber');
});
//not required as snapshot testing covers it
it('should render remove button', () => {
const {enzymeWrapper} = setup()
const button = enzymeWrapper.find('RaisedButton').at(0).props();
expect(button.label).toEqual("remove")
});
});

Jest: how to make sure the React children components are called

I'm trying to get a 100% test coverage for a component of mine that calls multiple components. I'm unable to get Jest to cover all the lines in the component.
// Component-test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Component from './Component';
describe('CompanyOverview component', () => {
it('should render', () => {
const tree = renderer.create(
<CompanyOverview params={{id: '4444'}}/>
);
expect(tree).toMatchSnapshot();
});
});
Here is the example code for the component:
// Component.js
import React from 'react';
import GetAPI from '../GetAPI';
import Employee from '../Employee';
const Component = ({ params }) => (
<GetAPI path={`/company/${params.id}`}>
{(company) => (
<div>
<h3>{company.name}</h3>
{company.employees.map((employee, i) => (
<Employee // <-- This isn't being picked up in the coverage.
firstname={employee.firstname}
lastname={employee.lastname}
/>
))}
</Fetch>
);
export default Component;
GetAPI.js
export default class Fetch extends React.Component {
componentDidMount() {
const { id } = this.props;
fetch('http://example.com/' + id)
.then((response) => response.json())
.then((result) => this.setState({ result, loading: false }))
.catch((error) => this.setState({ error, loading: false }));
}
render() {
if (this.state.loading) {
return (
<p>Loading...</p>
);
}
if (this.state.error) {
return (
<div>
<h3>Error:</h3>
<code>{this.state.error.message}</code>
</div>
);
}
return this.props.children(this.state.result);
}
}
Running
jest --no-cache --verbose --coverage shows that Employee isn't being called on the coverage.
Do I need to mock something? Any help would be appreciated.