I'm trying to write some jest tests for a simple weather web app I made with React. When I run my test, it creates a snapshot of the component and it looks ok, but when I try to trigger a click event I get a type error:
TypeError: tree.props.handleClick is not a function
I used the jest docs to write this test and I thought this was how you triggered a click event. Am I not referencing the click function correctly? I'm new to writing tests so any info on writing tests for React with jest are welcome!
React code
import React from "react"
import { useEffect, useState } from 'react'
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
const CityEntry = ({ weatherDatum, deleteLocation }) => {
const capitalizeFirstLetter = (string) => {
return string.charAt(0).toUpperCase() string.slice(1);
}
const handleClick= () => {
deleteLocation(weatherDatum.id)
}
return (
<div className="city">
<p className="location flex-item">{capitalizeFirstLetter(weatherDatum.location)} </p>
<p className="temp flex-item">{weatherDatum.temperature} ℉</p>
<p className="feels-like flex-item">Feels like: {weatherDatum.feelsLike}</p>
<p className="description flex-item">{capitalizeFirstLetter(weatherDatum.description)}</p>
<p><img className="icon flex-item" src={`https://openweathermap.org/img/w/${weatherDatum.icon}.png`}></img></p>
<button className="delete" onClick={handleClick}>Delete</button>
</div>
)
}
export default CityEntry;
Jest test code
import renderer from 'react-test-renderer';
import CityEntry from '../src/Components/CityEntry.js'
it('deletes a city entry when clicked', () => {
const component = renderer.create(
<CityEntry weatherDatum={{ id: '', lat: '', lon: '', location: '', temperature: '', feelsLike: '', description: '', icon: '' }} />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot()
renderer.act(() => {
tree.props.handleClick()
});
tree = component.toJSON();
expect(tree).toMatchSnapshot()
})
jest snapshot that was created
exports[`deletes a city entry when clicked 1`] = `
<div
className="city"
>
<p
className="location flex-item"
>
</p>
<p
className="temp flex-item"
>
℉
</p>
<p
className="feels-like flex-item"
>
Feels like:
</p>
<p
className="description flex-item"
/>
<p>
<img
className="icon flex-item"
src="https://openweathermap.org/img/w/.png"
/>
</p>
<button
className="delete"
onClick={[Function]}
>
Delete
</button>
</div>
`;
CodePudding user response:
The JSON representation of the component is only for assertions. You can't use it to manipulate the state of the component. JSON can't contain functions, it's just static data.
To simulate a button click you need to instead use the methods available on TestInstance.
It's probably also best to trigger the actual click handler on the button so that the other deleteLocation
function is called.
import renderer from 'react-test-renderer';
import CityEntry from '../src/Components/CityEntry.js'
it('deletes a city entry when clicked', () => {
const component = renderer.create(
<CityEntry weatherDatum={{ id: '', lat: '', lon: '', location: '', temperature: '', feelsLike: '', description: '', icon: '' }} />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot()
renderer.act(() => {
component.root.findByType('button').props.onClick();
});
tree = component.toJSON();
expect(tree).toMatchSnapshot()
})
By the way, you might consider using React Testing Library. The react-test-renderer
API is really quite low level and most people find this more ergonomic. Most React test suites I see that are created recently go down this road.
Philosophically it also prevents what you were about to do, which is to grab hold of the props and manipulate them directly. That's because good tests only ever interact with the elements as a real user would.