-
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.
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 PR updates the testing guides to use App Router and TypeScript, also updates `/examples` to show `app` and `pages` examples. ## Overview - [x] Create a new "Testing" section that is shared between `app` and `pages`. - [x] Explain the differences between E2E, unit testing, component testing, etc. - [x] Recommend E2E for `async` components as currently none of the tools support it. - [x] Update setup guides for **Cypress**, **Playwright**, and **Jest** with latest config options, and examples for `app` and `pages`. - [x] Add new guide for **Vitest** - [x] Clean up `/examples`: use TS, show `app` and `pages` examples, match docs config ## Cypress - [x] E2E Tests - [x] Component Testing - [x] Client Components - [x] Server Components - [ ] `async` components **Blockers:** - TS: `Option 'bundler' can only be used when 'module' is set to 'es2015' or later`. In **tsconfig.json** compilerOptions, Next.js uses "moduleResolution": "bundler", changing it to "node" fixes the issue but it can have repercussions. - cypress-io/cypress#27731 - Version 14 is currently not supported for component testing - cypress-io/cypress#28185 ## Playwright - [x] E2E Tests ## Jest - [x] Unit Testing - [x] Client Components - [x] Server Components - [ ] `async` components: testing-library/react-testing-library#1209 - [x] 'server-only': vercel#54891 - [x] Snapshot Testing **Blockers:** - TS: testing-library/jest-dom#546 - None of the solutions in the issue work with Next.js v14.0.4 and TS v5 ## Vitest - [x] Unit Testing - [x] Client Components - [x] Server Components - [ ] `async` components - [x] 'server-only' - [x] Update vitest example - [x] Handles CSS, and CSS modules imports - [x] Handles next/image ## Other - vercel#47448 - vercel#47299
The React team has announced that server components will likely be released in version 19, not 18.3, so we may want to merge issues involving both server components and the breaking changes in 19. As for my work, I'm still focused on updating our current progress, workarounds, and demo along with related resources like Are we RSC yet? and |
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.
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 had the same scenario. I mocked the child RSC in the tests and it works: // MyRSCParent.tsx
export async function MyRSCParent(){
const data = await fetch(....)
return <MyRSCChild />
}
// MyRSCChild.tsx
export async function MyRSCChild() {
const otherData = await fetch(...)
return <span>{otherData}</span<
} // MyRSCParent.test.tsx
jest.mock('path/to/nested/child/component', () => {
return {
MyRSCChild: () => null
}
})
it ('renders the component', () => {
render(await MyRSCParent())
}) This way you can just mock the RSC child component no matter how deeply nested it is in the tree. Perhaps it would be better if the mock returns the actual component after resolving its promise. |
Reminder: We recommend integration testing, rather than unit testing, without mocking components. |
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 off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
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! 😄 |
I'm also using the same approach for rendering async page element. But how should we handle rendering when we have async children in page. How can we achieve that rendering without mocking. |
I am a bit hesitant to post this, as it is a bit of a hacky solution that could potentially not work in all cases. However, In my team we have been using this for a few months now and it has been working great (for all our server components, including nested ones). It basically evaluates functional components and checks if a promise has to be resolved. After resolving the promise it goes down the chain of child elements to see if more promises have to be resolved. There is some extra code in there to prevent extensive warning logging from React, which uses a secret global from React. This code can be removed and all still works, but there will be some warning logs when running the tests when using hooks. https://gist.github.com/sroebert/a04ca6e0232a4a60bc50d7f164f101f6 |
@sroebert This breaks in React 19. Generally, anything that relies on React secret internals may break in any release. |
The secret internals part of the code can be removed, it should still work without it. |
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: