I have the following code to test the value of the Select element after changing:
it("changes value after selecting another field", () => {
doSetupWork();
let field = screen.getByLabelText("MySelectField");
expect(field).toHaveValue("");
fireEvent.change(field, { target: { value: "1" } });
// Insert one of two options from below
});
However, when I insert the following at the bottom, it does not work:
field = screen.getByLabelText("MySelectField");
expect(field).toHaveValue("1");
and gives the following error message:
Expected the element to have value: 1
Received:
But, when I wrap it in a setTimeout
with just 1ms delay, it does work:
setTimeout(() => {
field = screen.getByLabelText("MySelectField");
expect(field).toHaveValue("1");
}, 1);
It feels like there should be a more elegant way of writing this without setTimeout
. Any advice?
CodePudding user response:
You could try using waitFor
instead of setTimeout
:
import {waitFor} from '@testing-library/react'
...
await waitFor(() => screen.getByLabelText("MySelectField").toHaveValue("1"))
CodePudding user response:
What you're seeing is that it takes a finite amount of time for your app to react (excuse the pun) to the change that has occurred - specifically, it needs to re-render.
And yes, there is a nicer way - the waitFor
function from testing-library/react
:
import { screen, waitFor } from '@testing-library/react';
...
it("changes value after selecting another field", async () => {
...
fireEvent.change(field, { target: { value: "1" } });
await waitFor(async () => {
field = screen.getByLabelText("MySelectField");
expect(field).toHaveValue("1");
});
}
Note that the entire test body (i.e. after the test's name) has to be declared async
in order to be able to await
the new waitFor
block.
CodePudding user response:
When I am using react-testing-library I tend to use render when I have events to interact with. For instance:
In my App.js I have this code on the return method
const handleChoice = () => {};
const attributes = [
{ label: "One", value: "1" },
{ label: "Two", value: "2" },
{ label: "Three", value: "3" }
];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<select onChange={handleChoice} data-testid="MySelectField">
<option value="0">Zero</option>
{attributes.map((item) => {
return (
<option key={item.value} value={item.value}>
{item.label}
</option>
);
})}
</select>
</div>
);
then my test would be something like this:
import { fireEvent, render } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";
it("changes value after selecting another field", () => {
const { getByTestId } = render(<App />);
let field = getByTestId("MySelectField");
expect(field).toHaveValue("0");
fireEvent.change(field, { target: { value: "1" } });
expect(field.value).toBe("1");
fireEvent.change(field, { target: { value: "3" } });
expect(field.value).toBe("3");
// Insert one of two options from below
});
Take a look in this sandbox to see it working. https://codesandbox.io/s/ecstatic-https-hzi5n?file=/src/App.spec.js