What's the proper typing for a React Testing Library wrapper in typescript?
Asked Answered
B

3

7

So i have something like this from kent c dodd's library that is super useful


import * as React from 'react'
import {render as rtlRender} from '@testing-library/react'
import {ThemeProvider} from 'components/theme'

function render(ui, {theme = 'light', ...options} = {}) {
  const Wrapper = ({children}) => (
    <ThemeProvider initialTheme={theme}>{children}</ThemeProvider>
  )
  return rtlRender(ui, {wrapper: Wrapper, ...options})
}

export * from '@testing-library/react'
// override React Testing Library's render with our own
export {render}

i'm having trouble converting it to typescript though. any thoughts on what i need to fine tune below?

import * as React from 'react'
import { ReactNode } from 'react'
import {render as rtlRender} from '@testing-library/react'
import { QueryClientProvider, QueryClient } from 'react-query'

interface WrapperProps {
  children: ReactNode
}

const queryClient = new QueryClient();
function render(ui, {client = queryClient, ...options} = {}) {
  const Wrapper = ({children}: WrapperProps) => (
    <QueryClientProvider client={client}>
      {children}
    </QueryClientProvider>
  )
  return rtlRender(ui, {wrapper: Wrapper, ...options})
}

export * from '@testing-library/react'
// override React Testing Library's render with our own
export {render}

i'm getting the below about typing on the wrapper:

No overload matches this call.
  Overload 1 of 2, '(ui: ReactElement<any, string | JSXElementConstructor<any>>, options: RenderOptions<typeof import("path/node_modules/@testing-library/dom/types/queries"), HTMLElement>): RenderResult<...>', gave the following error.
    Type '({ children }: WrapperProps) => JSX.Element' is not assignable to type 'ComponentType<{}>'.
      Type '({ children }: WrapperProps) => JSX.Element' is not assignable to type 'FunctionComponent<{}>'.
        Types of parameters '__0' and 'props' are incompatible.
          Type '{ children?: ReactNode; }' is not assignable to type 'WrapperProps'.
            Property 'children' is optional in type '{ children?: ReactNode; }' but required in type 'WrapperProps'.
  Overload 2 of 2, '(ui: ReactElement<any, string | JSXElementConstructor<any>>, options?: Omit<RenderOptions<typeof import("path/node_modules/@testing-library/dom/types/queries"), HTMLElement>, "queries">): RenderResult<...>', gave the following error.
    Type '({ children }: WrapperProps) => JSX.Element' is not assignable to type 'ComponentType<{}>'.
      Type '({ children }: WrapperProps) => JSX.Element' is not assignable to type 'FunctionComponent<{}>'.ts(2769)
index.d.ts(41, 3): The expected type comes from property 'wrapper' which is declared here on type 'RenderOptions<typeof import("path/node_modules/@testing-library/dom/types/queries"), HTMLElement>'
index.d.ts(41, 3): The expected type comes from property 'wrapper' which is declared here on type 'Omit<RenderOptions<typeof import("path/node_modules/@testing-library/dom/types/queries"), HTMLElement>, "queries">'
Bibliotaph answered 20/7, 2021 at 20:23 Comment(0)
W
5

Try this instead:

import { ReactElement } from 'react'
import { render as rtlRender } from '@testing-library/react'
import { QueryClientProvider, QueryClient } from 'react-query'

const queryClient = new QueryClient();

const render = (ui: ReactElement, { client = queryClient, ...options } = {}) =>
    rtlRender(ui, {
        wrapper: ({ children }) => (
            <QueryClientProvider client={client}>
                {children}
            </QueryClientProvider>
        ), ...options
    });

export * from '@testing-library/react'
// override React Testing Library's render with our own
export { render }

Ideally you shouldn't be exporting the entirety of testing library just to replace the render function, you could just create your own wrapper and use it together with testing library. But still, that code above should work fine.

Wondawonder answered 20/7, 2021 at 21:6 Comment(0)
C
2

You can type your render function as this

import { render as testingRender, RenderOptions } from "@testing-library/react";

// ...

const render = (
  ui: React.ReactElement,
  options?: Omit<RenderOptions, "queries">,
) => {
  return testingRender(ui, { wrapper: Wrapper, ...options });
};

Just so you know, my wrapper it's defined as yours

type WrapperProps = {
  children: React.ReactNode;
};

const Wrapper = ({ children }: WrapperProps) => {
  return (
    <ChakraProvider>
      <AuthContext.Provider
        value={{
          // ...
        }}
      >
        {children}
      </AuthContext.Provider>
    </ChakraProvider>
  );
};
Cheat answered 20/7, 2021 at 21:1 Comment(1)
I've chosen to separate render function from Wrapper component, but that's not required at all. It was just simpler for me to understand it.Cheat
T
0

I think ts is infering Wrapper as JSX.Element, but the rtlRender expects a "FunctionComponent".

So, you can Wrapper: FunctionComponent or by using its shortcut Wrapper: FC

Also, there is not need for the WrapperProps interface, because children is spected.

import * as React from 'react'

import { QueryClient, QueryClientProvider } from 'react-query'

import {render as rtlRender} from '@testing-library/react'

const queryClient = new QueryClient();
function render(ui, {client = queryClient, ...options} = {}) {
  const Wrapper: React.FC = ({children}) => (
    <QueryClientProvider client={client}>
      {children}
    </QueryClientProvider>
  )
  return rtlRender(ui, {wrapper: Wrapper, ...options})
}

export * from '@testing-library/react'
// override React Testing Library's render with our own
export {render}
Turbinal answered 20/7, 2021 at 21:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.