Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enzyme- How to set state of inner component?

I need to access the state of the inner component, to make it active for click event, my problem is Enzyme does not allow this when using mount, this can only be achieved by shallow rendering of enzyme as mentioned over here, also as mentioned I have tried to use dive to fetch the Form component and again from Form to get Button component which I need to reach, the problem is that my test case keeps on failing as Form component length is zero.

enzyme: 3.1.0

enzyme-adapter-react-15: 1.0.1"

I am pretty new to Enzyme, Any help will be appreciated, Thanks

contactus.test.js :

 test('It should simulate submit action ', ()=>{
   let contactUs = shallow(<ContactUs />);
   sinon.spy(ContactUs.prototype, 'submitMessage');// Verify this method call
   let form = contactUs.find(Form)
   expect(form.length).toBe(1);//Failing over here
   let button = form.dive().find(Button);
   expect(button.length).toBe(1);
   button.setState({disabled : false});//Need to achieve this 
   expect(button).toBeDefined();
   expect(button.length).toBe(1);
   expect(button.props().label).toBe('SEND MESSAGE');

   button.find('a').get(0).simulate('click');
  expect(ContactUs.prototype.submitMessage).toHaveProperty('callCount', 
 1);
});

contactus.js :

import React, {Component,PropTypes} from 'react';
import Form from './form';
import {sendSubscriptionMessage} from '../../network';
import Button from '../Fields/Button';

export default class ContactUs extends Component {

constructor(props) {
    super(props);
    this.state = {
        contactData: {}
    }
}

onChangeHandler(event) {
    let value = event.target.value;
    this.state.contactData[event.target.name] = value;
}

submitMessage(event) {
    event.preventDefault();
    sendSubscriptionMessage(this.state.contactData);
}

render() {
    return (<div className = "row pattern-black contact logo-container" id = "contact">
        <div className = "container" >
        <h2 className = "sectionTitle f-damion c-white mTop100" >
        Get in Touch!

       <Form onChangeHandler = {
            this.onChangeHandler.bind(this)
        } >
        <Button onClick = {
            this.submitMessage.bind(this)
        }
        className = "gradientButton pink inverse mTop50"
        label = "SEND MESSAGE" / >
        </Form> </div> 
        </div>
    );
  }
 }
like image 275
Vipul Panth Avatar asked Nov 23 '17 20:11

Vipul Panth


People also ask

How do you set an enzyme state?

setState(state) => Self. A method to invoke setState() on the root component instance similar to how you might in the definition of the component, and re-renders. This method is useful for testing your component in hard to achieve states, however should be used sparingly.

How do you set a state in components?

The setState() Method State can be updated in response to event handlers, server responses, or prop changes. This is done using the setState() method. The setState() method enqueues all of the updates made to the component state and instructs React to re-render the component and its children with the updated state.

Can we set a component in state?

Yes, it's optional as we could store a string in the state and render the component conditionally but in some cases, it is simpler to just store the component in the state.

How do you determine the state value of an enzyme?

Here is how you can access them: import {shallow} from 'enzyme'; describe('SomeComponent component', () => { it('Shallow rendering', () => { const wrapper = shallow(<SomeComponent someProp={1}/>); const componentInstance = wrapper. instance(); //Accessing react lifecyle methods componentInstance.


1 Answers

First of all I think you should not test the Button and the Form functionalities here. In this file you should test only the ContactForm component.

For the first fail, this should work:

find('Form') (the Form should have quotes)

Same for the button: find('Button');

In this way you don't even have to import the Form and the Button components in your test file at all;

Then, you don't have to set any state for that button. You test the button functionality in the Button.test.js file. All you have to do here is to call its method like this: button.nodes[0].props.onClick();

Overall, this is how your test should look like ( note that I didn't test it, I've been using Jest for testing my components, but the logic should be the same ):

 test('It should simulate submit action ', ()=>{
   const wrapper = shallow(<ContactUs />);
   const spy = sinon.spy(ContactUs.prototype, 'submitMessage'); // Save the spy into a new variable

   const form = wrapper.find('Form') // I don't know if is the same, but in jest is enough to pass the component name as a string, so you don't have to import it in your test file anymore.

   expect(form).to.have.length(1);

   const button = wrapper.find('Button'); // from what I see in your code, the Button is part of the ContactUs component, not of the Form.

   expect(button).to.have.length(1);

   /* These lines should not be part of this test file. Create a test file only for the Button component.
   button.setState({disabled : false}); 
   expect(button).toBeDefined();
   expect(button.length).toBe(1);
   expect(button.props().label).toBe('SEND MESSAGE');
   */

   button.nodes[0].props.onClick(); // Here you call its method directly, cause we don't care about its internal functionality; we want to check if the "submitMessage" method has been called.


  assert(spy.called); // Here I'm not very sure... I'm a Jest fan and i would have done it like this "expect(spy).toHaveBeenCalled();"
});
like image 184
ValentinVoilean Avatar answered Oct 15 '22 11:10

ValentinVoilean