Checkbox is not `checked` after simulate `change` with enzyme
Asked Answered
E

4

24

I tried to use enzyme to simulate change event on a checkbox, and use chai-enzyme to assert if it's been checked.

This is my Hello react component:

import React from 'react';

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      checked: false
    }
  }

  render() {
    const {checked} = this.state;
    return <div>
      <input type="checkbox" defaultChecked={checked} onChange={this._toggle.bind(this)}/>
      {
        checked ? "checked" : "not checked"
      }
    </div>
  }

  _toggle() {
    const {onToggle} = this.props;
    this.setState({checked: !this.state.checked});
    onToggle();
  }
}

export default Hello;

And my test:

import React from "react";
import Hello from "../src/hello.jsx";
import chai from "chai";
import {mount} from "enzyme";
import chaiEnzyme from "chai-enzyme";
import jsdomGlobal from "jsdom-global";
import spies  from 'chai-spies';

function myAwesomeDebug(wrapper) {
  let html = wrapper.html();
  console.log(html);
  return html
}

jsdomGlobal();
chai.should();
chai.use(spies);
chai.use(chaiEnzyme(myAwesomeDebug));


describe('<Hello />', () => {

  it('checks the checkbox', () => {
    const onToggle = chai.spy();
    const wrapper = mount(<Hello onToggle={onToggle}/>);

    var checkbox = wrapper.find('input');
    checkbox.should.not.be.checked();
    checkbox.simulate('change', {target: {checked: true}});
    onToggle.should.have.been.called.once();

    console.log(checkbox.get(0).checked);
    checkbox.should.be.checked();
  });

});

When I run this test, the checkbox.get(0).checked is false, and the assertion checkbox.should.be.checked() reports error:

AssertionError: expected the node in <Hello /> to be checked <input type="checkbox" checked="checked">

You can see the message is quite strange since there is already checked="checked" in the output.

I'm not sure where is wrong, since it involves too many things.

You can also see a demo project here: https://github.com/js-demos/react-enzyme-simulate-checkbox-events-demo, notice these lines

Emarie answered 6/10, 2016 at 9:23 Comment(0)
B
31

I think some of the details of my explanation might be a bit wrong, but my understanding is:

When you do

var checkbox = wrapper.find('input');

It saves a reference to that Enzyme node in checkbox, but there are times that when the Enzyme tree gets updated, but checkbox does not. I don't know if this is because the reference in the tree changes and therefore the checkbox is now a reference to a node in an old version of the tree.

Making checkbox a function seems to make it work for me, because now the value of checkbox() is always taken from the most up to date tree.

var checkbox = () => wrapper.find('input');
checkbox().should.not.be.checked();
checkbox().simulate('change', {target: {checked: true}});
///...
Basis answered 9/12, 2016 at 19:2 Comment(0)
M
9

It is not bug, but "it works as designed".

Enzyme underlying uses the react test utils to interact with react, especially with the simulate api.

Simulate doesn't actually update the dom, it merely triggers react event handlers attached to the component, possibly with the additional parameters you pass in.

According to the answer I got here (https://github.com/facebook/react/issues/4950 ) this is because updating the dom would require React to reimplement a lot of the browsers functionality, probably still resulting in unforeseen behaviours, so they decided to simply rely on the browser to do the update.

The only way to actually test this is to manually update the dom yourself and then call the simulate api.

Maziar answered 24/10, 2016 at 7:12 Comment(0)
B
1

Below solution best worked for me:

it('should check checkbox handleClick event on Child component under Parent', () => {
const handleClick = jest.fn();
const wrapper = mount(
<Parent onChange={handleClick} {...dependencies}/>,); // dependencies, if any
checked = false;
wrapper.setProps({ checked: false });
const viewChildren = wrapper.find(Children);
const checkbox = viewChildren.find('input[type="checkbox"]').first(); // If you've multiple checkbox nodes and want to select first
checkbox.simulate('change', { target: { checked: true } });
expect(handleClick).toHaveBeenCalled();
});

Hope this helps.

Bleareyed answered 29/5, 2020 at 7:46 Comment(0)
P
1

This is what worked for me:

wrapper.find(CCToggle)
       .find('input[type="checkbox"]')
       .simulate('change', { target: { checked: true } })

CCToggle is my component.

Pavonine answered 5/11, 2021 at 20:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.