blitz-js / babel-plugin-superjson-next

Automatically transform your Next.js Pages to use SuperJSON
MIT License
125 stars 15 forks source link

Testing pages with react-testing-library #35

Closed RohanM closed 3 years ago

RohanM commented 3 years ago

I ran into trouble testing pages in my next.js app with react-testing-library once I introduced SuperJSON. Pages would render, but no props would make their way to them.

Page:

const Index = ({ widgets }) => {
    return <MyPageContent widgets={widgets} />;
});

export default Index;

export const getServerSideProps = async (context) => {
    // ...
    return props;
};

Test:

describe("Index page", () => {
    it("displays widgets", () => {
        const props = { widgets: [] };
        render(<Index widgets={props.widgets} />);
        expect(screen.queryByText(/my-widget/)).toBeInTheDocument();
    });
});

A quick look at the babel-plugin-superjson-next source code shows what's happening: the page is being wrapped in WithSuperJSON, which expects to receive serialized props. But my test was bypassing getServerSideProps, which would have provided the serialized props.

You could argue that it would be more comprehensive to test the whole page, including getServerSideProps. But for now, I solved the problem by wrapping the page in a serialization layer:

import SuperJSON from 'superjson';

// SuperJSON wraps all getServerSideProps in a serialize operation, and all pages in a deserialize operation.
// This works great when running the site, but messes with our page specs. This function can be used to
// effectively "unwrap" the page by serializing our props before passing them on to the SuperJSON wrapper.
//
// Usage:
// import WrappedPage from "pages/mypage";
// const Page = unwrapPage(WrappedPage);
export default function unwrapPage(Page) {
    return (props) => {
        const serializedProps = SuperJSON.serialize(props);
        return Page(serializedProps);
    };
}

And the test becomes:

import WrappedIndex from "pages/index";
const Index = unwrapPage(WrappedIndex);

// ...
Skn0tt commented 3 years ago

That's an interesting solution, thanks for sharing! We could also introduce the following change to WithSuperJSONPage, would that be helpful to you?

if (!serializedProps._superjson) {
    const PageWithoutProps = Page as React.ComponentType<{}>;
-   return <PageWithoutProps />;
+   return <PageWithoutProps {...props} />;
  }
flybayer commented 3 years ago

That's an interesting solution, thanks for sharing! We could also introduce the following change to WithSuperJSONPage, would that be helpful to you?

if (!serializedProps._superjson) {
    const PageWithoutProps = Page as React.ComponentType<{}>;
-   return <PageWithoutProps />;
+   return <PageWithoutProps {...props} />;
  }

Seems like this change should be made regardless of the test issue, right?

Skn0tt commented 3 years ago

It should! I'll put up a PR shortly.

RohanM commented 3 years ago

That's an interesting solution, thanks for sharing! We could also introduce the following change to WithSuperJSONPage, would that be helpful to you?

if (!serializedProps._superjson) {
    const PageWithoutProps = Page as React.ComponentType<{}>;
-   return <PageWithoutProps />;
+   return <PageWithoutProps {...props} />;
  }

Absolutely, that would make things much simpler. Thank you!

Skn0tt commented 3 years ago

Published in v0.2.0 ☺️