Simulate change with material UI and Enzyme not working - reactjs

When I try to simulate a change event with Enzyme in my Material-UI component, value is not changing.
My component looks like this:
export class EditableList extends Component {
...
onChangeNewEntry = event => {
this.setState({newEntry: event.target.value});
};
render() {
const {classes} = this.props;
return (
<div className={classes.div_main}>
<List>
<Paper className={classes.paper_title} elevation={2} key="NewEntry">
<ListItem>
<InputBase
data-testid='input-base'
className={classes.inputBase}
placeholder="New Entry"
value={this.state.newEntry}
onKeyPress={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
this.onCreateItem();
}
}}
onChange={this.onChangeNewEntry}
multiline
/>
</ListItem>
</Paper>
</List>
</div>
);
}
}
My test looks like this:
import React from 'react';
import {Provider} from "react-redux";
import EditableList from "./EditableList";
import store from "../../../../store";
import {createMount, createShallow} from "#material-ui/core/test-utils";
import {InputBase} from "#material-ui/core";
let mount;
let wrapper;
beforeEach(() => {
mount = createMount();
wrapper = mount(
<Provider store={store}>
<EditableList/>
</Provider>
);
});
afterEach(() => {
mount.cleanUp();
});
const findComponent = ( value) => {
return wrapper.find(`[data-testid='${value}']`).first();
};
it('allow user to write input for new item', () => {
const inputBase = findComponent('input-base');
const value = "New item";
inputBase.simulate('change', {target: {value: value}});
wrapper.update();
console.log(inputBase.prop('value'));
});
In the console, the valueis always an empty string.
I tried also the following without success:
wrapper.find(InputBase).at(0).props().onChange({target: {value: value}});
wrapper.find(InputBase).at(0).props().onChange({target: {name: 'InputBase', value: value}});

Related

Material-UI: the value provided `undefined` to the Tabs component is invalid

I am not having this warning probably on the normal functioning of the app but I am having this problem in the unit tests. So I am doing a unit test for a Tabs component and it gives me the following warning:
my jsx file looks like this:
class SimpleTabs extends React.Component {
handleChange = (event, value) => {
const { onChange } = this.props;
onChange(value);
};
render() {
const { classes, selectedChannelIndex } = this.props;
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs
key={selectedChannelIndex}
value={selectedChannelIndex}
onChange={this.handleChange}
classes={{ root: classes.tabsRoot, indicator: classes.tabsIndicator }}
>
{CHANNELS_ARRAY &&
CHANNELS_ARRAY.map((channel, i) => (
<Tab
key={i}
value={i}
label={channel.channel}
classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
/>
))}
</Tabs>
</AppBar>
<TabContainer>{this.props.children}</TabContainer>
</div>
);
}
}
export default withStyles(styles)(SimpleTabs);
and my unit test file looks like this:
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { configure, mount } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
import Tabs from '../../../src/components/common/Tabs/Tabs';
configure({ adapter: new Adapter() });
const defaultProps = { selectedChannelIndex: 0, value: 0, selectInput: 0 };
const setup = (props = {}) => {
const setupProps = { ...defaultProps, ...props };
return shallow(<Tabs {...setupProps} />);
};
describe('Tabs', () => {
const wrapper = mount(<Tabs {...defaultProps} />);
it('should be defined', () => {
expect(Tabs).toBeDefined();
});
it('should render correctly', () => {
const tree = mount(<Tabs />);
expect(shallowToJson(tree)).toMatchSnapshot();
});
});
I've seen others asking about this warning as well but many times it's said that to add the value on the tabs element is the solution but is not working for me.

React unit test is failed because of keep all dom tests

