10

In NextJs 13+ using the experimental App folder, async server components can be written, as described by the documentation:

export default async function Page({ params: { username } }) {
  // Initiate both requests in parallel
  const artistData = getArtist(username);
  const albumsData = getArtistAlbums(username);

  // Wait for the promises to resolve
  const [artist, albums] = await Promise.all([artistData, albumsData]);

  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums}></Albums>
    </>
  );
}

This is a very useful technique that I have implemented in many pages of my app. However, when testing with jest, I find that I am unable to write any tests that are able to render this default export:

it('should render without crashing', async () => {
  ...(setup mocks)
  const { container } = await waitFor(() => render(<Page params={{ username: 'Bob Dylan' }} />));
});

Any attempt to render the component or call it manually results in the following error:

Uncaught [Error: Objects are not valid as a React child (found: [object Promise])

Has anyone been able to correctly implement a Jest test using an async server component?

Nick McCurdy
  • 17,658
  • 5
  • 50
  • 82
Felipe
  • 10,606
  • 5
  • 40
  • 57

3 Answers3

2

You can test async components that don't use other RSC or app router features with React 18.3 (canary):

import { render, screen } from '@testing-library/react';
import { Suspense } from 'react';
import Page from './page';

it('should render without crashing', async () => {
  render(
    <Suspense>
      <Page params={{ username: 'Bob Dylan' }} />
    </Suspense>
  );
  await screen.findByRole('heading');
  expect(screen.getByRole('listitem')).toBeInTheDocument();
});

You may want to use a custom render function to simplify test setup if your suite heavily relies on async components.

If you need other RSC (i.e. server actions) or app router (i.e. layouts) features you can use hard coding, mocks, or an e2e test framework until we figure out native RSC rendering in #1209.

Nick McCurdy
  • 17,658
  • 5
  • 50
  • 82
1

At the moment there's no official way to do it. You can use this workaround:

it('should render without crashing', async () => {
  // ...(setup mocks)
  render(await Page({params: {username: 'Bob Dylan' }}))
});

It's not perfect but it's the current solution. Check out this issue for more info.

Gio Polvara
  • 23,416
  • 10
  • 66
  • 62
0

I waited for the async component to turn into a React Node. I mean:

render(
        await (async () => await AsyncComponent())()
    )

The discussion is still going under the Github Issue to this day, and NextJS guys suggest even using @//ts-expect-error, claiming that TypeScript team is should correct it now.

noviceGuru
  • 125
  • 1
  • 6