testing-library-docs icon indicating copy to clipboard operation
testing-library-docs copied to clipboard

Rerender with a wrapper

Open lithin opened this issue 3 years ago • 1 comments

  • @testing-library/react version: 13.4.0
  • Testing Framework and version: jest @ 29.0.3
  • DOM Environment: jsdom @ 20.0.0

Relevant code or config:

// in test utils
const customRender = (
  ui: ReactElement,
  apolloMocks?: ApolloMocks,
  options?: Omit<RenderOptions, 'wrapper'>
) =>
  render(ui, {
    wrapper: () => ui, // this would include our providers wrapped around ui but this demonstrates the issue in a cleaner way
    ...options,
  });

// in test file

const Comp = ({ number }: { number: number }) => <p>{number}</p>;

it('check render', () => {
    const { rerender } = customRender(<Comp number={1} />);
    rerender(<Comp number={2} />);
    expect(screen.getByText('2')).toBeInTheDocument();
  });

What you did:

Rerendering a component that was originally rendered out with the wrapper option, combining two different guides in the docs.

What happened:

No props are updated. The component does rerender, however it does so with the "old" props. (So in the example above, the test fails because 1 is present instead of 2.)

Reproduction:

Happy to add this if necessary.

Problem description:

We can't actually re-render stuff :) The workaround we're using is to put the wrapper directly into the test but that sort of defeats having any wrapper to begin with.

Suggested solution:

Not sure how the internals work and why the wrapper would interfere with the props. I imagine we'd need to see why the props aren't being passed correctly and fix it on that level.

lithin avatar Oct 06 '22 18:10 lithin

Note the

render(ui, {
    wrapper: () => ui,

This means that your wrapper will always render the initial element not the element given to rerender.

Remember that wrapper is treated as a normal React component. What happens internally is just normal React:

<Wrapper>
{element}
</Wrapper>

But in your example element will always be the initial ui.

What you want instead is

render(ui, {
    wrapper: (props) => props.children,

children will be what you give to render(children) and rerender(children).

I'll move this to the docs to add an example that highlights the importance of handling children.

eps1lon avatar Oct 08 '22 09:10 eps1lon