Using NextJS 14 and react testing-library, we previously used to test client components and client-only pages that take dynamic URL parameters like this:
type PageProps = {
params: { date: string }
}
const SomePage: FC<PageProps> = ({ params }) => {
const { date } = use(params);
...
it('renders expected week', async () => {
render(<SomePage params={{ date: '2024-03-20' }} />);
expect(screen.getByText('THIS WEEK')).toBeInTheDocument();
});
In nextJS 15, params is now a promise (https://nextjs.org/docs/app/guides/upgrading/version-15#async-request-apis-breaking-change), so as it's a client-component/page we're using React 19 'use' API in the page (https://react.dev/reference/react/use#reading-context-with-use):
type PageProps = {
params: Promise<{ date: string }>
}
const SomePage: FC<PageProps> = ({ params }) => {
const { date } = use(params);
...
What's the best way to test this with testing-library?
We can pass date as a Promise.resolve, but it never resolves:
it('renders expected week', async () => {
render(<Page params={Promise.resolve({ date: '2024-03-20' })} />);
expect(screen.getByText('THIS WEEK')).toBeInTheDocument();
});
whereas using a traditional 'useEffect' approach does:
const SomePage: FC<PageProps> = ({ params }) => {
const [date, setDate] = useState("");
useEffect(() => {
params.then((resolvedParams) => {
setDate(resolvedParams.date);
});
});
(sandbox here: https://codesandbox.io/p/sandbox/determined-ramanujan-cmjqvw?file=%2Fsrc%2Findex.js%3A10%2C3-15%2C6)
Wrapping the render in act
works for me:
import { render, screen, act } from "@testing-library/react";
// ^^^
// ...
it("renders message when promise resolves in use", async () => {
await act(() =>
render(
<MessageComponentWithUse
params={Promise.resolve({ message: "Hello world" })}
/>
)
);
const message = await screen.findByText(
/Here is the message: Hello World/i
);
expect(message).toBeInTheDocument();
});