React Testing Library - fireEvent doesn't change input value
Asked Answered
L

3

11

🙂 I'm learning testing in React, but fireEvent doesn't change value on my input. Unfotunanently, I don't know why?

I'm rendering BuyPlace component. In this component I have form which has Input components (these components are simply label with input and I'm passing props to this compoments on form).

On screen.debug() I can see that my components is rendered corretly with this form and all of these inputs.

Also, my input is catched corretly in my test, I checked this.

This is my test:

test("should change input value", async () => {
render(
<HashRouter>
<UserContext.Provider value={{ user: true }}>
<BuyPlace />
</UserContext.Provider>
</HashRouter>
);
const nameInputEl = screen.getByPlaceholderText("Podaj własną nazwę");
fireEvent.change(nameInputEl, {
target: { value: "Michał" },
});
await waitFor(() => {
expect(nameInputEl.value).toBe("Michał");
});
});

Test is not passing. Ofcourse my app is working corretly, but on test it doesn't work.

This is handleInputChange on my component:

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => {
setInputValues((prevValues) => {
return {
...prevValues,
[e.target.name]: e.target.value,
}
});
};

Every solution which I found is not working... My value on input is always empty string. Can someone help me with this? I want to learn to write good tests, but I don't know where is the problem.

EDIT I saw something interesting on screen.debug().

My input on test is rendered without onchange attribute, this is normal on tests? Check this out:

                   <input
                    id="name"
                    name="name"
                    placeholder="Podaj własną nazwę"
                    type="text"
                    value=""
                  />

On my rendered parent component I have handleInputChange and this function is passing to the Input components. On live everything is working but on test is not... Maybe this is why fireEvent is not working?

But why on test my input doesn't have onchange attribute?

EDIT 2 SOLUTION

Wow, after 3 days I found where the problem was!

The problem was in setState function which changes values. I had:

const myValues = (object with values);

setState((previousValues) => {
...previousValues,
[e.target.name]: e.target.value
})

But after changing on this:

setState({
...myValues,
[e.target.name]: e.target.value
})

My test is passing. So I can't use previousState callback on setState - this is why my test was incorrect. But... why? This is a question for me!

Litt answered 22/2, 2022 at 18:10 Comment(4)
Instead of fireEvent.change, does fireEvent.input work? – Conjunction
This is not working also... – Litt
@Litt In previous code, you were using setState with a function. (state) => {}, this function won't return anything so it will not work as expected.(state) => ({}), when you enclose the curly braced object within small brackets, it will be treated as an Object instead of a block. and the state will be changed as expected. When you removed () =>, then it became an Object instead of a function. So the state is properly updated. () => Value returns Value but in order to return an object directly you need to enclose it in small bracket. or use this syntax, () => { return {}; } – Ruffian
I have a similar problem using JSONForms. I have no control over the individual inputs and use a regular setState(object). However, the inputs rendered in screen.debug() are also lacking the onChange attribute. Is this normal? Are you, now after you fixed everything, seeing a onChange attribute in the screen debug? – Chore
H
6

It's recommended to use userEvent over fireEvent. Especially if you are using Material-UI, as there's been issues with using fireEvent rather userEvent

Note by testing library documentation

Most projects have a few use cases for fireEvent, but the majority of the time you should probably use @testing-library/user-event.

Instead of fireEvent.change you would do something as such

userEvent.type(nameInputEl, 'Michal');
Hawsepiece answered 23/2, 2022 at 18:4 Comment(4)
Thank you very much - I also tried with userEvent, it doesn't work... It's impossible! – Litt
@Litt Bummer, question: If you place a log in onChange does it called? – Hawsepiece
finally I found where the problem was. Please, read about it on my question - I edited this. But still - I'm wondering why that was not work? Maybe you can answer me! :) – Litt
What was the issue? I am facing the same problem. I placed a console.log() inside the onChange handler I am getting console output there, but ` expect(input.value).toBe("11");` This line gives error since value is "" empty string. Screen.debug() shows correct input being rendered and used for firing change event – Trainor
P
3

Hope you are aware this does not return anything yet

this piece is from you question;

setState((previousValues) => {
...previousValues,
[e.target.name]: e.target.value
})

you need to wrap it like below;

setState((previousValues) => ({
...previousValues,
[e.target.name]: e.target.value
}))
Pali answered 6/6, 2022 at 19:58 Comment(0)
B
1

Try with fireEvent.focus instead of fireEvent.click

it('test date', () => {
        render(<DateInput />)

        const startDateInput = screen.getByLabelText('Start')
        const endDateInput = screen.getByLabelText('End')

        // Setting the dates
        fireEvent.focus(startDateInput, { target: { value: '2023-07-01' } })
        fireEvent.focus(endDateInput, { target: { value: '2023-07-15' } })

        // Comparing dates
        expect(startDateInput).toHaveValue('2023-07-01')
        expect(endDateInput).toHaveValue('2023-07-15')
    })
    ```
Belfort answered 21/6 at 12:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.