Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulating click does not honor the disabled attribute #386

Open
djskinner opened this issue May 12, 2016 · 20 comments
Open

Simulating click does not honor the disabled attribute #386

djskinner opened this issue May 12, 2016 · 20 comments

Comments

@djskinner
Copy link

djskinner commented May 12, 2016

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')
  })
})
@aweary
Copy link
Collaborator

aweary commented May 12, 2016

@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.

@djskinner
Copy link
Author

My bad. mount does indeed work as expected.

@aweary
Copy link
Collaborator

aweary commented May 13, 2016

Thank for verifying!

@M1r1k
Copy link

M1r1k commented Nov 23, 2016

@MartinEden
Copy link

I can confirm this.

@AlexChipev
Copy link

Any chance this is reproducible in v16.2.0.? When I simulate click on disabled button I`m expecting (fn.mock.calls.length).toBe(0); but with shallow I get 2, with mount - 1. Seems it still calls the function.

@zachfejes
Copy link

zachfejes commented Dec 17, 2017

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?

@ljharb
Copy link
Member

ljharb commented Dec 18, 2017

@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.

@ljharb ljharb changed the title Simulating click does not honour the disabled attribute Simulating click does not honor the disabled attribute Dec 18, 2017
@zachfejes
Copy link

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

@Bartuz
Copy link

Bartuz commented May 9, 2018

if someone decided to copy-paste code from @zachfejes here is working getProps() line:

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

getProps() -> props()

@dao-heart
Copy link

dao-heart commented May 30, 2019

Hi, I am facing the same issue as @zachfejes.

const onSubmitSpy = sinon.spy();

const mountedWrapper = mount(
    <Provider store={store}>
      <reduxForm {...props} handleSubmit={onSubmitSpy} />
    </Provider>
  );

mountedWrapper.find("button").prop("onClick")();
 
expect(mountedWrapper.find("button").prop("disabled")).toBe(true); //true

 expect(onSubmitSpy.called).toBe(true); // true

@goodwin64
Copy link

Hi! Any updates on this issue?

@mattvalli
Copy link

Looks like the issue still exists for version 3.10.0. Is there a plan to address this issue in the near future?

@ljharb
Copy link
Member

ljharb commented Nov 20, 2019

@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.

@omril1
Copy link

omril1 commented Dec 6, 2019

@ljharb So how do you simulate a click event (or any event) in v4?

@ljharb
Copy link
Member

ljharb commented Dec 6, 2019

You don't simulate a click event - that'd be testing react itself, or the browser, and that's not your purview.

You assert that the right thing ended up in onClick, and you invoke the onClick prop manually, and assert that the right stuff happened.

@butterywombat
Copy link

butterywombat commented Dec 15, 2020

You assert that the right thing ended up in onClick

@ljharb can you show an example of this? I can do the latter part, but not sure how to assert that if the onClick fn is just a local fn in my functional component. I guess I'd have to expose overriding it as a prop

@ljharb
Copy link
Member

ljharb commented Dec 15, 2020

@butterywombat in your example, what does that local function do? You can extract it with .prop(), and then invoke it, and then test that it did the thing you expect.

@danielo515
Copy link

So what's the issue about nor? The disabled not being honoured on shallow, on mount or on both? It works on mount for me but not for shallow, not sure if that still is the desired case.

@ljharb
Copy link
Member

ljharb commented Apr 14, 2021

If there's a difference between shallow and mount, we should fix shallow to match mount whenever possible.

If someone can provide a PR, or a link to a branch/commit, that includes a test case that passes for mount and fails for shallow, that would make it much easier to work on a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests