apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
448 stars 35 forks source link

How can I writing specs for a component using "useReadQuery" #354

Closed EfstathiadisD closed 2 months ago

EfstathiadisD commented 2 months ago

I have a Server and Client component combo. The server has the following..

<PreloadQuery query={ProfileDocument}>
  {(queryRef) => (
    <Suspense fallback={null}>
      <Account queryRef={queryRef} />
    </Suspense>
  )}
</PreloadQuery>

The Client is reading it..

export const Account = ({ queryRef }: AccountProps) => {
  const t = useTranslations("PROFILE");

  const { data } = useReadQuery<Profile>(queryRef);

  const { name, initials } = useMemo(() => {
    const firstName = data.me.customer.firstName;
    const lastName = data.me.customer.lastName;

    return {
      name: `${firstName} ${lastName}`,
      initials: `${firstName.charAt(0)}${lastName.charAt(0)}`,
    };
  }, [data]);

  ...
}

That works as expected. Now I am trying to write some tests.. I am using MockProvider and when I am passing the following object as queryRef to the component which I copy pasted from the console. I get the error..

 Expected a QueryRef object, but got something else instead.

I tried a few cause it seems what I get on the server is different from the one on the client.

export const makeMockedQueryRef = createFixture<Record<string, unknown>>({
  __transportedQueryRef: true,
  options: {
    query: ProfileDocument,
    fetchPolicy: "cache-first",
    returnPartialData: false,
  },
  queryKey: "2a304dd6-a152-40a9-ab3f-87f6b98e0d45",
});
export const makeMockedProfileQueryRef = createFixture<Record<string, unknown>>(
  {
    __transportedQueryRef: { toPromise: () => vi.fn() },
    options: {
      query: ProfileDocument,
      fetchPolicy: "cache-first",
      returnPartialData: false,
    },
    queryKey: "2a304dd6-a152-40a9-ab3f-87f6b98e0d45",
  },
);

The ProfileDocument is correct. I checked. I am not sure of two things..

  1. How can I make the test work and the component to render in the specs.
  2. How can I ensure that the queryRef is correct. I tried to find the TS Def for it, but I don't think the correct one is being exported as both PreloadQueryRef, and QueryRef doesn't seem to be what I want..

It would be nice if someone can link to an example where such specs are written or a README as I couldn't find anything in the DOCS. Thank y!

PS: There is some warning re. the toPromise() with NextJS as well. It tells me that it should pass pure objects btwn Client and Server components. And toPromise() isn't as far as I can tell. It works on runtime, but I am thinking this isn't what I am mocking correct since it could be far more complicated that it looks on the surface. Maybe wrong?

phryneas commented 2 months ago

Hi @EfstathiadisD!

The queryRef you are seeing is only a partial transport object - in parallel, the data of that will also be streamed into the browser, injected into the DOM as a script tag, and simulate an ongoing request in your actual ApolloClient instance. The "transport query Ref" will then at some point (usually when consumed by a hook) be turned back into a real queryRef, latching onto that simulated ongoing request.

That said, I wouldn't try to replicate all of that in a test ;)

Instead, you can create a real queryRef by using the @apollo/client core api createQueryPreloader.

That could look like this:

const mockLink = new MockLink([
   /* Your mocks here */
]);
const client = new ApolloClient({
  link: mockLink,
  cache: new InMemoryCache(),
})
const preloader = createQueryPreloader(client);
const queryRef = preloader(query, { variables: { id: 1 } });

PS: There is some warning re. the toPromise() with NextJS as well. It tells me that it should pass pure objects btwn Client and Server components. And toPromise() isn't as far as I can tell. It works on runtime, but I am thinking this isn't what I am mocking correct since it could be far more complicated that it looks on the surface. Maybe wrong?

We're removing toPromise in the next release because of that warning, but it's really just a warning from React that's a bit too eager, you can ignore it for now.

EfstathiadisD commented 2 months ago

Thanks! That solve the issue.

github-actions[bot] commented 2 months ago

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.