Testing a Next.js app with a custom /pages/_app.js in React-Testing-Library
Asked Answered
H

1

17

I'm attempting to follow this guide in React Testing Library documentation to wrap all the components I want to test. I'm doing this because I need access to the various context providers that are defined in _app.js within the components I'm testing.

This is my /pages/_app.js file:

export class MyApp extends App {
  public componentDidMount() {
    const jssStyles = document.querySelector("#jss-server-side");
    if (jssStyles && jssStyles.parentNode) {
      jssStyles.parentNode.removeChild(jssStyles);
    }
  }
  public render() {
    const { Component, pageProps, apolloClient } = this.props;
    return (
      <Container>
        <StateProvider>
          <ThemeProvider theme={theme}>
            <ApolloProvider client={apolloClient}>
              <CssBaseline />
              <Component {...pageProps} />
              <SignUp />
              <Snackbar />
            </ApolloProvider>
          </ThemeProvider>
        </StateProvider>
      </Container>
    );
  }
}

export default withApollo(MyApp);

This is my /utils/testProviders.js file:

class AllTheProvidersWrapped extends App {
  public componentDidMount() {
    const jssStyles = document.querySelector("#jss-server-side");
    if (jssStyles && jssStyles.parentNode) {
      jssStyles.parentNode.removeChild(jssStyles);
    }
  }
  public render() {
    const { pageProps, apolloClient, children } = this.props;
    return (
      <Container>
        <StateProvider>
          <ThemeProvider theme={theme}>
            <ApolloProvider client={apolloClient}>
              <CssBaseline />
              {React.cloneElement(children, { pageProps })}
              <SignUp />
              <Snackbar />
            </ApolloProvider>
          </ThemeProvider>
        </StateProvider>
      </Container>
    );
  }
}
const AllTheProviders = withApollo(AllTheProvidersWrapped);

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

export * from "react-testing-library";
export { customRender as render };

This is my /jest.config.js file:

module.exports = {
  testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/"],
  moduleDirectories: ["node_modules", "utils", __dirname]
};

And this is an example of a test I'm trying to run:

import React from "react";
import { render, cleanup } from "testProviders";

import OutlinedInput from "./OutlinedInput";

afterEach(cleanup);

const mockProps = {
  id: "name",
  label: "Name",
  fieldStateString: "signUpForm.fields"
};

describe("<OutlinedInput />", (): void => {
  it("renders as snapshot", (): void => {
    const { asFragment } = render(<OutlinedInput {...mockProps} />, {});
    expect(asFragment()).toMatchSnapshot();
  });
});

The error message outputted from the test is:

    TypeError: Cannot read property 'pathname' of undefined

      52 | 
      53 | const customRender: CustomRender = (ui, options) =>
    > 54 |   render(ui, { wrapper: AllTheProviders, ...options });
         |   ^
      55 | 
      56 | // re-export everything
      57 | export * from "react-testing-library";

If I had to guess, I'd say that the <Component {...pageProps} /> component in /pages/_app.js is what provides the pathName as part of Next.js's routing.

The examples provided by Next.js don't cover how to do this so I'm hoping someone here might be able to help.

Hydnocarpate answered 6/5, 2019 at 1:17 Comment(3)
Did you ever solve this, @GCM?Lackluster
Nope :( I have to run the tests outside Next.js context, unfortunately.Hydnocarpate
I'm also wondering how to write tests that test the rendering of pages. Testing simple components is simple enough, but pages rely on HOC or complexe setup like req/res, apollo, etc. It's not so simple to test the _app for instance... But I'm also wondering if testing those things are interesting with unit tests, I'm considering using Cypress for everything that is "visual" testing (workflow, navigation), and keep unit tests for what's simpler. (pure components)Forwent
H
0

Did you already tryed remove the contexts from your /utils/testProviders.js file:

    class AllTheProvidersWrapped extends App {
      public componentDidMount() {
        const jssStyles = document.querySelector("#jss-server-side");
        if (jssStyles && jssStyles.parentNode) {
          jssStyles.parentNode.removeChild(jssStyles);
        }
      }
      public render() {
        const { pageProps, apolloClient, children } = this.props;
        return (
          <ApolloProvider client={apolloClient}>
            <CssBaseline />
            {React.cloneElement(children, { pageProps })}
            <SignUp />
            <Snackbar />
          </ApolloProvider>
        );
      }
    }
    const AllTheProviders = withApollo(AllTheProvidersWrapped);
    
    const customRender = (ui, options) =>
      render(ui, { wrapper: AllTheProviders, ...options });
    
    export * from "react-testing-library";
    export { customRender as render };

I Think, the Context provider only works on the _app.js file, it's a Next.js thing.

Hocus answered 6/4, 2023 at 18:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.