How can I use jest and enzyme to test content generated dynamically? - reactjs

So, this component "Info" is a container which process some data in order to generate a "Details" children component with some props.
Info.JS
import React from 'react'
import Details from './Details/Details'
import {Card} from 'reactstrap'
import classes from './Info.module.css'
import {connect} from 'react-redux'
const Info = (props)=>{
let itemDetails = 'Item Details'
let items = undefined
if(props.activeTab === '1'){
items = props.shortTerm
} else if (props.activeTab ==='2'){
items = props.mediumTerm
} else if (props.activeTab ==='3'){
items = props.longTerm
}
if(items.length!==0){
itemDetails=(
items.map((i,index)=>{
if(i.id===props.itemIndex){
return <Details
title={i.itemName}
desc={i.itemDesc}
date={"Created at "+i.created}
edited={i.lastEdited}
key={index}/>
}
console.log(itemDetails)
return null
})
)
} else{
return itemDetails = (
<Details
title="Title"
desc="Description"
key={null}
date={null}/>
)
}
return(
<Card className={classes.info}>
{itemDetails}
</Card>
)
}
const mapStateToProps = (state) =>{
return{
shortTerm:state.reducer.items.shortTerm,
mediumTerm:state.reducer.items.mediumTerm,
longTerm:state.reducer.items.longTerm,
activeTab:state.reducer.activeTab,
itemIndex: state.reducer.itemIndex
}
}
export default connect(mapStateToProps)(Info)
Question
How can I make a test in which I can check if any component is being rendered? Or, how can I write a test in which I can check if any "itemDetails" is being rendered?
I tried this, so far, to test if I could find any being rendered but it always return me a error saying "Cannot read property 'find' of undefined".
The test code is this one:
Info.test.js
import React from 'react'
import {Provider} from 'react-redux'
import {configure,shallow,mount,render} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16'
import Info from './Info'
import Details from './Details/Details'
configure({adapter:new Adapter()})
describe('<Info />',()=>{
let wrapper
beforeEach(()=>{
wrapper= shallow(<Info/>);
})
it('Should return one Details',()=>{
expect(wrapper.find(Details)).toHaveLength(1)
})
});

So I found an answer,based on this one: Testing React Redux - cannot read properties of undefined, or wrapper undefined
It worked perfectly for me! I happens that, to generate the component, I had to pass some props to the component. This is the setup that I used in order to make the test to work:
import React from 'react'
import {Provider} from 'react-redux'
import {configure,shallow,mount,render} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16'
import {Info} from './Info'
import Details from './Details/Details'
configure({adapter:new Adapter()})
const setup=()=>{
let props= {
shortTerm:[],
mediumTerm:[],
longTerm:[],
activeTab:'1',
itemIndex:0
}
let wrapper = shallow(<Info {...props}/>);
return {props, wrapper};
};
describe('<Info />',()=>{
const {wrapper}=setup()
it('Should return one Details Component',()=>{
expect(wrapper.find(Details)).toHaveLength(1)
})
});

Related

Client Side Unit Testing Meteor/React w/Enzyme

