When testing react-select inputs with @testing-library/react, it's not possible to programatically open the select. This is with the latest version of react-select.
I created a reproduction repo. I pasted the relevant code below to make it easier to follow
//App.js
import React, { Component } from "react";
import Select from "react-select";
import "./App.css";
const fruits = [
{ label: "Papaya", value: "papaya" },
{ label: "Apple", value: "apple" }
];
class App extends Component {
render() {
return (
<div className="App">
<label htmlFor="selectInput">Select a fruit</label>
<Select
autoFocus={this.props.autoFocus}
id="selectInput"
options={fruits}
/>
</div>
);
}
}
App.defaultProps = {
autoFocus: false
};
export default App;
// App.test.js
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import App from "./App";
describe("select input", () => {
describe("without autofocus", () => {
it("should be able to open", () => {
const { getByLabelText, getByText, debug } = render(<App />);
const input = getByLabelText("Select a fruit");
input.focus();
fireEvent.keyDown(input, { key: "ArrowDown" });
expect(getByText("Papaya")).toBeInTheDocument();
});
});
describe("with autofocus", () => {
it("should be able to open", () => {
const { getByLabelText, getByText, debug } = render(
<App autoFocus={true} />
);
const input = getByLabelText("Select a fruit");
input.focus();
fireEvent.keyDown(input, { key: "ArrowDown" });
expect(getByText("Papaya")).toBeInTheDocument();
});
});
});
When I run the test, this is the result
FAIL src/App.test.js
select input
without autofocus
โ should be able to open (135ms)
with autofocus
โ should be able to open (59ms)
โ select input โบ with autofocus โบ should be able to open
Unable to find an element with the text: Papaya. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
<body>
<div>
<div
class="App"
>
<label
for="selectInput"
>
Select a fruit
</label>
<div
class=" css-2b097c-container"
id="selectInput"
>
<span
aria-live="polite"
class="css-1laao21-a11yText"
>
<p
id="aria-selection-event"
>
</p>
<p
id="aria-context"
>
0 results available. Select is focused ,type to refine list, press Down to open the menu,
</p>
</span>
<div
class=" css-1pahdxg-control"
>
<div
class=" css-1hwfws3"
>
<div
class=" css-1wa3eu0-placeholder"
>
Select...
</div>
<div
class="css-1g6gooi"
>
<div
class=""
style="display: inline-block;"
>
<input
aria-autocomplete="list"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-3-input"
spellcheck="false"
style="box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
<div
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
/>
</div>
</div>
</div>
<div
class=" css-1wy0on6"
>
<span
class=" css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class=" css-1gtu0rj-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-19bqh2r"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
23 | input.focus();
24 | fireEvent.keyDown(input, { key: "ArrowDown" });
> 25 | expect(getByText("Papaya")).toBeInTheDocument();
| ^
26 | });
27 | });
28 | });
at getElementError (node_modules/@testing-library/dom/dist/query-helpers.js:46:10)
at args (node_modules/@testing-library/dom/dist/query-helpers.js:100:13)
at args (node_modules/@testing-library/dom/dist/query-helpers.js:83:17)
at Object.getByText (src/App.test.js:25:14)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 1.91s
Ran all test suites.
I have same issue. tried to use fireEvent.mouseDown() or userEvent.click(). any solutions there? and I didn't use autoFocus prop.
Finally, I resolve this issue. there is two points: 1. You should simulate keydown event for display dropdown. 2. Dropdown renders menu asynchronously.
it('should display dropdown menu and call onSortChange when option clicked', async () => {
const { getByText, onSortChange, debug } = setup();
// (1) I tried to simulate mouse event but not working. this only is effective solution.
fireEvent.keyDown(document.querySelector('.list__control'), { key: 'ArrowDown', keyCode: 40 });
// (2) Dropdown renders menu asynchronosly. you have to wait for element to find by text
await waitForElement(() => getByText('Option 1'));
fireEvent.click(getByText('Option 1'));
expect(onSortChange).toHaveBeenCalledTimes(1);
expect(onSortChange).toHaveBeenLastCalledWith(['code', 'ASC']);
});
And don't forget use classNamePrefix props for write test easy.
<ReactSelect
classNamePrefix="list"
noOptionsMessage={noOptionsMessage}
isSearchable={false}
isClearable={false}
components={selectComponents}
{...props}
/>

@iamchanii are you using the autoFocus prop? I tried your solution and wasn't able to get it to work when it's set to true. Without that prop I can interact with ReactSelect with RTL with no issues ๐ข
@montezume No, but I wish it is help for you.


as you can see, I added fireEvent.blur() line before simulate keydown and it's work. but I don't think it is not RTL spirit:
The more your tests resemble the way your software is used, the more confidence they can give you.
@iamchanii thanks for your help!
I figured out the source of the problem.
When I did
const input = getByLabelText('Select a fruit');
fireEvent.blur(input);
the input that I was grabbing was not actually an input. It's a div.

So calling blur on that div doesn't do what we need.
So instead, I do
describe("with autofocus", () => {
it.only("should be able to open", async () => {
const { getByLabelText, getByText, debug } = render(
<App autoFocus={true} />
);
const input = getByLabelText("Select a fruit");
fireEvent.blur(document.querySelector("input"));
fireEvent.keyDown(input, {
key: "ArrowDown",
keyCode: 40
});
await waitForElement(() => getByText("Papaya"));
fireEvent.click(getByText("Papaya"));
expect(getByText("Papaya")).toBeInTheDocument();
});
});
and it works, because now the blur is being called on the actual input.
Thanks for your help! ๐
I was even able to remove the async
describe("with autofocus", () => {
it.only("should be able to open", () => {
const { getByLabelText, getByText } = render(<App autoFocus={true} />);
const containerDiv = getByLabelText("Select a fruit");
const input = containerDiv.querySelector("input");
fireEvent.blur(input);
fireEvent.keyDown(input, {
key: "ArrowDown",
keyCode: 40
});
expect(getByText("Papaya")).toBeInTheDocument();
fireEvent.click(getByText("Papaya"));
expect(getByText("Papaya")).toBeInTheDocument();
});
});
I'm glad to help you! ๐
I think I can close this now.
Thanks!
I was facing the same issue with autoFocus props in unit test cases.
When used fireEvent.blur(input); all test cases were working.
Most helpful comment
@iamchanii thanks for your help!
I figured out the source of the problem.
When I did
the
inputthat I was grabbing was not actually aninput. It's a div.So calling blur on that div doesn't do what we need.
So instead, I do
and it works, because now the blur is being called on the actual input.
Thanks for your help! ๐
I was even able to remove the async