14

I built up a component with React and Material-UI. I'm using React and Redux.

my index.jsx looks like this:

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import configureStore from '../store/configureStore';
import Routes from '../routes/routes';
import '../styles/main.less';

const store = configureStore();
render(
  <Provider store={store}>
    <MuiThemeProvider>
      <Routes />
    </MuiThemeProvider>
  </Provider>,
  document.getElementById('app'),
);

My component InputSearch looks like this:

import React, { PropTypes, Component } from 'react';
import TextField from 'material-ui/TextField';

class InputSearch extends Component {
  ...

  render() {
    return (
      ...
      <TextField
        defaultValue={this.props.keyword}
        ref={(input) => { this.input = input; }}
        autoFocus
        hintText='Type a keyword'
        errorText={this.state.errorText}
        floatingLabelText='Search for keyword'
        style={styles.textField}
      />
    );
  }
}

InputSearch.propTypes = {
  keyword: PropTypes.string.isRequired,
  resetSearch: PropTypes.func.isRequired,
  searchBooks: PropTypes.func.isRequired,
  toggleResultsOpacity: PropTypes.func.isRequired,
  firstSearch: PropTypes.bool.isRequired,
};

export default InputSearch;

I'm using Airbnb Enzyme and Jest to build unit tests. My test to the InputSearch component looks like this:

import React from 'react';
import { shallow, mount } from 'enzyme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import TextField from 'material-ui/TextField';
import InputSearch from '../components/InputSearch/InputSearch';

const resetSearchMock = jest.fn();
const searchBooksMock = jest.fn();
const toggleResultsOpacityMock = jest.fn();

const setup = () => {
  const props = {
    keyword: '',
    resetSearch: resetSearchMock,
    searchBooks: searchBooksMock,
    toggleResultsOpacity: toggleResultsOpacityMock,
    firstSearch: true,
  };

  const wrapper = shallow(<MuiThemeProvider><InputSearch {...props} /></MuiThemeProvider>);

  return {
    props,
    wrapper,
  };
};

