Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React, how to simulate user input for unit testing?

I have been trying to unit test a react component that takes in user input. More specifically I'm trying to test the onChange function within the react component. However I can't seem to set the input value, I've tried a few different ways suggested on the internet and none seem to work. Below is the component I'm trying to test.

class Input extends Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    /* Check if max length has been set. If max length has been
    set make sure the user input is less than max Length, otherwise
    return before updating the text string. */
    if(this.props.maxLength) {
      if(event.target.value.length > this.props.maxLength) {
        return;
      }
    }
    this.setState({ value: event.target.value });
  }

  render () {
    const { disabled, label, maxLength, multiline, type, value, ...others} = this.props;
    const theme = themeable(others.theme);

    let inputClassName = classNames({
      "input": type !== 'checkbox',
      "checkbox": type == 'checkbox',
      disabled,
      multiline,
      value,
      [`${this.props.className}`]: !!this.props.className
    });

    return (
      <div {...theme(1, 'container')}>
        {this.props.label ? <label htmlFor={this.props.htmlFor} {...theme(2, 'label')}>{label}</label> : null}
          <input value={this.state.value} {...theme(3, ...inputClassName)} onChange={this.handleChange} type={type} />
      </div>
    );
  }
}

I found this issue: https://github.com/airbnb/enzyme/issues/76 and tried the suggestions towards the bottom, I keep getting either undefined or a blank string. I tried levibuzolic's suggestion of using enzyme's simulate change, which can be seen below. However this just returns AssertionError: expected '' to equal 'abcdefghij'

it('Make sure inputted text is shorter than max length', function() {
    const result = mount(<Input maxLength={10}></Input>);
    result.find('input').simulate('change', {target: {value: 'abcdefghijk'}});
    expect(result.state().value).to.equal("abcdefghij");
});

Then I tried takkyuuplayer's suggestion which is also below. This also fails with AssertionError: expected '' to equal 'abcdefghij'

  it('Make sure inputted text is shorter than max length', function() {
    const result = mount(<Input maxLength={10}></Input>);
    result.find('input').node.value = 'abcdefghijk';
    expect(result.state().value).to.equal("abcdefghij");
  });

I found this article: https://medium.com/javascript-inside/testing-in-react-getting-off-the-ground-5f569f3088a#.f4gcjbaak and tried their way which also failed.

  it('Make sure inputted text is shorter than max length', function() {
    const result = mount(<Input maxLength={10}></Input>);
    let input = result.find('input');
    input.get(0).value = 'abcdefghijk';
    input.simulate('change');
    expect(result.state().value).to.equal("abcdefghij");
  });

Finally I tried using the react test utils as suggested by Simulating text entry with reactJs TestUtils, below is the code I tried, however this failed with the error message: TypeError: Cannot read property '__reactInternalInstance$z78dboxwwtrznrmuut6wjc3di' of undefined

  it('Make sure inputted text is shorter than max length', function() {
    const result = mount(<Input maxLength={10}></Input>);
    let input = result.find('input');
    TestUtils.Simulate.change(input, { target: { value: 'abcdefghijk' } });
    expect(result.state().value).to.equal("abcdefghij");
  });

So how does one simulate user input so they can test the onChange function?

like image 894
2trill2spill Avatar asked Jan 03 '17 20:01

2trill2spill


1 Answers

You seem to have a bug in your Input component. When event.target.value.length > this.props.maxLength you never set the actual state, leaving the state.value as ''. It seems you expected it to have been set to the value, but truncated to maxLength. You'll need to add that yourself:

handleChange(event) {
  /* Check if max length has been set. If max length has been
  set make sure the user input is less than max Length, otherwise
  return before updating the text string. */
  if (this.props.maxLength) {
    if (event.target.value.length > this.props.maxLength) {
      // ** Truncate value to maxLength
      this.setState({ value: event.target.value.substr(0, this.props.maxLength) });
      return;
    }
  }
  this.setState({ value: event.target.value });
}

... then, the following test works and passes:

it('Make sure inputted text is shorter than max length', () => {
  const result = mount(<Input maxLength={10}></Input>);
  result.find('input').simulate('change', { target: { value: '1234567890!!!' } });
  expect(result.state().value).to.equal("1234567890");
});
like image 53
Jeff McCloud Avatar answered Oct 20 '22 20:10

Jeff McCloud