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.
It's a little unclear what functionality you're trying to test, but it sounds like you just want to assert that
onChangeis 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 likeonChangeis being passed on to the typeahead, so be sure to do that. Something like this should work (I haven't actually tested):