I have a note list component that is connected to redux and render a list of items. I create 2 unit test for testing component for when component have list and when is empty. A first unit test no notes on the list is a success but when testing having multiple notes on the list it keeps the previous unit test dom and the test is failed.
import React from 'react';
import '#testing-library/jest-dom/extend-expect';
import { render, screen } from '#testing-library/react';
import configureStore from 'redux-mock-store';
import NoteList from '../../../../../src/components/note/note-list-children/NoteList';
const tasks = [
{
title: 'e2e',
content: 'Learning E2E',
date: '2020/03/05',
id: 'evRFE5i5',
done: false,
},
{
title: 'unit',
content: 'Learning Unit Test',
date: '2020/03/04',
id: 'FTSTSrn7',
done: false,
},
];
describe('No notes on list.', () => {
const mockStore = configureStore();
const initialState = {
tasksReducer: [],
mainReducer: {},
};
const store = mockStore(initialState);
beforeEach(() => {
render(<NoteList store={store} tasks={[]} />);
});
it('Should having no notes on the list (showing a proper message).', () => {
expect(screen.getByText(/No notes yet./i)).toBeTruthy();
});
});
describe('Having multiple notes on the list.', () => {
beforeEach(() => {
const mockStore = configureStore();
const initialState = {
tasksReducer: [],
mainReducer: {},
};
const store = mockStore(initialState);
render(<NoteList store={store} tasks={tasks} />);
});
it('Should not showing empty massege for note lists', () => {
screen.debug();
expect(screen.queryByText(/No notes yet./i)).not.toBeInTheDocument();
});
});
Note list component
import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link';
// Redux
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { deleteTask, doneTask } from '../../../redux/actions/actionTasks';
import { activeMain } from '../../../redux/actions/actionMain';
// Materail UI
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction';
import ListItemText from '#material-ui/core/ListItemText';
import Checkbox from '#material-ui/core/Checkbox';
import IconButton from '#material-ui/core/IconButton';
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
// Component
import DeleteNotes from './DeleteNotes';
const NoteList = props => {
const { tasks, active } = props;
const handleActiveMain = item => {
props.activeMain('singleNote', item.title, item.content, item.date, item.id, item.done);
};
const handleToggle = item => {
if (item.done === false) {
props.doneTask(true, item.id);
} else {
props.doneTask(false, item.id);
}
};
const handleDeleteNote = id => {
props.deleteTask(id);
if (active.id == id || tasks.length === 1) {
props.activeMain('create');
}
};
const handleEditNote = item => {
props.activeMain('edit', item.title, item.content, item.date, item.id, item.done);
};
return (
<div className="list-tasks" data-test="note-list">
<List className="note-list">
{tasks.length ? (
tasks.map(item => (
<ListItem ContainerProps={{ 'data-test': 'note-item' }} key={item.id} dense button>
<ListItemIcon>
<Checkbox
edge="start"
onClick={() => handleToggle(item)}
checked={item.done === true}
tabIndex={-1}
disableRipple
inputProps={{ 'aria-labelledby': item.id, 'data-test': 'note-check' }}
/>
</ListItemIcon>
<Link href="/" as={process.env.BACKEND_URL + '/'}>
<ListItemText
onClick={() => handleActiveMain(item)}
className={item.done === true ? 'done' : ''}
id={item.id}
primary={<span data-test="note-title">{item.title}</span>}
secondary={<span data-test="note-date">{item.date}</span>}
/>
</Link>
<ListItemSecondaryAction>
<IconButton
onClick={() => handleDeleteNote(item.id)}
edge="end"
aria-label="delete"
>
<DeleteIcon data-test="note-delete" />
</IconButton>
<Link
href={`/edit?id=${item.id}`}
as={`${process.env.BACKEND_URL}/edit?id=${item.id}`}
>
<IconButton onClick={() => handleEditNote(item)} edge="end" aria-label="edit">
<EditIcon data-test="note-edit" />
</IconButton>
</Link>
</ListItemSecondaryAction>
</ListItem>
))
) : (
<p data-test="no-note-yet">No notes yet.</p>
)}
</List>
{tasks.some(item => item.done === true) === true && <DeleteNotes selectedNotes={tasks} />}
</div>
);
};
NoteList.propTypes = {
active: PropTypes.object.isRequired,
tasks: PropTypes.array.isRequired,
deleteTask: PropTypes.func.isRequired,
activeMain: PropTypes.func.isRequired,
doneTask: PropTypes.func.isRequired,
};
const mapDispatchToProps = dispatch => {
return {
deleteTask: bindActionCreators(deleteTask, dispatch),
activeMain: bindActionCreators(activeMain, dispatch),
doneTask: bindActionCreators(doneTask, dispatch),
};
};
const mapStateToProps = state => {
return {
active: state.mainReducer,
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(NoteList);

How to mock stateless child component event when testing parent component

as mentioned in the title I'm trying to set up some test for <Search /> component, in particular I want to test the useState hooks.
After mocking the Redux store and creating a shallow wrapper I tried to simulate an input from the child component DisplaySearchBar but apparently I cannot even mamage to select it.
That's the error I get:
Method “props” is meant to be run on 1 node. 0 found instead.
Here's Search.js
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { handleScriptLoad } from '../../../helpers/Autocomplete';
import { getRestaurants, setAlert } from '../../../actions/restaurantAction';
import DisplaySearchBar from '../../layout/DisplaySearchBar/DisplaySearchBar';
import styles from './Search.module.scss';
const Search = ({ getRestaurants, setAlert }) => {
const [where, setWhere] = useState('');
const [what, setWhat] = useState('');
const [sortBy, setSortBy] = useState('rating');
const sortByOptions = {
'Highest Rated': 'rating',
'Best Match': 'best_match',
'Most Reviewed': 'review_count',
};
// give active class to option selected
const getSortByClass = (sortByOption) => {
if (sortBy === sortByOption) {
return styles.active;
} else {
return '';
}
};
// set the state of a sorting option
const handleSortByChange = (sortByOption) => {
setSortBy(sortByOption);
};
//handle input changes
const handleChange = (e) => {
if (e.target.name === 'what') {
setWhat(e.target.value);
} else if (e.target.name === 'where') {
setWhere(e.target.value);
}
};
const onSubmit = (e) => {
e.preventDefault();
if (where && what) {
getRestaurants({ where, what, sortBy });
setWhere('');
setWhat('');
setSortBy('best_match');
} else {
setAlert('Please fill all the inputs');
}
};
// displays sort options
const renderSortByOptions = () => {
return Object.keys(sortByOptions).map((sortByOption) => {
let sortByOptionValue = sortByOptions[sortByOption];
return (
<li
className={getSortByClass(sortByOptionValue)}
key={sortByOptionValue}
onClick={() => handleSortByChange(sortByOptionValue)}
>
{sortByOption}
</li>
);
});
};
return (
<DisplaySearchBar
onSubmit={onSubmit}
handleChange={handleChange}
renderSortByOptions={renderSortByOptions}
where={where}
what={what}
handleScriptLoad={handleScriptLoad}
/>
);
};
Search.propTypes = {
getRestaurants: PropTypes.func.isRequired,
setAlert: PropTypes.func.isRequired,
};
export default connect(null, { getRestaurants, setAlert })(Search);
DisplaySearchBar.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { clearSearch } from '../../../actions/restaurantAction';
//Import React Script Libraray to load Google object
import Script from 'react-load-script';
import Fade from 'react-reveal/Fade';
import Alert from '../Alert/Alert';
import styles from './DisplaySearchBar.module.scss';
const DisplaySearchBar = ({
renderSortByOptions,
onSubmit,
where,
handleChange,
what,
handleScriptLoad,
restaurants,
clearSearch,
}) => {
const googleUrl = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_API_KEY}&libraries=places`;
// {googleUrl && <Script url={googleUrl} onLoad={handleScriptLoad} />}
return (
<section className={styles.searchBar}>
<form onSubmit={onSubmit} className={styles.searchBarForm}>
<legend className="title">
<Fade left>
<h1>Where are you going to eat tonight?</h1>
</Fade>
</legend>
<Fade>
<fieldset className={styles.searchBarInput}>
<input
type="text"
name="where"
placeholder="Where do you want to eat?"
value={where}
onChange={handleChange}
id="autocomplete"
/>
<input
type="text"
name="what"
placeholder="What do you want to eat?"
onChange={handleChange}
value={what}
/>
<div className={styles.alertHolder}>
<Alert />
</div>
</fieldset>
<fieldset className={styles.searchBarSubmit}>
<input
id="mainSubmit"
className={`${styles.myButton} button`}
type="submit"
name="submit"
value="Search"
></input>
{restaurants.length > 0 && (
<button
className={`${styles.clearButton} button`}
onClick={clearSearch}
>
Clear
</button>
)}
</fieldset>
</Fade>
</form>
<article className={styles.searchBarSortOptions}>
<Fade>
<ul>{renderSortByOptions()}</ul>
</Fade>
</article>
</section>
);
};
DisplaySearchBar.propTypes = {
renderSortByOptions: PropTypes.func.isRequired,
where: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
what: PropTypes.string.isRequired,
handleScriptLoad: PropTypes.func.isRequired,
restaurants: PropTypes.array.isRequired,
clearSearch: PropTypes.func.isRequired,
};
const mapStatetoProps = (state) => ({
restaurants: state.restaurants.restaurants,
});
export default connect(mapStatetoProps, { clearSearch })(DisplaySearchBar);
And Search.test.js
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import Search from '../Search';
import DisplaySearchBar from '../../../layout/DisplaySearchBar/DisplaySearchBar';
const mockStore = configureStore();
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
describe('Search', () => {
test('renders withut errors', () => {
const store = mockStore(initialState);
const wrapper = mount(
<Provider store={store}>
<Search setAlert={jest.fn()} getRestaurants={jest.fn()} />
</Provider>
);
wrapper.find(DisplaySearchBar).props();
});
});
Thanks for your help!
shallow doesn't work for react-redux new versions (>= 6).
Use mount instead:
const wrapper = mount( // <-- changed shallow to mount.
<Provider store={store}>
<Search {...props} />
</Provider>
);
Run It On Sandbox (Use tests tab to run tests.)
Try to mount it like this:
const wrapper = shallow(
<Provider store={store} />
<Search setAlert=jest.fn() getRestaurants=jest.fn() />
</Provider>
);

Simulate Is not working without state in react js

This is my functional Component.
const InputText = (props: Props) => {
return (
<div>
<StyledInput type="text" placeholder={props.placeholder} />
<br></br>
</div>
);
};
export default InputText;
and this is my test cases.
enter code here
const wrap = (props: Props) => shallow(<InputText {...props} />);
it("input text change", () => {
// const wrap = (props: Props) => mount(<StyledInput {...props} />);
const wrapper = wrap({ placeholder: "UserName" });
const usernameInput = wrapper.find("input");
usernameInput.simulate("change", { target: { value: "umesh#hcl.com" } });
expect(usernameInput.text()).toEqual("umesh#hcl.com");
});
So this test case failed with error:
Expected "Umesh#hcl.com" and recevide:"";
How can I resolve this?
Check this one
App.test.js
import React from "react";
import { shallow, configure } from "enzyme";
import Input from "./App";
import Adapter from 'enzyme-adapter-react-16';
configure({adapter: new Adapter()});
describe("Input Component", () => {
it("should trigger the onchange", () => {
const wrapper = shallow(<Input />);
wrapper
.find("input")
.simulate("change", { target: { value: "on typing" } });
expect(wrapper.find('input').props().value).toBe('on typing')
});
});
App.js
import React, { useState } from "react";
const InputText = props => {
const [inputText, setInput] = useState("");
const onHandleChange = e => {
setInput(e.target.value);
};
return (
<div>
<input
type="text"
value={inputText}
onChange={onHandleChange}
/>
<br />
</div>
);
};
export default InputText;

Enzyme unit testing onChange method using Material UI Components

How would i be able to unit-test onChange method on this component.
Comment.js
import React from "react";
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
const Comment = (props) => (
<div>
<form onSubmit={props.onSubmit}>
<TextField
type="text"
id="outlined-multiline-static"
label="Write A Comment"
multiline
name="comment_body"
value={props.commentBody}
rows="10"
fullWidth
margin="normal"
variant="outlined"
onChange={props.commentChange}
/>
{/* <Button type="submit" variant="outlined" component="span" color="primary">
Post A Comment
</Button> */}
<button type="submit" variant="outlined" component="span" color="primary">
Write a Comment
</button>
</form>
</div>
)
export default Comment;
This is my attempt to unit test the onChange component, getting a
Method “simulate” is meant to be run on 1 node. 0 found instead
around this line
const component = shallow(<Comment commentChange={onChangeMock} commentBody={'test'} />)
component.find('input').simulate('change');
Comment.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import Comment from './Comment';
describe('Should render <Comment/> component', () => {
it('Should render form', () => {
const wrapper = shallow(<Comment/>)
// wrapper.find('Form').at(0)
expect(wrapper.find("form")).toHaveLength(1); // checks if there is a form.
})
it('Should render button', () => {
const wrapper = shallow(<Comment/>)
expect(wrapper.find('button')).toHaveLength(1);
})
it('should check for onChange method', () => {
// const wrapper = shallow(<Comment onChange={}/>)
const onChangeMock = jest.fn();
// const event = {
// preventDefualt(){},
// target: {
// value: 'testing'
// }
// }
const component = shallow(<Comment commentChange={onChangeMock} commentBody={'test'} />)
component.find('input').simulate('change');
expect(onChangeMock).toBeCalledWith('test')
})
})
The Comment component is being passed in another component like this:
ImageContainer.js
state = {
isComment: false,
comment_body: ""
}
handleCommentChange = (e) => {
this.setState({
comment_body: e.target.value
})
}
commentSubmit = (event, id) => {
event.preventDefault();
console.log(this.state.comment_body); // doesn't get console.log
// note that commentBody is being used for the req.body as well so its called by req.body.commentBody
const commentBody = this.state.comment_body
const data = {
commentBody,
id
}
this.props.postComment(data);
this.setState({
comment_body: ''
})
}
<Comment onSubmit={(e) => this.commentSubmit(e, img.id)}
commentBody={this.state.comment_body }
commentChange={this.handleCommentChange}/>
The reason you are having the error is because when you call component.find('input') it returns an array of matched components, so what you want to do is
component.find('input').at(0).simulate('change')
However, there is another way you can test this, which is my preferred method.
component.find('input').at(0).props().onChange()
Below is the correct way to do the test with both methods
import React from "react";
import Enzyme, { shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Comment from "./Comment";
import TextField from "#material-ui/core/TextField";
Enzyme.configure({ adapter: new Adapter() });
describe("Should render <Comment/> component", () => {
it("should check for onChange method (1)", () => {
// const wrapper = shallow(<Comment onChange={}/>)
const onChangeMock = jest.fn();
const component = shallow(
<Comment commentChange={onChangeMock} commentBody={"test"} />
);
component
.find(TextField)
.at(0)
.simulate("change", "test");
expect(onChangeMock).toBeCalledWith("test");
});
it("should check for onChange method (2)", () => {
// const wrapper = shallow(<Comment onChange={}/>)
const onChangeMock = jest.fn();
const component = shallow(
<Comment commentChange={onChangeMock} commentBody={"test"} />
);
component
.find(TextField)
.at(0)
.props()
.onChange();
expect(onChangeMock).toBeCalled();
});
});
For this particular test it will be better if you just use toBeCalled rather than toBeCalledWith. There is no need to test the value it is called with.

Resources