React-testing-library: render options: ability to pass props to wrapper

Created on 4 Sep 2020  路  9Comments  路  Source: testing-library/react-testing-library

Describe the feature you'd like:

In creating a custom render for tests, it is often desirable to modify some of the providers in the wrapper to meet various scenarios, such as switching locale, etc. I was able to accomplish this via what now maybe seems to be the older recommendation from the docs:

export const customRender = (ui: React.ReactElement, options?: CustomRenderOptions) => {
  return {
    ...render(<TestProviders {...options?.TestProviderProps}>{ui}</TestProviders>),
  };
};

where we allow some props to be passed into the TestProviders component that has redux, etc.

Suggested implementation:

I'd like to be able to just do that in the render options, so something like:

const customRender = (ui, options) =>
  render(ui, { wrapper: AllTheProviders, wrapperProps: options.wrapperProps, ...options })

or something like that.

Describe alternatives you've considered:

see implementation above.

Teachability, Documentation, Adoption, Migration Strategy:

Most helpful comment

I think this has come up in the past. Either way, I would prefer to not add this extra API when there's a simpler alternative:

const customRender = (ui, options) =>
  render(ui, { wrapper: props => <AllTheProviders {...props} {...options.wrapperProps} />, ...options })

All 9 comments

I could see this pattern replacing the "custom render" idea in many cases:

import { render, screen } from '@testing-library/react'
import AppContext from '../'

render(<Foo />, {
  wrapper: AppContext,
  wrapperProps: { intl: { lang: 'en-US' } },
})

I like it because you can rely more on the examples from the docs instead of remapping imports to an internal file with the custom render function. There are some pitfalls with custom render related to getting types right, adjusting tooling to allow root-relative import for the test utils, etc.

I think this has come up in the past. Either way, I would prefer to not add this extra API when there's a simpler alternative:

const customRender = (ui, options) =>
  render(ui, { wrapper: props => <AllTheProviders {...props} {...options.wrapperProps} />, ...options })

Does that implement rerender and unmount correctly? I feel like there is a simple way but it doesn't cover everything fully, so you can't really use it if you're setting up the lib for a team and expecting it to work just like the docs say.

How does it not support rerender?

maybe my example is incorrect, but here's a rerender that's not working with this approach:

test('will re-render with different children value', async () => {
  const { container, getByTestId, rerender, debug } = customRender(
    <Button data-testid="rerender">First</Button>
  );
  expect(getByTestId('rerender').textContent).toBe('First');
  rerender(<Button data-testid="rerender">Second</Button>);
  expect(getByTestId('rerender').textContent).toBe('Second');
});
export const customRender = (ui: React.ReactElement, options?: CustomRenderOptions) => {
  return render(ui, {
    wrapper: (props: any) => (
      <TestProviders {...props} {...options?.TestProviderProps}>
        {ui}
      </TestProviders>
    ),
    ...options?.testingLibraryOptions,
  });
};

Thanks @kelly-tock, I think there's a bug in that customRender:

export const customRender = (ui: React.ReactElement, options?: CustomRenderOptions) => {
  return render(ui, {
    wrapper: (props: any) => <TestProviders {...props} {...options?.TestProviderProps} />,
    ...options?.testingLibraryOptions,
  });
};

The props that wrapper gets contains the children which you'll want forwarded. Pretty sure this will work fine.

doh, I didn't fully convert it from before. let me test this, I see what you mean now.

confirmed, that works.

Wahoo :) I love it when we realize new APIs aren't needed 馃槃 Thanks @kelly-tock.

Was this page helpful?
0 / 5 - 0 ratings