Material-ui: Enzyme Simulated Select

Created on 27 Sep 2016  路  11Comments  路  Source: mui-org/material-ui

Simulating change on SelectField component using enzyme works but the value of select field is undefined.

//returned element
< div>
< SelectField
value={this.state.selectId}
onChange={this.handleChange}
>
{list.map(
object = > < MenuItem key={object._id} value={object._id} primaryText={object.value} />
)}

handleChange(event, index, value){
console.log(event, index, value)
this.setState({selectId: value})
}

const testTarget = shallow( );

it('should select.',
() => {
testTarget.find('SelectField').simulate('change', { target: { value: 'idvalue' } });
}
);

console produces
[event] Object, [index] 0, [value] undefined

However when select component is used normally it works fine.

  • Material-UI: 0.15.4
  • React: 15.3.2
    -Enzyme: 2.4.1
  • Browser: mozila 49.0

Most helpful comment

@hboylan I found looking at material-ui's own tests to be helpful. For Select:

https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Select/Select.test.js#L72

            wrapper.find('[role="button"]').simulate('click');
            wrapper
                .find(MenuItem)
                .at(3)
                .simulate('click');

successfully causes onChange to fire from a Select for me.

All 11 comments

Got it to work with enzyme simulate 'change' by modifying
Works for testing and normal selects
handleChange( event, index, value){
this.setState({selectId: value||event.target.value})
}

@pricetula can you specify what you modified to get it working, my code is the same as yours and the mocked method is not being called.

@mcohoon the change i made is in my component's handleChange method

handleChange(event, index, value){
  console.log(event, index, value)
  this.setState({selectId: value})
}

changed to

handleChange( event, index, value){
  this.setState({selectId: value||event.target.value})
}

value parameter is undefined when in testing so i normaly have to get it from event.target.value

I dont have the repo right now but i remember running into this issue a couple of times

@pricetula You explain how to handle the right value when the method is called.
Our methods are not being called.

This works for me

import Select from "material-ui/Select"

...

const wrapper = shallow(<MyForm />);

const select = wrapper.find(Select);
expect(select).toHaveLength(1);

select.simulate('change', {target: {value: '1'}});

Alas, this doesn't work for me. 馃槶

// RegionSelect.js
state = {
    region: '',
    regions: ['West', 'Central', 'East']
}

handleChange = event => {
    console.log(event.target.value) // not triggered
    this.setState({ region: event.target.value })
}

render() {
    const { className } = this.props
    const { region, regions } = this.state
    return (
        <FormControl className={className}>
            <InputLabel htmlFor="region">Region</InputLabel>
            <Select
                value={region}
                onChange={this.handleChange.bind(this)}
                input={<Input id="region" />}
            >
                {regions.map((r, i) => (
                    <MenuItem key={i} value={r}>{r}</MenuItem>
                ))}
            </Select>
        </FormControl>
    )
}
// RegionSelect.test.js
test('handle submit', () => {
    const root = mount(<RegionSelect />)
    root.find('Select').simulate('change', { target: { value: 'East' } })
    console.log(root.state('region')) // not set
})

@hboylan please try changing your handleChange method in your component to pass in multiple parameters

handleChange = event => {...}

to

handleChange = (event, index, value) => {...} // value || event.target.value 

This is because maybe while test it uses event object and value is used when not in test enviroment

@pricetula Thanks for the suggestion!

Unfortunately, the onChange method isn't triggered at all. I even tried bypassing handleChange:

onChange={console.log}

(btw I'm on Jest v20.0.4)

@hboylan I found looking at material-ui's own tests to be helpful. For Select:

https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Select/Select.test.js#L72

            wrapper.find('[role="button"]').simulate('click');
            wrapper
                .find(MenuItem)
                .at(3)
                .simulate('click');

successfully causes onChange to fire from a Select for me.

Thanks @city41 !

Although in hindsight this might look quite obvious, I believe this should be added somewhere in the docs for quick lookup.

Edit: My only observation is that although the method mentioned above does trigger the change event, the Menu is not collapsed afterwards as I would expect it to.

This code worked for me:

Component:

import React, { ReactElement, useCallback } from 'react';
import { FormControl, InputLabel, Select, MenuItem, FormHelperText } from '@material-ui/core';
import { useFormikContext, useField } from 'formik';

interface Props {
  id: string;
  label: string;
  name: string;
  data: Array<{ value: any; text: string }>;
  placeholder?: string;
  className?: any;
}

export default function GenericSelectField(props: Props): ReactElement {
  const [field, meta] = useField(props);
  const { setFieldValue } = useFormikContext();

  const handleChange = useCallback(
    event => {
      setFieldValue(props.name, event.target.value);
    },
    [props.name, setFieldValue]
  );

  return (
    <FormControl
      fullWidth={true}
      error={meta.error && meta.touched ? true : false}
      className={props.className}
    >
      <InputLabel htmlFor={props.id}>{props.label}</InputLabel>
      <Select
        id={props.id}
        value={field.value}
        onChange={handleChange}
        inputProps={{ name: field.name, id: `${props.id}-input` }}
        placeholder={props.placeholder}
      >
        {props.data.map((data, itr) => (
          <MenuItem key={itr} value={data.value} id={`menu-item-${itr}`}>
            {data.text}
          </MenuItem>
        ))}
      </Select>
      <FormHelperText id={`${props.id}-helper-text`}>
        {meta.error && meta.touched ? meta.error : ''}
      </FormHelperText>
    </FormControl>
  );
}

Test:

import React from 'react';
import { mount } from 'enzyme';
import { Select } from '@material-ui/core';

describe('Select test', () => {

  fit('should show form validation errors', async () => {
    await act(async () => {
      const initialValues = { testSelect: '' };

     const validationSchema = Yup.object().shape({
       Yup.string().required('This field is required');
     });

      const wrapper = mount(
        <Formik initialValues={initialValues} validationSchema={validationSchema} >
          <Form>
            <GenericSelectField
              id='test-select'
              name='testSelect'
              label='My Test'
              data={[
                { value: 1, text: 'Option 1' },
                { value: 2, text: 'Option 2' },
              ]}
            />
         </Form>
        </Formik>
      );

      expect(wrapper).toBeTruthy();

      await wait(0);
      wrapper.update();

      wrapper.find('form').simulate('submit');
      await wait(0);
      wrapper.update();

      expect(wrapper.find('p[id="test-select-helper-text"]').text()).toBe(
        'Esse campo 茅 obrigat贸rio'
      );

      const changeTarget: any = {
        target: { name: 'testSelect', value: 1 },
      };
      wrapper.find(Select).props().onChange!(changeTarget, null);

      await wait(0);
      wrapper.update();

      expect(wrapper.find('p[id="test-select-helper-text"]').text()).toBe('');
    });
  });
Was this page helpful?
0 / 5 - 0 ratings