Material-ui: [Autocomplete] onChange does not fire in tests (react-testing-library)

Created on 7 Nov 2019  路  10Comments  路  Source: mui-org/material-ui


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)
  })
})
  • [x] The issue is present in the latest release.
  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 馃槸

expect(handleChange).toHaveBeenCalledTimes(1) fails

Expected Behavior 馃

expect(handleChange).toHaveBeenCalledTimes(1) passes

Steps to Reproduce 馃暪

Steps:

  1. Run test

Context 馃敠

Your Environment 馃寧

| 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 |

Autocomplete question test

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:


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)

All 10 comments

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')
  })

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 ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

revskill10 picture revskill10  路  3Comments

ghost picture ghost  路  3Comments

sys13 picture sys13  路  3Comments

newoga picture newoga  路  3Comments

chris-hinds picture chris-hinds  路  3Comments