Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Autocomplete] onChange does not fire in tests (react-testing-library) #18251

Closed
2 tasks done
SladeRun14 opened this issue Nov 7, 2019 · 12 comments
Closed
2 tasks done
Labels
component: autocomplete This is the name of the generic UI component, not the React module! support: question Community support but can be turned into an improvement test

Comments

@SladeRun14
Copy link

SladeRun14 commented Nov 7, 2019

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)
  })
})
  • The issue is present in the latest release.
  • 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
@oliviertassinari oliviertassinari added the duplicate This issue or pull request already exists label Nov 7, 2019
@oliviertassinari
Copy link
Member

See #18113 (comment), onChange only fire when an option is selected.

@maximeantoine1997

This comment has been minimized.

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 8, 2020

@maximeantoine1997 We don't provide support here, please ask on StackOverflow. Alternatively, you can check our tests.

@emkographics
Copy link

emkographics commented Feb 13, 2020

@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 (jestjs/jest#9606), the workaround is no longer required.

@emkographics

This comment has been minimized.

@sruthisripathi

This comment has been minimized.

@oliviertassinari
Copy link
Member

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.

@dclark27
Copy link

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)

@oliviertassinari oliviertassinari added the component: autocomplete This is the name of the generic UI component, not the React module! label May 22, 2020
@oliviertassinari oliviertassinari added test support: question Community support but can be turned into an improvement and removed duplicate This issue or pull request already exists labels Jun 8, 2020
@oliviertassinari
Copy link
Member

oliviertassinari commented Jun 9, 2020

Update, since Jest v26.0.0 (jestjs/jest#9606), the workaround in #18251 (comment) is no longer required.

@hiahmedhameed
Copy link

hiahmedhameed commented Oct 27, 2020

I'm still not able to select autocomplete

Trying to do steps in #18251 (comment) 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 ?

@faisalscientist
Copy link

I'm still not able to select autocomplete

Trying to do steps in #18251 (comment) 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 ?

Have you found a way to test this yet?

@Aerophite
Copy link

I'm still not able to select autocomplete
Trying to do steps in #18251 (comment) 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 ?

Have you found a way to test this yet?

MUI now changes the text field to a "combobox" role so you have to do getByRole('combobox'). Not exactly sure when that happened but my company had to update tests a couple of months ago when we updated @mui/material to ^5.6.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: autocomplete This is the name of the generic UI component, not the React module! support: question Community support but can be turned into an improvement test
Projects
None yet
Development

No branches or pull requests

9 participants