The react-select library is great for creating all sorts of select and autosuggest components. However, it’s actually rather complex under the hood and can be a little tricky to test via Rails Capybara tests. Here I present a Poltergeist-specific solution to the problem.
There are two complicating factors:
- it’s difficult to get the select component to respond to your input
- the web driver insists that there is actually an overlapping
<div>
obscuring the input (if Poltergeist detects that you are trying to send keys to something that is obscured by an overlapping element, it will fail)
This solution employs Poltergeist’s native.send_keys
in combination with setting the z-index of the overlapping <div>
to overcome these obstacles.
I created a tiny helper that is reusable for any tests involving interacting with a react-select component. I put my team’s helper in a feature helpers file that we automatically include during our feature tests.
feature_helpers.rb
def search_react_select_for(text)
find(".Select").trigger("click")
fix_overlap = %{ $('.Select-placeholder').css('z-index', -99999) }
page.execute_script(fix_overlap)
find(".Select .Select-input input").native.send_keys(text)
end
def click_autosuggest_option(node_type, text)
find(node_type, text: text, visible: :all, match: :first).click
end
An easy way to get the node_type
in the click_autosuggest_option
helper is to just customize your react-select renderer so that it adds a custom class or id for you to reference in your tests. For example:
SelectUser.jsx
const optionRenderer = (option) => (
<div id={option.id} className="user-option">
{option.firstName}
</div>
);
//...
return <Select optionRenderer={optionRenderer} />;
Now you can write Capybara tests like this:
select_user_spec.rb
scenario "user searches for john", :js do
user = create(:user, first_name: "john")
search_react_select_for("john")
click_autosuggest_option(".user-option", "john") # here we're matching using the class name
# expectation goes here
end
Because Capybara auto-waits when you call find
(and have the :js
metatag), asynchronous calls are no problem. Just make sure to specify :js
meta tag for your tests. I hope this helps!