/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails react-core */ 'use strict'; import {patchSetImmediate} from '../../../../scripts/jest/patchSetImmediate'; // Polyfills for test environment global.ReadableStream = require('web-streams-polyfill/ponyfill/es6').ReadableStream; global.TextEncoder = require('util').TextEncoder; global.TextDecoder = require('util').TextDecoder; let act; let serverAct; let use; let clientExports; let clientExportsESM; let turbopackMap; let Stream; let React; let ReactDOMClient; let ReactServerDOMServer; let ReactServerDOMClient; let Suspense; let ErrorBoundary; describe('ReactFlightTurbopackDOM', () => { beforeEach(() => { // For this first reset we are going to load the dom-node version of react-server-dom-turbopack/server // This can be thought of as essentially being the React Server Components scope with react-server // condition jest.resetModules(); patchSetImmediate(); serverAct = require('internal-test-utils').serverAct; // Simulate the condition resolution jest.mock('react-server-dom-turbopack/server', () => require('react-server-dom-turbopack/server.node'), ); jest.mock('react', () => require('react/react.react-server')); const TurbopackMock = require('./utils/TurbopackMock'); clientExports = TurbopackMock.clientExports; clientExportsESM = TurbopackMock.clientExportsESM; turbopackMap = TurbopackMock.turbopackMap; ReactServerDOMServer = require('react-server-dom-turbopack/server'); // This reset is to load modules for the SSR/Browser scope. jest.resetModules(); __unmockReact(); act = require('internal-test-utils').act; Stream = require('stream'); React = require('react'); use = React.use; Suspense = React.Suspense; ReactDOMClient = require('react-dom/client'); ReactServerDOMClient = require('react-server-dom-turbopack/client'); ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; static getDerivedStateFromError(error) { return { hasError: true, error, }; } render() { if (this.state.hasError) { return this.props.fallback(this.state.error); } return this.props.children; } }; }); function getTestStream() { const writable = new Stream.PassThrough(); const readable = new ReadableStream({ start(controller) { writable.on('data', chunk => { controller.enqueue(chunk); }); writable.on('end', () => { controller.close(); }); }, }); return { readable, writable, }; } it('should resolve HTML using Node streams', async () => { function Text({children}) { return {children}; } function HTML() { return (
hello world
); } function App() { const model = { html: , }; return model; } const {writable, readable} = getTestStream(); const {pipe} = await serverAct(() => ReactServerDOMServer.renderToPipeableStream(, turbopackMap), ); pipe(writable); const response = ReactServerDOMClient.createFromReadableStream(readable); const model = await response; expect(model).toEqual({ html: (
hello world
), }); }); it('should resolve the root', async () => { // Model function Text({children}) { return {children}; } function HTML() { return (
hello world
); } function RootModel() { return { html: , }; } // View function Message({response}) { return
{use(response).html}
; } function App({response}) { return ( Loading...}> ); } const {writable, readable} = getTestStream(); const {pipe} = await serverAct(() => ReactServerDOMServer.renderToPipeableStream(, turbopackMap), ); pipe(writable); const response = ReactServerDOMClient.createFromReadableStream(readable); const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(); }); expect(container.innerHTML).toBe( '
helloworld
', ); }); it('should unwrap async module references', async () => { const AsyncModule = Promise.resolve(function AsyncModule({text}) { return 'Async: ' + text; }); const AsyncModule2 = Promise.resolve({ exportName: 'Module', }); function Print({response}) { return

{use(response)}

; } function App({response}) { return ( Loading...}> ); } const AsyncModuleRef = await clientExports(AsyncModule); const AsyncModuleRef2 = await clientExports(AsyncModule2); const {writable, readable} = getTestStream(); const {pipe} = await serverAct(() => ReactServerDOMServer.renderToPipeableStream( , turbopackMap, ), ); pipe(writable); const response = ReactServerDOMClient.createFromReadableStream(readable); const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(); }); expect(container.innerHTML).toBe('

Async: Module

'); }); it('should unwrap async ESM module references', async () => { const AsyncModule = Promise.resolve(function AsyncModule({text}) { return 'Async: ' + text; }); const AsyncModule2 = Promise.resolve({ exportName: 'Module', }); function Print({response}) { return

{use(response)}

; } function App({response}) { return ( Loading...}> ); } const AsyncModuleRef = await clientExportsESM(AsyncModule); const AsyncModuleRef2 = await clientExportsESM(AsyncModule2); const {writable, readable} = getTestStream(); const {pipe} = await serverAct(() => ReactServerDOMServer.renderToPipeableStream( , turbopackMap, ), ); pipe(writable); const response = ReactServerDOMClient.createFromReadableStream(readable); const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(); }); expect(container.innerHTML).toBe('

Async: Module

'); }); it('should error when a bundler uses async ESM modules with createClientModuleProxy', async () => { const AsyncModule = Promise.resolve(function AsyncModule() { return 'This should not be rendered'; }); function Print({response}) { return

{use(response)}

; } function App({response}) { return ( (

{__DEV__ ? error.message + ' + ' : null} {error.digest}

)}> Loading...}>
); } const AsyncModuleRef = await clientExportsESM(AsyncModule, { forceClientModuleProxy: true, }); const {writable, readable} = getTestStream(); const {pipe} = await serverAct(() => ReactServerDOMServer.renderToPipeableStream( , turbopackMap, { onError(error) { return __DEV__ ? 'a dev digest' : `digest(${error.message})`; }, }, ), ); pipe(writable); const response = ReactServerDOMClient.createFromReadableStream(readable); const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(); }); const errorMessage = `The module "${Object.keys(turbopackMap).at(0)}" is marked as an async ESM module but was loaded as a CJS proxy. This is probably a bug in the React Server Components bundler.`; expect(container.innerHTML).toBe( __DEV__ ? `

${errorMessage} + a dev digest

` : `

digest(${errorMessage})

`, ); }); });