Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text() not returning content for React elements #692

Closed
milesj opened this issue Nov 17, 2016 · 13 comments
Closed

text() not returning content for React elements #692

milesj opened this issue Nov 17, 2016 · 13 comments

Comments

@milesj
Copy link
Contributor

milesj commented Nov 17, 2016

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.

@ljharb
Copy link
Member

ljharb commented Nov 17, 2016

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

@milesj
Copy link
Contributor Author

milesj commented Nov 17, 2016

@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".

@ljharb
Copy link
Member

ljharb commented Nov 17, 2016

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.

@laumair
Copy link

laumair commented Nov 22, 2016

@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()..

@gustavohenke
Copy link

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

@milesj
Copy link
Contributor Author

milesj commented Nov 24, 2016

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

@ljharb
Copy link
Member

ljharb commented Nov 25, 2016

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.

@ljharb
Copy link
Member

ljharb commented Jul 6, 2018

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

@ljharb ljharb closed this as completed Jul 6, 2018
@mddrill
Copy link

mddrill commented Jul 9, 2018

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.

@DanDobrick
Copy link

@mddrill
A cleaner syntax is suggested here:
#1800 (comment)

@ghasemikasra39
Copy link

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?

@ljharb
Copy link
Member

ljharb commented Jun 2, 2020

See #1436 for React Native issues.

@matheusggds

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants