Why is my button undefined in my test? - reactjs

My test:
describe('button component', () => {
it('should toggle off when clicked', () => {
let component;
component = ReactTestUtils.renderIntoDocument(<Search />);
let searchbtn = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'button');
ReactTestUtils.Simulate.click(searchbtn);
console.log(searchbtn, 'search button***'); //UNDEFINED
expect(searchbtn.calledOnce).to.equal(false);
})
});
This is my search component:
render() {
return (
<div className="search">
<button className="searchButton" onClick={this.handleSearch}>{this.state.on ? 'ON' : 'OFF'}</button>
</div>
);
}
Do I need to spy on it or mock it? or is there a better way to test buttons in react?

Since you have added enzyme tag I will answer using enzyme.
It can be tested very easily via shallow rendering -
const searchWrapper = shallow(<Search />);
const button = searchWrapper.find('button').first();
When you simulate click event on button the onClick handler provided via onClick prop which is handleSearch in your case will be called.
So if you are setting some state based on the onClick function call corresponding ui changes based on the state changes can be compared or checked if changes were reflecting correctly in the dom.
or
if you just want to check if method was called or not by using a fake method of similar name -
const onButtonClick = sinon.spy();
expect(onButtonClick.calledOnce).to.equal(false);
button.setProps({ onClick:onButtonClick});
button.simulate('click');
expect(onButtonClick.calledOnce).to.equal(true);

Related

How to test button that on click renders html element within the same component in React testing library, Jest?

My OrderModuleHeader is a clickable div that renders div when clicked like shown below:
const [orderModuleIsOpen, setOrderModuleIsOpen] = useState(true);
render (
<div>
<OrderModuleHeader data-testid="orderModuleHeaderButton"
onClick={() => setOrderModuleIsOpen(!orderModuleIsOpen)}
>
<NavigationArrow>
<FontAwesomeIcon
icon={orderModuleIsOpen ? faChevronUp : faChevronDown}
className="fa-xs"
/>
</NavigationArrow>
</OrderModuleHeader>
{orderModuleIsOpen && (
<OrderModuleBody>Some content</OrderModuleBody>
)
</div>)
So div OrderModuleBody is displayed only when we click the button.
I started my test by the following:
describe('Expand body button', () => {
it('should render OrderModuleBody when clicked', () => {
const button = screen.getByTestId('orderModuleHeaderButton');
fireEvent.change() ...
});
}
I realize that I made div clickable and in test I call it a button.
I don't know what exactly I need to write in fireEvent to test if OrderModuleBody (div) renders on click.
NOTE: I'm not allowed to bring in shallow method from Enzyme or use any other testing frameworks except Jest, React testing library.
Any advice is appreciated.
You'll need to render your component in order to test it. You then access the button after which you simulate a button click, and then eventually expect your change to occur.
The testing library has a function named userEvent which can simulate a button click.
import { screen } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
describe('Expand body button', async () => {
it('should render OrderModuleBody when clicked', () => {
// render your component
render(<YourComponent />)
// access your button
const button = screen.getByTestId('orderModuleHeaderButton')
// simulate button click
userEvent.click(button);
// expect result
await waitFor(() =>
expect(screen.getByText("Some content")).toBeInTheDocument();
});
}

Trying to test a simulate change on a textinput component using Jest

I'm trying to simulate a change event on a component(ListPage)
<TextInput
className="search-s"
id="textM"
width="m"
type="search"
handleChange={this.props.updateS}
placeholder="Search for a scenario"
/>
</div>
The handleChange attribute calls a prop function called updateS which looks like
updateS(e) {
this.setState({
name: e.target.value,
});
}
And what I currently have for the test function
it("should call handleChange on change with the correct params", () => {
const wrapper = shallow(<ListPage />);
const spy = jest.spyOn(wrapper.instance(), "handleChange");
wrapper.instance().forceUpdate();
const p = wrapper.find(".search-s");
p.simulate("change");
expect(spy).toHaveBeenCalled();
});
For some reason my test function doesnt work when I try to simulate a change and check whether the updateSearch function was called. All the guides online have examples of testing functions within the component but not passed props which I feel is what is causing the problem. Any insight would be great

how to test a method of a component in enzyme is it calls a method which is passed as prop

handleConfirm = () => {
this.props.handleCompletionDateChange();
this.setState({ showInformation: false });
};
I have above method handleConfirm in our class component DateSection
if I test handleConfirm method using below code I am getting error as
TypeError: _this.props.handleCompletionDateChange is not a function
const wrapper1 = shallow(<DateSection {...props} />);
const instance = wrapper1.instance() as any;
spyOn(instance, 'handleConfirm').and.callThrough();
expect(instance.handleConfirm()).toBe(true);
expect(instance.handleConfirm).toHaveBeenCalled();
How should pass the above function as a prop while testing,
You better not access instance() in tests. Tests become fragile(say, converting component into functional will break everything while component itself can be fine) and less reliable(if you calling methods that will never be called in real life - e.g. if that method is not called by others and not bound as event handler).\
Instead you need to find way to call that with the help of .simulate() or .props().someCabllackProp(...).
Assuming this handleConfirm is onClick handled for some <button name="confirm">:
it('calls handleCompletionDateChange', () => {
const handleCompletionDateChange = jest.fn();
const wrapper1 = shallow(<DateSection
{...props}
handleCompletionDateChange={handleCompletionDateChange} \
/>);
wrapper1.find("button[name='confirm']").simulate('click');
expect(handleCompletionDateChange).toHaveBeenCalled();
})
The same to validate this.setState({ showInformation: false }); part. We need to figure out how to validate that based on render results.
Something like
expect(wrapper1.find("[data-id='information-block']")).toHaveLength(0);

How to test a function that has been passed by props

If I have a component like this
import React from "react";
const GettingStarted = ({ object }) => <Button onPress={() => object.next()} />;
How would you test with jest that object.next() gets called inside the component?
The basic idea would be to spy on the function being passed to the onPress. Then, you would simulate an onPress event on the button and check that the spied upon function was called with any parameters, etc. Then you would test the actual output of the function. So, for example, if the function changes the text in the button from 'Click Me' to 'Clicked!', you would assert on the first text property before the click and then check the updated one.
Example with Jest:
const onPressSpy = jest.fn();
const gettingStartedButton = shallow(<GettingStarted object={onPressSpy} />);
expect(gettingStartedButton.find('button').children().text()).toBe('Click Me!');
gettingStartedButton.find('button').simulate('press');
expect(onPressSpy).toHaveBeenCalled();
expect(gettingStartedButton.find('button').children().text()).toBe('Clicked!');

testing custom react methods with jest and enzyme

I am trying to test a method within a react component. The component is a form and it should be testing the handleSubmit() method gets called when the submit button is clicked. I have tried the below.
it('handlesSubmit when submit button is clicked', () => {
wrapper.find(Button).simulate('click');
expect(wrapper.instance().handleSubmit).toHaveBeenCalled();
})
This gave an error jest.fn() value must be a mock function or spy. So I tried this:
it('handlesSubmit when submit button is clicked', () => {
const handleSubmit = jest.fn();
wrapper.find(Button).simulate('click');
expect(handleSubmit).toHaveBeenCalled();
})
This errors saying Expected mock function to have been called
The first block fails because wrapper.instance().handleSubmit is not a jest mock function; it is whatever the class method defines it as.
The second block fails because handleSubmit, while it is a jest mock function, is not tied to your wrapper component at all. It is a local variable. When you simulate the click, it is again calling the actual implementation.
In order to accomplish what you are trying to do, you have to do something like this
it('handlesSubmit when submit button is clicked', () => {
const handleSubmit = jest.fn();
WrapperComponent.prototype.handleSubmit = handleSubmit;
const wrapper = shallow(<WrapperComponent />);
wrapper.find(Button).simulate('click');
expect(handleSubmit).toHaveBeenCalled();
})
where WrapperComponent is the component you are testing.
The above ought to work, but you can sometimes accomplish something similar in a nicer way. Depending on your component's implementation, it is oftentimes easier to test that the functionality in your handleSubmit method is called rather than the handleSubmit method itself. For instance, if my component was something like
class TestComponent extends React.Component {
constructor(props) {
super(props)
this.state = { clicked: false }
this.onClick = this.onClick.bind(this)
}
onClick() {
this.props.onClick()
this.setState({ clicked: true })
}
render() {
return (
<button onClick={ this.onClick }>
{ 'Click Me' }
</button>
)
}
}
I could test it by doing
it('calls onClick props and sets clicked state to true when clicked', () => {
const onClick = jest.fn();
const testComp = shallow(<TestComponent onClick={ onClick } />);
wrapper.find('button').simulate('click');
expect(onClick).toHaveBeenCalled();
expect(testComp.state('clicked')).toBe(true)
})
I generally prefer this type of test because I don't have to overwrite prototype, and it is actually testing that the click triggers the logic I expect. The original test really only covers that I am passing this.handleSubmit as an onClick prop to a Button component and nothing more.

Resources