enzyme: Enzyme matchers do not match correctly children of React Fragments

Describe the bug

This is a dedicated issue corresponding to a comment I initially left at https://github.com/airbnb/enzyme/issues/1213#issuecomment-416658762.

When using React Fragments, it looks like only the first child is being picked up by the matchers.

To Reproduce

Assuming the following code:

const Foobar = () => (
  <>
    <div>Foo</div>
    <div>Bar</div>
  </>
);

And the following test:

describe('<Foobar />', () => {
  test('should include Foo and Bar', () => {
    const wrapper = mountWithIntl(<Foobar />);

    expect(wrapper).toIncludeText('Foo');
    expect(wrapper).toIncludeText('Bar');
  });
});

Bar simply does not get rendered:

 FAIL  test/src/Foobar.test.jsx
  ● <Foobar /> › should include Foo and Bar

    Expected <IntlProvider> to contain "Bar" but it did not.
    Expected HTML: "Bar"
    Actual HTML: "Foo"

       8 |
       9 |     expect(wrapper).toIncludeText('Foo');
    > 10 |     expect(wrapper).toIncludeText('Bar');
         |                     ^
      11 |   });
      12 | });
      13 |

      at Object.test (test/src/Foobar.test.tsx:10:21)

mountWithIntl is a simple wrapper that adds an IntlProvider (from react-intl) to help us deal with i18n in our tests. If I remove it and replace with bare mount, I get:

  ● <Foobar /> › should include Foo and Bar

    Trying to get host node of an array

       7 |     const wrapper = mount(<Foobar />);
       8 |
    >  9 |     expect(wrapper).toIncludeText('Foo');
         |                     ^
      10 |     expect(wrapper).toIncludeText('Bar');
      11 |   });
      12 | });

I get the same results if I swap the <>...</> shorthand with <React.Fragment>...</React.Fragment>.

However, if the code is:

const Foobar = () => (
  <div>
    <div>Foo</div>
    <div>Bar</div>
  </div>
);

then now the tests pass fine.

For what it’s worth, the built code for Foobar (built with tsc) looks like:

const Foobar = () => (React__default.createElement("div", null,
    React__default.createElement("div", null, "Foo"),
    React__default.createElement("div", null, "Bar")));

Expected behavior

I’m very confused as Enzyme 3.5 has Fragment support. Do you see something just obviously wrong above?

I’m wondering if the TypeScript compilation doesn’t compile into something that’s causing https://github.com/airbnb/enzyme/issues/1178. Or I’m just doing something wrong…

Additional context

  • enzyme@3.5.0
  • enzyme-adapter-react-16@1.3.0
  • enzyme-matchers@6.0.4 and jest-enzyme@6.0.4 (outside of this project I know)
  • TypeScript v3.0.1
  • Jest and ts-jest

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 7
  • Comments: 24 (15 by maintainers)

Commits related to this issue

Most upvoted comments

@astorije I’ll try to see what I can do about this over the weekend

That’s up to Facebook; there’s an open issue for the shallow renderer to support useEffect.

@astorije in that case, can you file a new issue, and fill out the entire issue template?

I believe @madicap is still working on it; anything based on DocumentFragment would only work on mount.

Hey @madicap, did you end up being able to look into this? 😃

I don’t think there’s a workaround at the moment.

I’ve pushed up failing test cases; the issue seems to be that the adapter’s nodeToHostNode only returns the first node - even though for fragments, there’s multiple. Additionally, there’s no way in the tree to identify that there’s a fragment.

I’m not sure what the best option is - perhaps including the fragment in the tree and flattening it only where needed? or perhaps overloading nodeToHostNode to return an array, or better, a DocumentFragment, in this case? cc @madicap

I’m also curious if reversing the order of the assertions to be

expect(wrapper).toIncludeText('Bar');
expect(wrapper).toIncludeText('Foo');

will cause the same assertion to fail, i.e. is this always failing on the second assertion or on the text of the second child?