Enzyme: testing of setState call fails when compound component has few child components

Created on 21 Sep 2018  ·  3Comments  ·  Source: enzymejs/enzyme

Describe the bug
Test fails with strange setState error when multiple childs are rendered and callback is passed to each of them. The strange thing is that setState is called from the production code.

Method “setState” is only meant to be run on a single node. 2 found instead.

      79 |       return React.Children.map(this.props.children, child => {
      80 |         return React.cloneElement(child, {
    > 81 |           onPress: () => this.setState({ name: '::any name::' })
         |                                                       ^
      82 |         })
      83 |       })
      84 |     }

To Reproduce

  class MyComponent extends React.Component {
    render() {
      return React.Children.map(this.props.children, child => {
        return React.cloneElement(child, {
          onPress: () => this.setState({ name: '::any name::' })
        })
      })
    }
  }

  class MyChildComponent extends React.Component {
    render() {
      return <Text>Hello!</Text>
    }
  }

// Test
 const wrapper = shallow(
      <MyComponent>
        <MyChildComponent />
        <MyChildComponent />
      </MyComponent>
    )
    wrapper
      .find(MyChildComponent)
      .first()
      .props()
      .onPress()

    expect(wrapper.state()).toEqual({ name: '::any name::' })

Expected behavior
Test to pass

Desktop (please complete the following information):

  • OS: macOS Sierra
  • Jest Version 23.6.0
  • Enzyme 3.6.0
  • react-native 0.57

Most helpful comment

ahhh this is because enzyme does not yet support components that return non-nodes - ie, an array. If you wrap the React.Children.map in a React.Fragment everything will work.

See #1149; closing this one in favor of that.

All 3 comments

What's the output of wrapper.find(MyChildComponent).first().props().onPress.toString())?

What happens if you add a wrapper.update() before trying to check the state?

What's the output of wrapper.find(MyChildComponent).first().props().onPress.toString())?

function onPress() {return _this.setState({ name: '::any name::' });}

What happens if you add a wrapper.update() before trying to check the state?
Actually the test doesn't fail in the expectation, as it fails when onPress callback is called.

I've tried and with update but the error message is same as code doesn't get to this point at all.

it('bad case', () => {
    const wrapper = shallow(
      <MyComponent>
        <MyChildComponent />
        <MyChildComponent />
      </MyComponent>
    )

    console.log(
      wrapper
        .find(MyChildComponent)
        .first()
        .props()
        .onPress.toString()
    )

    wrapper
      .find(MyChildComponent)
      .first()
      .props()
      .onPress()

    wrapper.update()

    expect(wrapper.state()).toEqual({ name: '::any name::' })
  })

Output:

● (Tabs) Component › bad case

    Method “setState” is only meant to be run on a single node. 2 found instead.

      79 |       return React.Children.map(this.props.children, child => {
      80 |         return React.cloneElement(child, {
    > 81 |           onPress: () => this.setState({ name: '::any name::' })
         |                                                       ^
      82 |         })
      83 |       })
      84 |     }

ahhh this is because enzyme does not yet support components that return non-nodes - ie, an array. If you wrap the React.Children.map in a React.Fragment everything will work.

See #1149; closing this one in favor of that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aweary picture aweary  ·  3Comments

AdamYahid picture AdamYahid  ·  3Comments

modemuser picture modemuser  ·  3Comments

nelsonchen90 picture nelsonchen90  ·  3Comments

amcmillan01 picture amcmillan01  ·  3Comments