enzyme: Simulating click does not honor the disabled attribute

There seems to be a difference in behaviour between shallow/mount and react-addons-test-utils when it comes to simulating a click on an element with the disabled attribute. React and react-addons-test-utils does not call the onClick handler when the disabled attribute is present but shallow/mount does.

Component under test:

export const DisabledButton = React.createClass({
  getInitialState (){
    return {
      clicks: 0
    }
  },
  handleClick () {
    console.log(`the click was registered`)
    this.setState({
      clicks: ++this.state.clicks
    })
  },
  render () {
    return (
      <section>
        <p>Clicks: {this.state.clicks}</p>
        <button onClick={this.handleClick} disabled={true}>Click Me!</button>
      </section>
    )
  }
})

All tests pass using react-addons-test-utils:

import TestUtils from 'react-addons-test-utils'
import ReactDOM from 'react-dom'

describe('<DisabledButton /> TestUtils', function () {

  it('should start with zero clicks', () => {
    const component = TestUtils.renderIntoDocument(
      <DisabledButton />
    )
    const p = ReactDOM.findDOMNode(TestUtils.findRenderedDOMComponentWithTag(component, 'p'))
    expect(p.textContent).toContain('0')
  })

  it('should not do anything when clicked because the button is disabled', () => {
    const component = TestUtils.renderIntoDocument(
      <DisabledButton />
    )
    TestUtils.Simulate.click(TestUtils.findRenderedDOMComponentWithTag(component, 'button'))

    const p = ReactDOM.findDOMNode(TestUtils.findRenderedDOMComponentWithTag(component, 'p'))
    expect(p.textContent).toContain('0')
  })
})

Using shallow the second test will fail:

import { shallow } from 'enzyme'

describe('<DisabledButton /> shallow', function () {

  it('should start with zero clicks', () => {
    const wrapper = shallow(
      <DisabledButton />
    )
    expect(wrapper.find('p').text()).toContain('0')
  })

  it('should not do anything when clicked because the button is disabled', () => {
    const wrapper = shallow(
      <DisabledButton />
    )
    wrapper.find('button').simulate('click')
    expect(wrapper.find('p').text()).toContain('0')
  })
})

Using mount the second test will fail:

import { mount } from 'enzyme'

describe('<DisabledButton /> mount', function () {

  it('should start with zero clicks', () => {
    const wrapper = mount(
      <DisabledButton />
    )
    expect(wrapper.find('p').text()).toContain('0')
  })

  it('should not do anything when clicked because the button is disabled', () => {
    const wrapper = shallow(
      <DisabledButton />
    )
    wrapper.find('button').simulate('click')
    expect(wrapper.find('p').text()).toContain('0')
  })
})

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 14
  • Comments: 20 (5 by maintainers)

Most upvoted comments

@zachfejes react 15 only goes up to 15.6; there’s no 15.9?

simulate should be avoided; it doesn’t faithfully simulate anything. If you want to invoke the onClick function, extract it from props and invoke it.

I can confirm this.

@djskinner your second test uses shallow in the failing test case when it should use mount. Can you verify it actually fails with mount? I wouldn’t expect it to, as simulate is a thin wrapper around ReactTestUtils.Simulate. shallow on the other hand doesn’t have any guard against this AFAIK right now.

Hi! Any updates on this issue?

My bad. mount does indeed work as expected.

@mattvalli the plan is in v4, to remove simulate entirely. It’s a bad API, and it doesn’t faithfully simulate anything.

Nobody should be using simulate in their tests.

@ljharb thanks for the response - I’ve corrected the version number in my comment to clarify, react 15.5.4.

I’m noticing this bug with react 15.5.4 + enzyme 3.1.0, specifically with a shallow component. For reference, the component has 3 buttons in it, I’m finding them with wrapper.find(‘button’) with has a length 3, and then attempting to simulate a click with:

   const fooMock = jest.fn();
   const wrapper = shallow(<MyComponent foo={fooMock} />); // the button, when clicked, should call foo();

   console.log(wrapper.find('button').at(0).getProps().disabled); // outputs 'disabled', as expected

   wrapper.find('button').at(0).simulate('click');

   expect(fooMock).toHaveBeenCalledTimes(0);

And the test fails, because fooMock was called once - despite the button being disabled. Is there any way around this?