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.