I'm currenty trying to mock the react-select component and no matter what i do the firevent.change from react-testing-library seems to only fire on the first call of firevent.change.
I was wondering why the change was only firing once and how to fix it.
My jest mock is as follows:
jest.mock("react-select", () => ({ options, value, onChange }) => {
function handleChange(event) {
console.log("called");
const option = options.find(option => {
return option.value == event.currentTarget.value;
});
onChange(option);
}
return (
<select
id="uc"
data-testid="select"
value={value}
onChange={event => handleChange(event)}
>
{options?.map(({ label, value }) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
);
});
As you can see its a fairly simple select block and i thought i should be able to call.
fireEvent.change(selectOptions[0], { target: { value: "0" } });
And this will work however if i then chain them together as in the following example:
test("Should render add another button and function", () => {
const mockSetOpen = jest.fn();
const { queryAllByTestId, getByTestId, getByLabelText, debug } = renderWithRedux(
<TabByRestraints setOpen={mockSetOpen} hidden={false} />,
{
userCatagories: userCategoriesMock,
permissionGroups: permissionGroupsMock,
roles: rolesMock
}
);
// Check that the user catagories exist and user catagories values
const selectOptions = queryAllByTestId("select");
expect(selectOptions).toHaveLength(2);
expect(getByTestId("user-category-row")).toBeTruthy();
fireEvent.change(selectOptions[0], { target: { value: "0" } });
fireEvent.change(selectOptions[1], { target: { value: "132" } });
expect(getByLabelText("Add a new user category restraint")).toBeTruthy();
});
I can see that the console.log("called") is only fired once. The second select on the page is never has its value selected therfore the test fails.
I would like both change to dispatch a change event.
fireEvent.change(selectOptions[0], { target: { value: "0" } });
fireEvent.change(selectOptions[1], { target: { value: "132" } });
However it only ever sets of the first.
Any help greatly appeciated.
You should do another query for the select elements. I would go further and name them differently to make the test easier to read/debug.
// it's easier to have separate ids for each select, will be easier to read/maintain the test in the future
const firstSelect = queryByTestId("select1");
fireEvent.change(firstSelect, { target: { value: "0" } });
// a rerender might have happened so you need to query for the second select after the first event.
const secondSelect = queryByTestId("select2");
fireEvent.change(secondSelect, { target: { value: "132" } });
expect(getByLabelText("Add a new user category restraint")).toBeTruthy();
The fact that render
works but renderWithRedux
doesn't suggests the problem lies in the redux state management.
One thing to consider -- setting state queues a rerender within React, while dispatching an action to Redux queues the change in Redux's own update queue. React testing tools may not know how to deal with that.
Although it's closer to the code, you may want to consider mocking dispatch
and verifying that it is receiving the expected results of the select changes. That way you can validate that your code is working, without worrying about what third party code is doing. (Kind of ironic that you were trying to avoid using react-select when renderWithRedux was the actual third-party problem. :D )
You may also want to consider directly calling your onChange handler, plucking it from the selects in your component instead of firing the events. This would be another way to reduce the amount of third-party code you are testing and mocking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With