describe('Initial test', () => {
  test('Shows error message when input search is empty.', () => {
    const { wrapper, props } = setup();
    expect(wrapper.find(TextField).getValue()).toEqual('');
  });
}

However, I'm getting the following error:

TypeError: wrapper.find(...).getValue is not a function

Can anyone help me reach the right way to check the value of the Material UI TextField, please?

4 Answers 4

4

I have been writing test for few days using mocha, enzyme and chai. The problem that comes with testing material-ui is these are inturn react component so they cannot be tested as you test regular html elements.

You can find out what property change by printing the whole component, like

console.log(wrapper.find('TextField').debug());

This will print the whole element for you, you can notice that the TestField has value prop which is what you are suppose to check because this prop is what decided the value in the TextField

So the code will go like this:

describe('Initial test', () => {
  test('Shows error message when input search is empty.', () => {
    const { wrapper, props } = setup();
    expect(wrapper.find(TextField).props().value).to.equal('');
  });
}

This is how I have been testing the TextField component.

Hope you find it helpful.

Sign up to request clarification or add additional context in comments.

2 Comments

Hi @Farhaan Bukhsh, it is a good tip the "debug" function. However, my TextField component has not a "value" property, it only has a "defaultValue" property. How can I simulate a "change" event passing a new value and test it?
Hey , you can replace the value to defaultValue if that is the prop you are having. Also I will suggest you to update material-ui. Now to simulate a change event it is very straight forward. wrapper.find('TextField') .find('input') .simulate('change', { target: { value: "Farhaan" } } ) , this will put Farhaan in the TextField now you can easily check the value.
3

Please, if someone has a better solution answer my question. After some attempts, I figured out how to test some material UI components. Basically, we need to find the native html elements (input, button, etc) inside the material UI components via enzyme find. I also realized that "shallow", only do a one level deep search, as @Andreas Köberle said in comments below. To force a deep search in the DOM tree we need to use enzyme "mount". http://airbnb.io/enzyme/docs/api/ReactWrapper/mount.html

Here is my new test code.

import React from 'react';
import { shallow, mount } from 'enzyme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { search } from '../sagas/search';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import Toggle from 'material-ui/Toggle';
import InputSearch from '../components/InputSearch/InputSearch';


const resetSearchMock = jest.fn();
const searchBooksMock = jest.fn();
const toggleResultsOpacityMock = jest.fn();
const muiTheme = getMuiTheme();
const props = {
  keyword: '',
  resetSearch: resetSearchMock,
  searchBooks: searchBooksMock,
  toggleResultsOpacity: toggleResultsOpacityMock,
  firstSearch: true,
};

const setup = () => {
  const wrapper = mount(
    <InputSearch {...props} />,
    {
      context: {muiTheme},
      childContextTypes: {muiTheme: React.PropTypes.object}
    }
  );

  return {
    props,
    wrapper,
  };
};

const { wrapper } = setup();
const textFieldMUI = wrapper.find(TextField);
const toggleAuthor = wrapper.find(Toggle).find('input#author');
const toggleTitle = wrapper.find(Toggle).find('input#title');
const button = wrapper.find(RaisedButton).find('button');

describe ('Initial test, validate fields', () => {  
  test('TextField component should exists.', () => {
    expect(textFieldMUI).toBeDefined();
  });

  test('Shows an error message when input search is empty and the search button is clicked.', () => {
    const { props } = setup();
    props.keyword = '';

    const wrapper = mount(
      <InputSearch {...props} />,
      {
        context: {muiTheme},
        childContextTypes: {muiTheme: React.PropTypes.object}
      }
    );

    button.simulate('click');
    expect(textFieldMUI.props().errorText).toEqual('This field is required');
  });

  test('Shows an error message when both "author" and "title" toggles are off and the search button is clicked.', () => {
    toggleTitle.simulate('click');
    button.simulate('click');
    expect(textFieldMUI.props().errorText).toEqual('Select at least one filter (Title or Author)');
  });

});

Comments

1

Enzyme shallow renders only one level deep, so in your case only MuiThemeProvider and InputSearch are rendered. You can use Jest snapshot feature to see what was rendered inside wrapper. You can use dive to force Enzyme to render the content of a component:

expect(wrapper.('InputSearch').dive().find(TextField).getValue()).toEqual('');

or you dont wrap the component with MuiThemeProvider and render InputSearch directly. You only need to add the styles prop. Now InputSearch is the top level component and Enzyme will render its content.

const setup = () => {
  const props = {
    keyword: '',
    resetSearch: resetSearchMock,
    searchBooks: searchBooksMock,
    toggleResultsOpacity: toggleResultsOpacityMock,
    firstSearch: true,
    styles: {textfield: {fontSize:10}}
  };

  const wrapper = shallow(<InputSearch {...props} />);

  return {
    props,
    wrapper,
  };
};

3 Comments

Hi @Andreas Köberle, thanks for your answer. I knew that "shallow" renders only one level deep, however, I tried with enzyme "mount" as well and it not works. I tried your suggestion with "dive" but this not solve. I also tried removing the "MuiThemeProvider" but not works too. Can you please try code a sample with material ui and TextField? Appreciate your help.
Ok, so I cant find getValue in the enzyme docs. Maybe you are looking for prop('value') instead?
Hey @Andreas Köberle, "getValue" is a material UI built in method to get the "TextField" component value.
0
const event = { 'target': { 'value': 'No' } };
wrapper.find('#selectBox').prop('onChange').call(null, event);

2 Comments

The same tweak works for Material <select /> as well.
While this code may resolve the OP's issue, it is best to include an explanation as to how your code addresses the OP's issue. In this way, future visitors can learn from your post, and apply it to their own code. SO is not a coding service, but a resource for knowledge. Also, high quality, complete answers are more likely to be upvoted. These features, along with the requirement that all posts are self-contained, are some of the strengths of SO as a platform, that differentiates it from forums. You can edit to add additional info &/or to supplement your explanations with source documentation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.