-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for React Server Components #1209
Comments
You can use like this:
|
@prakashtiwaari Not quite. It's
|
A friend came up with the following: const props = {
params: { surveyType: '123' },
searchParams: { journeyId: '456' },
}
const Result = await Page(props)
render(Result) I'll wait for a moderator to close this but it would be nice to have async components supported inside render |
This comment was marked as resolved.
This comment was marked as resolved.
I like this idea personally. It should be easy enough to introspect if a component is async by determining if it returns a Promise. Then we can await the Promise internally and either return a Promise in render or poll to return synchronously. |
For those that are trying to mock API responses, this seems to be working for now: test("should do stuff", async () => {
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({ login: "Gio" }),
});
render(await Page());
expect(screen.getByText("Hi Gio!")).toBeInTheDocument();
}); If you don't define I would love to be able to use MSW, but that's still not working with Next's model. See: https://twitter.com/ApiMocking/status/1656249306628915208?s=20 On a side note, even if we make this work, there's the issue of Next layout composition. If I render a |
We could document using layouts similarly to how we already document using providers: by importing them and passing them as the import {render} from '@testing-library/react'
import Layout from './layout'
import Page from './page'
render(<Page />, {wrapper: Layout}) @Gpx has a good point that rendering nested layouts would be more challenging. We could try to expose an abstraction, but the paths to import from would be dynamic and depend on Next patterns. If this isn't practical, we can open an issue with Next upstream and ask for an API to consume internally. Also if we change render to be compatible with returning promises, we should probably ship a breaking change so end users are aware they may need to change their tests (especially if they're using TypeScript). |
I agree we should ask Next to provide an API. I'm thinking something like this: const RSC = routeComponent('/some/path') // This will return an RSC with all nested layouts passing the correct props
render(RSC)
// With additional props
const RSC = routeComponent('/some/path', { foo: 1 })
render(RSC) WDYT? As for the breaking change, won't this be a feature? I don't think anyone was relying on |
I'm doing more research into RSCs using Next, and I noticed some mistaken assumptions I had:
Overall, I think supporting React server components involves figuring out the following concerns:
|
@Gpx Shouldn't that be |
The idea to render the server component as async client components seems to be the best idea I've seen so far. I don't think it's suitable for RTL to bake Next.js' (or any other framework's) semantics into it's API. Async client components solve this by being agnostic of any framework. I think this would retain the current RTL API with maybe the addition of a suspense wrapper - rendering nothing initially could be confusing so enforcing a fallback makes sense. render(<Page />) // throws an error because there's no boundary (I think this would be default react behaviour)
render(<Suspense><Page /></Suspense>) // This would be ok |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
I do think it's easier to just mount the server component as a client component with createRoot though - this works in react-dom canary today. See commit on your test repo here: tom-sherman/rsc-testing@9e4aa67 I'm not sure how RTL can support rendering of the root layout in Next.js, but as I said before I don't think it should - at least not part of it's core API. The root layout returning |
Thanks for the branch, that's interesting. I'd still like to understand why this works and |
You're going to need an |
That's closer to what I was thinking originally. Though, I think I'm confused about how this is working, but I'll reply here again when I resolve it. |
No, I think it should be const { Component, props } = routeComponent('/some/path')
render(<Component {...props} />) We need not only the components tree but also the props that Next is passing. The URL should be passed to the method, not the filesystem path. If we want to simulate a real user interacting with the app they'll use URLs. To be clear, I'm not saying we should implement this in RTL, but rather that we should ask the Next team to provide it since it will be helpful for anyone doing tests. |
I'm not sure we need it to return params, since you can just choose what params to render in your test by changing the URL. |
Say you want to test the URL
I can create the If we want to keep the |
Do we agree that RTL (at least in the core API) shouldn't support this kind of routing stuff? If so probably best to split that conversation out into a different issue? |
This comment was marked as resolved.
This comment was marked as resolved.
@Gpx Yes, I think that's simpler, and I'd rather avoid exposing the implementation detail of param values.
@tom-sherman I'm not suggesting we add a Next specific app router API directly into Testing Library. However, I'd like us to have either docs or some sort of adapter/facade/etc. design pattern that composes a Next routing primitive. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
The demo seems to work great with React 19 RC! 😄 |
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
how to test the server components in nextjs using jest , can anyone give me an example? |
@nickmccurdy
I'm sorry, but I don't understand what you're saying. Concerning the case where we have nested async components in the component we are testing, how are we supposed to avoid errors like ETA: Ah, I think this is the solution: > @Ruffeng Please use the workarounds, they properly use React canary to support nested components. Anything else will require reimplementing async components, which is not worth it for Testing Library or any end user to support. All these hidden comments make following the conversation confusing... |
@david-zenx The workarounds and demo still support Jest! |
@Stan-Stani I've been trying to keep this issue as clean and on topic as I reasonably can, as there have been some resolved, duplicate, and off topic replies, and this has been important for reference and historical documentation. Sorry if that's confusing, but you probably don't have to worry about hidden replies if you read other replies (especially the workarounds). Let me know if you have any additional questions or feedback, though. |
@nickserv I am wondering if you are still planning to do this. We are using your mentioned workaround as the official solution in storybook, as storybook runs in the browser and our playwright based test runner as well. It works actually susprisingly well. We have a storybook demo here: However some libraries uses react-server conditions for their exports. So that you can not accidently import stuff meant only for RSC on the client:
If more and more libraries are gonna adopt the react-server convention, then this solution would become problematic. Just wondering what you’re thoughts are on this matter, as it seems you have been thinking about it for quite some time! |
I can confirm the workaround works. In addition, I believe I’ve discovered an issue when using snapshots with Vitest and async react server components. For example here the snapshots were being generated as an empty test("Having `<Layout>` here will generate an empty snapshot", async () => {
const { container } = render(
<Suspense>
<Layout>
<Page />
</Layout>
</Suspense>,
);
await screen.findByText("server");
expect(container).toMatchSnapshot();
});
test("Don't using screen here will generate an empty snapshot", async () => {
const { container } = render(
<Suspense>
<Layout>
<Page />
</Layout>
</Suspense>,
);
expect(container).toMatchSnapshot();
}); My workaround was to first perform some screen assertions. After doing this, the snapshots are generated correctly when using test("Removing the `<Layout>` tag and calling screen will generate a correct snapshot", async () => {
const { container } = render(
<Suspense>
<Page />
</Suspense>,
);
await screen.findByText("server");
expect(container).toMatchSnapshot();
}); I created a pull-request that adds those examples to @nickserv demo. |
@abel-castro any trick to getting this to work? wrapping it in a Suspense was insufficient for me - I still got an error about trying to render a Promise |
This is still working great for us: #1209 (comment) |
For me the problem has been that the React version is not correct - something happens when installing Next.js RC, it becomes overwritten. You can check it by throwing a console.log(React.version) into a test. If it is not using React 19, run npm install react@rc react-dom@rc again (even though you already have React RC in your dependencies). |
I agree with @aurorascharff. As mentioned in previous comments, to make this work, it’s currently necessary to use some edge versions of React, ReactDOM, and Next.js. For reference, you can check the versions used by @nickserv in his demo. "dependencies": {
"next": "^15.0.0-rc.0",
"react": "canary",
"react-dom": "canary",
... |
This did not work for me, I had to install the fresh one with npm install react@rc react-dom@rc. It gave me:
|
At this point my resources are limited. I'm disabled and out of savings, so I have to work part time and spend less time on open source. I also have many RSC projects with not nearly enough energy to finish them. At this point the best way to ensure I can work on this soon is to sponsor me (like Kent C. Dodds and Artem Zakharchenko have), otherwise I'll be prioritizing the workaround and other projects which are much simpler. |
Can confirm this works, I don't think this should be flagged as 'duplicate'. It's a good solution if you don't want to update React (in my case that broke other packages like Radix). When a proper solution is in place I can simply replace the render function with the original function from testing-library. |
Since we've upgraded to react 19 we seem to be getting the following in our screenshot tests for forms with assigned server actions; <form
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"
class="form"
id="pay-payment-request-form"
>
...
</form> Not sure if this is supposed to happen, but seems wrong? |
@Netail Not sure what's causing that. Can you share a more complete reproduction please? |
I have noticed this happen with React 19 for a while, unrelated to testing. I'm not sure if they will fix it, but it doesn't cause any problems. |
After upgrading to React 19 the recommended workaround for testing async server components using suspense and "await findBy" is no longer working for me. |
Hm, I think this is expected right? This is just internal React error, when you are trying to use action prop in wrong way? I think it will never be called if you use the API correctly? |
mhm seems like it |
Since React 19 is released, how long will it take for RTL to officially support server components and server actions? |
Yes this is expected, as the action with the code is served by the React server. But when it comes to testing a form with an action e.g. const submit = screen.getByTestId("submit");
await act(() => userEvent.click(submit)); It actually throws the error, so it seems we need new testing utility which will indeed call the Currently, to test forms with |
Describe the feature you'd like:
As a user of react 18 with NextJS (with app directory), I would like to render async server components
example:
// Page.tsx
...
// Page.test.tsx
Extracting server page logic would be an alternative, but I think that would also significantly reduce the purpose of RTL if that were to become an encouraged architectural default.
Current progress, workarounds, and demo
The text was updated successfully, but these errors were encountered: