I want to test controlled component using Jest/enzyme.
I have an <Input> component that given max and min props will autocomplete the value when change event is triggered
// Input.tsx
const Input = ({value, onChange, max, min}) => {
const handleChange = (evt) => {
const newValue = Number(evt.target.value)
if(min !== undefined && newValue < min) {
onChange(min)
return
}
if(max !== undefined && newValue > max) {
onChange(max)
return
}
onChange(newValue)
}
return (
<div>
<label>My input</label>
<input type="number" value={value} onChange={handleChange} />
</div>
)
}
I would like to test the component and its different change use-case with jest.
Because it's a fully controlled component I start to write the following code:
const onChangeSpy = jest.fn()
const Wrapper = () => {
const [state] = React.useState(0)
return (
<Input
value={state}
onChange={onChangeSpy}
min={0}
max={50}
/>
)
}
const wrapper = mount(<Wrapper />)
wrapper
.find('input')
.simulate('change', { target: { value: 25 } })
.simulate('change', { target: { value: 115 } })
expect(onChangeSpy.mock.calls).toHaveLength(2) // pass
But I am running in an issue: The value prop of the <Input> is never updated because I never use the React.setState. My example is dumb and I may not need it, but in more complex real world scenario, I would like to setup some state by triggering events and then either test my state value or my onChangeSpy
How could I achieve this goal? Updating the state and be able to test it?
Update
I found a workaround that works for me.
It combines useState and jest.fn()
const onChangeSpy = jest.fn()
const Wrapper = () => {
const [state, setState] = React.useState(0)
return (
<Input
value={state}
// Call both spy and setState (not clean but working)
onChange={(val) => {
onChangeSpy(val)
setState(val)
}}
min={0}
max={50}
/>
)
}
const wrapper = mount(<Wrapper />)
wrapper
.find('input')
.simulate('change', { target: { value: 25 } })
.simulate('change', { target: { value: 115 } })
expect(onChangeSpy.mock.calls).toHaveLength(2) // pass
expect(wrapper.find('input').props().value).toEqual('115') // pass