I'm currenty trying to mock the react-select ponent 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.
I'm currenty trying to mock the react-select ponent 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.
Share Improve this question edited Feb 18, 2020 at 11:51 skyboyer 23.8k7 gold badges62 silver badges71 bronze badges asked Feb 18, 2020 at 10:40 MorphasisMorphasis 1,4331 gold badge13 silver badges27 bronze badges 6-
Are you sure there is an option with value
132
for the second select rendered at the time whenfireEvent.change
executes? – Christos Lytras Commented Feb 21, 2020 at 15:26 - Yes because i used debug() and i can see the options rendering correcty with there values. I also tested using the same select again (selectOptions[0]) but instead kust changing its value again same issue persists sadly. – Morphasis Commented Feb 21, 2020 at 15:45
-
I am very curious to why this isn't working, so I created a simple React app using
react-select
to render 2 select inputs with such values. It's working fine for me. Here is the screenshot. Of course I haven't implemented any state management with redux which may be the culprit, but I am not sure. The only difference is that instead ofrenderWithRedux
I'm usingrender
from@testing-library/react
. I can upload that to a repository if you're interesting. – Christos Lytras Commented Feb 21, 2020 at 17:03 - Please do its strange that the state change would cause this. It can probbably be resovled then with some kind of timeout or promise structure. – Morphasis Commented Feb 21, 2020 at 17:09
-
Here you can find the repo. If it doesn't run the tests when you run
yarn test
then just hita
to run all tests. Let me know if you have any trouble running the test and see the results. – Christos Lytras Commented Feb 21, 2020 at 17:24
2 Answers
Reset to default 4 +25You 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 ponent instead of firing the events. This would be another way to reduce the amount of third-party code you are testing and mocking.