10
votes

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

4 Answers

4
votes

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.

3
votes

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

});
1
votes

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,
  };
};
0
votes
const event = { 'target': { 'value': 'No' } };
wrapper.find('#selectBox').prop('onChange').call(null, event);