Hi,
I am using jsdom with enzyme.
When the component mounts first time it is set to "checked":"checked", the input element has "onChange" event which will change the state and re-renders the check box as uncheked.
if(this.state.field_value === "True"){
<input
type="checkbox"
className="form-control"
name={this.state.field_name}
title={this.state.help_text}
checked="checked"
onChange={this.handleChange} />
}else{
<input
type="checkbox"
className="form-control"
name={this.state.field_name}
title={this.state.help_text}
onChange={this.handleChange} />
}
onChange function:
handleChange: function(event) {
var checked = event.target.checked ? "True":"False";
this.setState({field_value:checked});
}
Now to test this with enzyme, I tried doing this.
it('Renders and Simulates Click Event ', () => {
const CheckBoxProps = {
"field_name": "Howareyou",
"input_type": "Checkbox"
"field_value": "True"
};
const Wrapper = mount(<CheckBox
dataObj={CheckBoxProps} />);
// This one works -- But doesn't seem like a simulation..
expect(Wrapper.find('input[type="checkbox"][checked="checked"]')).to.have.length(1);
Wrapper.setState({'field_value':'false'})
expect(Wrapper.find('input[type="checkbox"][checked="checked"]')).to.have.length(0);
// This one not works
Wrapper.find('input[type="checkbox"][checked="checked"]').simulate('change');
console.log(Wrapper.find('input[type="checkbox"][checked="checked"]')); // still length is 1
});
Even this also not working,
Wrapper.find('input[type="checkbox"]').simulate('change');
expect(Wrapper.state('field_value')).to.equal("False");
P.S : How to do simulation for all the events like input type text,radio,textarea etc.. is there any resource available.
@marlonbernardes I need to make it work for change event.. Well, will give a try for 'click'.
@marlonbernardes Still not working...
You should bind the value of this when you call your handleChange function, because it's not pointing to your react component:
onChange={this.handleChange.bind(this)}
Try this and let me know if it works for you. I also have some tips on how to make your component's code more concise, but lets focus on one thing at a time.
@marlonbernardes Sorry for the late reply. Still it is not working... Can you post me a sample of how to test a checkbox with jsdom and enzyme ? That would be a great help.
(of course, you shouldn't create functions and pass them as props in the render path - so you'd want this.handleChange = this.handleChange.bind(this) in your constructor)
@ljharb @marlonbernardes This is the full code https://gist.github.com/bboysathish/8c78d6c295c0613d08b6.
Edit: That's not the best code though.. But it should work.
The following is the error,
1) checkbox Renders and Simulates Click Event :
AssertionError: expected 'True' to equal 'False'
+ expected - actual
-True
+False
at Context.<anonymous> (checkbox_component_test.js:25:47)
at callFn (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runnable.js:315:21)
at Test.Runnable.run (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runnable.js:308:7)
at Runner.runTest (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:422:10)
at C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:533:12
at next (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:342:14)
at C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:352:7
at next (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:284:14)
at Immediate._onImmediate (C:\Users\sathishkumar_so\Downloads\SSBT\Admin-UI\Feb24\Code\node_modules\mocha\lib\runner.js:320:5)
@bboysathish calling .simulate('change') doesn't change the value though - have you tried .simulate('change', { target: { checked: true } }) or something similar?
@ljharb Worked like a charm, Thanks Wrapper.find('input[type="checkbox"]').simulate('change',{ target: { checked: false } }); . But is there a documentation for this target argument ? or list of arguments I can pass to simulate. As per this documentation, http://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html. I don't see anything.
@bboysathish See item 2 on http://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html#arguments
@ljharb R u referring this ...args (Any [optional]): A mock event object that will get passed through to the event handlers.? Can you please point me, how does mock event object will look like ? (Kinda what are the properties it accepts)
@bboysathish it's any object you like. whatever you pass there, will be passed as arguments to the event handler. there's no magic.
@ljharb So, for input[type="text"] to simulate a change event with some text I can do something like this?
Wrapper.find('input[type="text"]').simulate('change',{ target: { value: "I need model target object :)" } })
Looks right to me - try it and see! :-)
Great!! Will try.. Thanks @ljharb ...
Wrapper.find('input[type="checkbox"]').simulate('change',{ target: { checked: true } });
Now, this isn't working for me.
I hit some problems when I was using e.currentTarget in the onChange function. If I specified .simulate('change',{ currentTarget: { checked: true } });, it seemed to get overwritten by the time it reached the onChange function, giving unexpected results. Changing currentTarget to target in both the onChange function and the test fixed this.
In case someone else hits this. If you're using typescript, React.SyntheticEvent doesn't have event.target.checked. Given that I'd like my tests to work, I don't have a solution to this other than casting in the event handler :(.
As a variation on @Burkazoid's suggestion, a workaround that doesn't require changing currentTarget to target in the code would be to use onChange instead of simulate in your tests and pass in your desired event, i.e.:
.prop('onChange')({currentTarget: { checked: false }})
Is there a way to simulate checking a checkbox that is within in a select?
Edit: My use case is that I have a select with a bunch of checkboxes in them; instead of passing the change event directly to the select, I wanted to simulate clicking the checkboxes, because I wanted to make sure the values that I have passed to the checkboxes are correct.
The current test setup that I have has two steps: 1. Test passing a change event to select 2. Getting all of the checkboxes and testing the props().value; in a nutshell, I want to combine these two together.
The problem that I have is that the rendered DOM of select does not have any checkboxes, I assume because it is hidden unless you click on it, but I simulating click on the select does not open it.
I am using Material-UI components, but that shouldn't change the scenario.
@HunderlineK no, you can't simulate anything. You can explicitly invoke the onClick or onChange prop; you can explicitly set the state so that it's open, but what you want to do requires a full browser integration test, and can't be done properly with enzyme.
For those who are rendering the actual html input in another component.
You will need to mount instead of shallow in order to properly use the simulate('change ....
@Duongasaur i'm going to hide that comment, because no, you don't need mount instead of shallow, and you shouldn't need simulate anyways.
wrapper = mount(<customCheckboxToConfigure store={store}/>);
wrapper.setProps({checked:false});
wrapper.find('input[type="checkbox"]').simulate('change', {target:{checked:true}})
expect(wrapper.state(checked)).toBe(true);
@ljharb this shows an error like
Method “simulate” is only meant to be run on a single node. 0 found instead.
@surya-art sounds like there's no checkboxes then. Remember to wrapper.update() after the setProps, and then what does wrapper.debug() print out?
(Please file a new issue with your comment and your response, if that doesn't solve it for you)
As a variation on @Burkazoid's suggestion, a workaround that doesn't require changing
currentTargettotargetin the code would be to useonChangeinstead ofsimulatein your tests and pass in your desired event, i.e.:.prop('onChange')({currentTarget: { checked: false }})
change currentTarget to target if you're using .prop
.prop('onChange')({target: { checked: false }})
Most helpful comment
@bboysathish calling
.simulate('change')doesn't change the value though - have you tried.simulate('change', { target: { checked: true } })or something similar?