so i've been stuck for several days on an issue while implementing Unit Testing and Integration testing in a large production application that was built in Meteor/React tech stack. I am using the meteortesting:mocha package as recommended by the meteor documentation and enzyme.
The issue i am having is that i am not really grasping how i can mock the withTracker functionality. I am trying to use our dev database as the source for the test users and mock data. All of the props are generated in the tracker and then sent to the component it wraps. (Code sample below). Another issue i am having is that we are using meteor:universe for i18n internationalization. When mounting the component it shows plain text instead of the translated content. Wondering if there's a work around. Thanks in advance!
Component I am testing:
import React, { useState } from "react";
import ABCComponent from "./ABCComponent";
import XYZ from "./XYZComponent";
import * as ROUTE_CONSTANTS from "../../global/RoutesConstants";
import { withRouter } from "react-router-dom";
import { withTracker } from "meteor/react-meteor-data";
import UserAssessments from "../../collections/UserAssessments";
import moment from "moment-timezone";
import { i18n } from "meteor/universe:i18n";
const SortDashboard = (props) => {
const [isSkillsSort, setIsSkillSort] = useState(true);
return (
<div>
{/* Contains some logic to set 'isSetSkillSort' state true or false (business logic hidden for security purposes*/}
{isSkillsSort ? (
<ABCComponent user={props.user} skillsSorts={props.skillsSorts} employeeList={props.directReportEmp} />
) : (
<XYZComponent
user={props.user}
importanceSorts={props.importanceSorts}
employeeList={props.directReportEmp}
/>
)}
</div>
);
};
const SortDashboardTracker = withTracker((props) => {
if (!props.user) return {};
const abcSubscription = Meteor.subscribe("abcSubscription");
if (abcSubscription.ready()) {
const rawData = UserAssessments.find(
{ "assessor._id": Meteor.user().profile._id },
{ sort: { updatedDate: -1 } }
).fetch();
rawData.forEach((assessment) => {
//Do Something (business logic hidden for security purposes)
});
}
const xyzSubscription = Meteor.subscribe("xyzSubscription");
let directReportEmp = [];
if (xyzSubscription.ready()) {
directReportEmp = Meteor.users.find({ "profile.managerId": Meteor.user().username }).fetch();
}
return { importanceSorts, skillsSorts, directReportEmp };
})(SortDashboard);
export default withRouter(SortDashboardTracker);
My Test:
import {Meteor} from 'meteor/meteor';
import React from 'react';
import chai from 'chai';
import sinon, { mock } from 'sinon'
import {mount, shallow, configure, render} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {mockManager,mockEmp1,mockEmp2,mockEmp3,mockUser} from '../../mockUsers'
import SortDashboard from '../../../../imports/components/cllWizard/SortDashboard';
import { withRouter, BrowserRouter as Router } from "react-router-dom";
configure({adapter: new Adapter()});
if (Meteor.isClient) {
describe('WizardComponent', ()=> {
//let returnedText
//importing the mock user we created for testing purposes
const currentUser = mockUser
let props = {user: currentUser}
beforeEach(() => {
// now Meteor.user() will return the user we just imported
sinon.stub(Meteor, 'user');
Meteor.user.returns(currentUser);
// needed in methods
sinon.stub(Meteor, 'userId');
Meteor.userId.returns(currentUser._id);
});
//afterEach specifies that we want to restore the user after running the test
afterEach(() => {
Meteor.user.restore();
Meteor.userId.restore();
});
it('CLIENT: should render the Sort Dashboard', () => {
const wrapper = mount(<Router><SortDashboard.WrappedComponent {...props}/></Router>)
console.log(wrapper.debug())
});
});
}
TLDR;
Need to test a client side component that uses withTracker and withRouter
Need to be able to see the translated text from meteor:universe:i18n in the test
Pulling mock data from the db instead of manually creating it.
The issue may very well be my approach and lack of understanding. Please correct me where-ever necessary. Thanks in advance!

Simple passing of Value from one Component to another through context

I'm new to react native and would like to use Context to keep a socket connection alive between screens in the future. For now, I tried to learn the concept of context just to pass simple values around but the value doesn't get sent.
Tried to follow the tutorial here, but by sending simple values instead.
I create my ValueContext in ValueContext.js here.
import React from 'react';
const ValueContext = React.createContext();
export default ValueContext;
Here's my LoginScreen.js where I set context provider.
import React, { Component } from 'react';
import ConnectionScreen from './ConnectionScreen';
import ValueContext from './ValueContext';
const testValue = 5;
export const sendValue = props => (
<ValueContext.Provider value={testValue}>
<ConnectionScreen />
</ValueContext.Provider>
)
class LoginScreen extends Component {
render() {
return()
}
}
Then in my ConnectionScreen.js
import React, { Component } from 'react';
import { View, Alert } from 'react-native';
import LoginScreen from './LoginScreen';
import ValueContext from './ValueContext';
export const receiveValue = props => (
<ValueContext.Consumer>
{testValue => <ConnectionScreen {...props} testValue={testValue} />}
</ValueContext.Consumer>
)
class ConnectionScreen extends Component {
showAlertValue = () => {
Alert.alert(this.props.testValue);
}
render() {
return(
<View>
{this.showAlertValue()}
</View>
)
}
}
So after setting the value in LoginScreen, I would like to access it in ConnectionScreen. All I get in my alert box is an empty box with no values. Am I doing something wrong here?

Jest - Testing react with multiple HOC's. Material IU, react-router, mobX in Typescript

I'm trying to write unit tests for a project I have. To simplify the issues, I created a small sample project that shows the issues. You can pull it down from github here: Github Sample
Upon Shallow render, the error I get is:
TypeError: Cannot read property 'displayName' of undefined
at createStoreInjector (node_modules/mobx-react/index.js:585:46)
at node_modules/mobx-react/index.js:698:16
at Object.<anonymous> (src/Home/Home.tsx:21:76)
at Object.<anonymous> (src/Home/Home.test.tsx:17:189)
The issue I'm having is that I need to unit test components with multiple HOC's. There is one for styles from Material UI, one for react-router and two for mobX injection and observer. You can see the failing test in /src/Home in the file Home.test.tsx.
I can not figure out how to get a jest test to pass on this component. I also have the issue where I add to the Home Component. It also has the same multiple HOC's so that fails thing as well.
There must be a way to get these types of components tested, but I can't seem to get it to work. Any help would be awesome!
For those that don't want to pull the project, here is a summary of the component under test and the test itself.
Home.tsx
import withStyles, { WithStyles } from '#material-ui/core/styles/withStyles';
import classNames from 'classnames';
import { inject, observer } from 'mobx-react';
import * as React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import logo from '../logo.svg';
import { HomeStore } from '../Stores/HomeStore';
import { styles } from './Home.Styles';
interface IProps extends RouteComponentProps<{}> {
homeStore?: HomeStore;
}
export default withStyles(styles)(
inject('homeStore')(
withRouter(
observer(
class Home extends React.Component<
IProps & RouteComponentProps<{}> & WithStyles<typeof styles>,
{}
> {
public render() {
const { classes } = this.props;
return (
<div className={classes.app}>
<header className={classes.appHeader}>
<img src={logo} className={classNames(classes.appLogo, classes.spin)} alt='logo' />
<h1 className={classes.appTitle}>Welcome to React</h1>
</header>
<p className={classes.appIntro}>
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}))));
Home.test.tsx
import { shallow, ShallowWrapper } from 'enzyme';
import * as React from 'react';
import { MemoryRouter } from 'react-router';
import { HomeStore } from '../Stores/HomeStore';
import Home from './Home';
jest.mock('react-router-dom');
jest.mock('./Home.styles');
const homeStore = {} as HomeStore;
const props = {
homeStore: homeStore,
history: {},
location: {},
match: {},
staticContext: {}
};
describe('Order Tests', () => {
let homeWrapper: ShallowWrapper;
beforeEach(() => {
homeWrapper = shallow(<MemoryRouter><Home {...props} /></MemoryRouter>).first().shallow().first().shallow();
console.log(homeWrapper.debug());
});
it('passes a test', () => {
expect(true).toBe(true);
});
});

React + Redux - Call a method of a component wrapped for the Redux Provider

Whats'up,
I am trying to test some react components that uses redux.
The default behavior should load by a rest call a list of options in a select input. This call is on the method componentDidMount() in my component.
The component works fine, but I cannot simulate the same behavior in my tests.
I cannot call the method componentDidMount() from my instance wrapped by Provider.
Can anyone help me with this
import React from 'react'
import {expect} from 'chai'
import {mount, shallow} from 'enzyme'
import sinon from 'sinon'
import { reducer as formReducer } from 'redux-form'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import ConnectedComponent from '../../../src/components/Component'
describe('Component <Component />', () => {
let store = createStore(combineReducers({ form: formReducer }))
let wrapper = mount(<Provider store={store}><ConnectedComponent /></Provider>)
// this call does not works
wrapper.instance().componentDidMount()
it('should load select input on component mount', () => {
expect(wrapper.find('select option')).to.have.length(12)
})
})
I was able to do like the following :
import React from 'react';
import {connect} from "react-redux";
export class Mock extends React.Component {
constructor(props) {
super(props);
}
myMethod() {
return 123
}
render() {
return (
<div>Test</div>
)
}
}
Mock = connect()(Mock);
export default Mock;
Jest test snippet :
const wrapper = mount(
<Provider store={store}>
<Mock/>
</Provider>
)
let result = wrapper.find(Mock).children().instance().myMethod();
expect(result).toEqual(123);
hope that helps someone!

Issues with initialising Redux in React while using mapDispatchToProps

I'm trying to learn some redux. Component I'm working on is a simble <div> based button that - when clicked - passes value as a parameter to the dispatch, so it can be displayed later someplace else.
After following both documentation and tutorials over the web I came up with the following code:
main app container
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from './../store/configureStore.js'
import Input from './input.js'
let store = configureStore()
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Input />
</Provider>
)
}
}
button container
import React, { Component } from 'react'
import { connect } from 'react-redux'
import printOut from './../actions/actions.js'
import InputComponent from './../components/inputComponent.js'
import PropTypes from 'prop-types'
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
const Input = connect(mapDispatchToProps)(InputComponent)
export default Input
button component
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Input extends Component {
render() {
return (
<div style={style} onClick={this.props.onClick('lala')}>Button!</div>
)
}
}
Input.PropTypes = {
onClick: PropTypes.func.isRequired
}
const style = {
height: 30,
width: 100,
backgroundColor: '#ff4068'
}
export default Input
Application breaks. I got this from the console:
Uncaught TypeError: (0 , _actions2.default) is not a function
at Object.onClick (index.js:33683)
at Input.render (index.js:33743)
(...)
index.js:22443 The above error occurred in the <Input> component:
in Input (created by Connect(Input))
in Connect(Input) (created by App)
in Provider (created by App)
in App
From what little I understood, there are some issues with button component and the way I'm trying to pass the param to props. So I tried to change it a little and added a function to handle that before render.
...
onClick(input) {
return this.props.onClick(input)
}
render() {
return (
<div style={style} onClick={onClick('lala')}>Button!</div>
)
}
...
The error I get this time is onClick is not defined. Oh, ok. I forgot this keyword before calling this new function. So I add it to the component and now I have
<div style={style} onClick={this.onClick('lala')}>Button!</div>
But the error being returned didn't really changed - it's again the original error of Uncaught TypeError: (0 , _actions2.default) is not a function
I'm starting to run out of ideas now. Could someone please tell me how what my be the issue here?
Help me Stack Overflow, you're my only hope! to quote timeless classic.
Are you sure you are importing printOut in properly? Shouldn't it be import { printOut } from './../actions/actions.js' ?
Then, first argument in connect is mapStateToProps and the second is mapDispatchToProps this is probably why you have dispatch is not a function.
You are importing InputComponent:
import InputComponent from './../components/inputComponent.js'
but inside button component you are exporting it as Input:
export default Input
so change InputComponent with :
import Input from './../components/inputComponent.js'
Use this for connect
export default connect(mapDispatchToProps)(Input)
You are facing 2 problems.
1. Syntax problem in your import
The following problem Uncaught TypeError: (0 , _actions2.default) is not a function is caused by the import of your actions.
Instead of
import printOut from './../actions/actions.js'
It should be
import { printOut } from './../actions/actions.js'
2. You are incorrectly using connect
connect accepts these two arguments with the following order:
mapStateToProps: contains the props you want to give to the component
mapDispatchToProps: contains the actions to dispatch
Even if you could call your action, there is no way the dispatch will happen because you call the reducers instead of the dispatch.
What you should do is the following:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { printOut } from './../actions/actions.js';
import InputComponent from './../components/inputComponent.js';
import PropTypes from 'prop-types';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
export default connect(null, mapDispatchToProps)(InputComponent);
You can read this documentation for more details: http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components

Resources