While Autocomplete's onChange
event is called correctly during normal usage, when testing with the react-testing-library it is not. The following test fails:
import React from "react"
import { render, fireEvent, prettyDOM } from "@testing-library/react"
import Autocomplete from "@material-ui/lab/Autocomplete"
import TextField from "@material-ui/core/TextField"
describe("MUI Autocomplete", () => {
const handleChange = jest.fn()
it("Should call onChange()", () => {
const { container } = render(
<Autocomplete
onChange={handleChange}
options={[{ name: "one" }, { name: "two " }]}
getOptionLabel={option => option.name}
renderInput={params => <TextField {...params} />}
/>,
)
const input = container.querySelector("input")
fireEvent.change(input, { target: { value: "two" } })
console.log(" HTML: " + prettyDOM(input))
expect(input).toHaveValue("two")
expect(handleChange).toHaveBeenCalledTimes(1)
})
})
expect(handleChange).toHaveBeenCalledTimes(1)
fails
expect(handleChange).toHaveBeenCalledTimes(1)
passes
Steps:
| Tech | Version |
| ----------- | ------- |
| Material-UI/lab | v4.0.0-alpha.30 |
| React | v16.11.0 |
| testing-library/react | v9.3.2 |
| testing-library/jest-dom | v4.2.3 |
See https://github.com/mui-org/material-ui/issues/18113#issuecomment-549358852, onChange only fire when an option is selected.
@oliviertassinari I'm trying to test the same as @SladeRun14. My question is how do we select an option in the Autocomplete component using React-Testing-Library?
it("should trigger onChange", async () => {
const onChange = jest.fn();
const { getByTestId } = render(<DefinitionSelector onChange={onChange} />);
const autocomplete = getByTestId("selector");
expect(autocomplete).not.toBeNull();
fireEvent.change(autocomplete, { target: { inputValue: "Whatever" } });
expect(onChange).toHaveBeenCalled();
cleanup();
});
DefinitionSelector is a component using the Autocomplete Material UI component. When the inputValue changes to a known value (in this case to "Whatever"), props.onChange is triggered. It works fine when used with other components.
I just want to select an item (instead of changing the inputvalue), but I can't find a way to this with Material UI
@maximeantoine1997 We don't provide support here, please ask on StackOverflow. Alternatively, you can check our tests.
@SladeRun14 We ran into the same issue recently and ended up doing the following, create setupTests.js in your root project and add:
document.createRange = () => ({
setStart: () => {},
setEnd: () => {},
commonAncestorContainer: {
nodeName: "BODY",
ownerDocument: document,
},
})
Update: since Jest v26.0.0 (https://github.com/facebook/jest/pull/9606), the workaround is no longer required.
@maximeantoine1997 see my previous comment and see if that help you. Also, i haven't used inputValue in my change fireEvent so you may want to try with value instead.
You can use ListboxProps
to give test id to autocomplete listbox
and then from there you can select menuitem using getByText
it("Should call onChange()", () => {
const { container } = render(
<Autocomplete
onChange={handleChange}
options={[{ name: "one" }, { name: "two " }]}
getOptionLabel={option => option.name}
renderInput={params => <TextField {...params} />}
ListboxProps={{ 'data-testid': list-box' }}
/>,
)
const input = getByRole(container, "textbox")
fireEvent.click(input)
const listBox = getByTestId('list-box')
const menuItem = getByText(listBox, 'one')
fireEvent.click(menuItem)
expect(input.value).toBe('one')
})
You can refer to the tests of the component to learn the idiomatic way to test it: https://github.com/mui-org/material-ui/blob/32d647cbf72fb3661638d88c428ba37b804b6f15/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js.
Thanks @oliviertassinari
To all the googlers who get here and don't want to dig around, I was able to execute tests by following examples above and doing this:
const autocomplete = getByRole('textbox')
// click into the component
autocomplete.focus()
// type "a"
fireEvent.change(document.activeElement, { target: { value: 'a' } })
// arrow down to first option
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' })
// select element
fireEvent.keyDown(document.activeElement, { key: 'Enter' })
expect(autocomplete.value).toEqual('Arkansas')
expect(someChangeHandler).toHaveBeenCalledTimes(1)
Update, since Jest v26.0.0 (https://github.com/facebook/jest/pull/9606), the workaround in https://github.com/mui-org/material-ui/issues/18251#issuecomment-585864937 is no longer required.
I'm still not able to select autocomplete
Trying to do steps in https://github.com/mui-org/material-ui/issues/18251#issuecomment-632825845 but can't see any result from getByRole('textbox')
I tired to add data-testid="autocomplete"
but also that does not work!
This is my autocomplete component
import React from 'react';
import styled, {createGlobalStyle} from 'styled-components';
import Autocomplete from '@material-ui/lab/Autocomplete';
import {FormControl, TextField} from './Forms';
const StyledAutocomplete = styled(Autocomplete)`
& .MuiInputBase-root {
height: 4.1rem;
}
& label: {
font-size: 2rem;
}
`;
const StyleAutoCompleteOptions = createGlobalStyle`
.MuiAutocomplete-popper div {
font-size: 1.6rem;
}
`;
export interface AutocompleteOptionData {
value: string;
label: string;
}
interface AutocompleteFieldProps {
currentValue: AutocompleteOptionData | null;
error?: string | React.ReactElement;
id: string;
label: string;
name: string;
options: AutocompleteOptionData[];
onChange?: (value: AutocompleteOptionData) => void;
className?: string;
}
const AutocompleteField: React.FC<AutocompleteFieldProps> = ({
id,
label,
name,
options,
currentValue,
error,
onChange,
className,
}) => {
console.log('currentValue', currentValue);
return (
<FormControl htmlFor={id} error={error}>
<StyleAutoCompleteOptions />
<StyledAutocomplete
id={id}
options={options}
data-testid="autocomplete"
onChange={(_event: React.ChangeEvent<{}>, value) =>
onChange &&
onChange(
(value as AutocompleteOptionData | null) || {
label: '',
value: '',
}
)
}
value={currentValue}
getOptionLabel={data => (data as AutocompleteOptionData).label}
getOptionSelected={option =>
(option as AutocompleteOptionData).label === currentValue?.label
}
className={className}
renderInput={params => (
<TextField
{...params}
error={!!error}
name={name}
label={label}
variant="standard"
data-testid="select-input-field"
/>
)}
/>
</FormControl>
);
};
export default AutocompleteField;
Any idea how to test it ?
Most helpful comment
Thanks @oliviertassinari
To all the googlers who get here and don't want to dig around, I was able to execute tests by following examples above and doing this: