Enzyme: text() not returning content for React elements

Created on 17 Nov 2016  路  12Comments  路  Source: enzymejs/enzyme

Say I have the following simple example:

<Foo>
  Hello

  {condition && (
    <span>, Miles</span>
  )}
</Foo>

And I want to verify the text() (children) of the component is either "Hello" or "Hello, Miles". This doesn't work when wrapped with React components, as it simply returns <Foo /> when text() is used.

It's caused by these lines in ShallowTraversal:

  if (node.type && typeof node.type === 'function') {
    return `<${node.type.displayName || functionName(node.type)} />`;
  }

Couldn't this simply check props.children, stringify them, and join them? Is there a reason that using text() on a React element is prohibited?

Otherwise, I have to write tests like the following.

expect(wrapper.find(Foo).prop('children')).to.deep.equal(['Hello', false]);
expect(wrapper.find(Foo).prop('children')).to.deep.equal(['Hello', <span>, Miles</span>]);
// If "Miles" is an interpolated variable
expect(wrapper.find(Foo).prop('children')).to.deep.equal(['Hello', <span>, {'Miles'}</span>]);

Which is kind of annoying, as the span is irrelevant.

shallow discussion question

Most helpful comment

As a temporary workaround, would expect(wrapper.find(Foo).render().text()).to.equal('Hello World') work?

All 12 comments

As a temporary workaround, would expect(wrapper.find(Foo).render().text()).to.equal('Hello World') work?

@ljharb In the example above it would, but depending on the implementation of Foo, it would not. For example, if Foo renders text within it self, or through like a title prop, the title would be included in the output. Like so:

<Foo title="Welcome">
  Hello

  {condition && (
    <span>, Miles</span>
  )}
</Foo>

Using render().text() would return "WelcomeHello, Miles".

Clarifying per our offline discussion: it sounds like what you want is "get the text of the children prop" - since .text() would be "get the text of what the root element _renders_.

Given that, I suspect your best bet is shallow(<div>{wrapper.prop('children')}</div>).text(), but I'll leave this open to think more about it.

@milesj @ljharb wrapper.childAt(1).text() should would if we were to assert on Miles, otherwise I assume this would either work wrapper.children().at()..

I have noted this problem too.
I'm working around it by checking the children prop, just like @milesj suggested in the OP.

What if there was a childrenText() method that solves this problem? We would avoid backwards incompatible changes if text() was modified.

OK, to restate: if you have const jsx = <Bar><Foo>hello</Foo></Bar>, and you want to start with shallow-rendering Bar and assert that hello is passed to Foo - you'd do:

const jsx = <Bar><Foo>hello</Foo></Bar>;
const wrapper = shallow(jsx);

assert.equal(wrapper.text(), '<Foo />'); // current implementation

const fooChildren = wrapper.find(Foo).prop('children');

assert.equal(React.Children.toArray(fooChildren), ['hello']); // works, but couples to the exact jsx structure of how `Bar` renders `Foo`'s children, so, bad.
assert.equal(shallow(<div>{fooChildren}</div>).text(), 'hello'); // works perfectly, but is inelegant

const foo = wrapper.dive();

assert.equal(foo.text(), 'hello'); // this depends on the `render` implementation of `Foo`.

A method like childrenText would certainly work (it would effectively do the wrapping div + .text() call), but that seems like a bit of a special case, given that elements can be passed in props other than children.

Maybe a method like shallowWrapProp that took a prop name, and returned the shallow wrapper? ie, wrapper.shallowWrapProp('children').text()? Then it would work for any arbitrary prop, and wouldn't be limited to just the "text" method?

I'm still not convinced this is an edge case we want to support, but that seems like it might be a clean way to implement it if we do.

I don't think this is still an issue in v3; please file a new issue if that's not the case.

I am experiencing this issue with jest-enzyme 6.0.2 and enzyme 3..3.0 (according to npm outdated these are the latest versions). The shallow(<div>{fooChildren}</div>).text() thing worked as a workaround.

@mddrill
A cleaner syntax is suggested here:
https://github.com/airbnb/enzyme/issues/1800#issuecomment-417044226

Dear @ljharb
I had the same issue in React Native.
I used expect(wrapper.find(Foo).render().text()).to.equal('Hello World') and worked perfect.
Don't we have a better way I doing this?

See #1436 for React Native issues.

Was this page helpful?
0 / 5 - 0 ratings