Reputation: 391
I am trying to test a specific functionality that's defined inside onChange handler of AsyncTypeahead component from react-bootstrap-typeahead library. But onChange handler and selected property are not triggered at all. They are triggered when I type something on UI and select one option from typeahead dropdown. But from tests, both onChange and selected are not triggered. Here's the code for your reference:
Search.jsx
const getSelectedObject = value => {
if (value) {
return {name: value};
}
return value;
};
const Search = props => {
const client = useApolloClient();
const [options, setOptions] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const onSearch = async searchInput => {
props.clear();
if (!searchInput) return;
setIsLoading(true);
try {
const variables = {
fields: props.fields,
value: searchInput,
};
const res = await client.query({
query: props.query,
variables,
});
const newOptions = res ? res.data: [];
setOptions(newOptions);
} catch (err) {
console.error(err);
} finally {
setIsLoading(false);
}
};
const searchValue = debounce(onSearch, 2000);
return (
<th style={props.style || {minWidth: "100px"}} id={props.id}>
<label style={{visibility: "hidden"}} htmlFor={`${props.id}_input`}>
{props.id}
</label>
<AsyncTypeahead
inputProps={{name: props.name, id: `${props.id}_input`}}
labelKey="name"
id="fields"
placeholder={props.placeholder || "Search"}
onChange={(value) => console.log("onchange trigger", value)}
selected={(val) => getSelectedObject(val)}
isLoading={isLoading}
onSearch={searchValue}
options={options}
/>
</th>
);
};
Search.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
selected: PropTypes.object,
placeholder: PropTypes.string,
accountRole: PropTypes.string.isRequired,
fieldNames: PropTypes.array.isRequired,
};
Search.defaultProps = {
selected: undefined,
placeholder: "Search",
};
export default Search;
Search.test.jsx
test.only("selecting an option from search result SHOULD call ONCHANGE handler", async () => {
render(<ConversationSearch {...props} />);
// When
userEvent.type(screen.getByRole("combobox"), "to");
// Then
expect(screen.getByRole("combobox").getAttribute("value")).toBe("to");
await waitFor(() =>
expect(screen.getByRole("listbox")).toBeInTheDocument(),
);
expect(
screen.getByRole("link", {name: "Type to search..."}),
).toBeDefined();
await waitFor(() =>
expect(
screen.getByRole("link", {name: "Searching..."}),
).toBeInTheDocument(),
);
await waitFor(() =>
expect(
screen.getByRole("option", {name: "option1"}),
).toBeInTheDocument(),
);
expect(
screen.getByRole("option", {name: "option2"}),
).toBeInTheDocument();
expect(screen.getByLabelText("option1")).toBeVisible();
fireEvent.change(screen.getByRole("combobox"), {target: {value: "option1"}});
// expect(screen.getByRole("combobox").getAttribute("value")).toBe("option1");
});
I have modified the parts of the code to keep the focus more on component rather than business context.
Upvotes: 2
Views: 885
Reputation: 3509
It's a little unclear what functionality you're trying to test, but it sounds like you just want to assert that onChange
is called when an option is selected. Note that the library already tests this, but if you insist on confirming that, go ahead.
Based on the code in Search.jsx
, it doesn't look like onChange
is being passed on to the typeahead, so be sure to do that. Something like this should work (I haven't actually tested):
test.only("selecting an option from search result SHOULD call ONCHANGE handler", async () => {
// Pass a spy to the `onChange` handler.
const onChange = jest.fn();
render(<ConversationSearch {...props} onChange={onChange} />);
userEvent.type(screen.getByRole("combobox"), "to");
...
const option = await screen.findByRole("option", { name: "option1" });
userEvent.click(option);
// `onChange` should be called after a selection.
await waitFor(() => {
expect(onChange).toHaveBeenCalledTimes(1);
});
});
Upvotes: 1