/** * 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 * @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment */ 'use strict'; const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; let ReactDOMClient; let ReactDOMServer; function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); // Make them available to the helpers. return { ReactDOMClient, ReactDOMServer, }; } const {resetModules, itRenders} = ReactDOMServerIntegrationUtils(initModules); describe('ReactDOMServerIntegration', () => { beforeEach(() => { resetModules(); }); describe('context', function () { let Context, PurpleContextProvider, RedContextProvider, Consumer; beforeEach(() => { Context = React.createContext('none'); class Parent extends React.Component { render() { return ( {this.props.children} ); } } Consumer = Context.Consumer; PurpleContextProvider = props => ( {props.children} ); RedContextProvider = props => ( {props.children} ); }); itRenders('class child with context', async render => { class ClassChildWithContext extends React.Component { render() { return (
{text => text}
); } } const e = await render( , ); expect(e.textContent).toBe('purple'); }); itRenders('stateless child with context', async render => { function FunctionChildWithContext(props) { return {text => text}; } const e = await render( , ); expect(e.textContent).toBe('purple'); }); itRenders('class child with default context', async render => { class ClassChildWithWrongContext extends React.Component { render() { return (
{text => text}
); } } const e = await render(); expect(e.textContent).toBe('none'); }); itRenders('stateless child with wrong context', async render => { function FunctionChildWithWrongContext(props) { return (
{text => text}
); } const e = await render(); expect(e.textContent).toBe('none'); }); itRenders('with context passed through to a grandchild', async render => { function Grandchild(props) { return (
{text => text}
); } const Child = props => ; const e = await render( , ); expect(e.textContent).toBe('purple'); }); itRenders('a child context overriding a parent context', async render => { const Grandchild = props => { return (
{text => text}
); }; const e = await render( , ); expect(e.textContent).toBe('red'); }); itRenders('readContext() in different components', async render => { function readContext(Ctx) { const dispatcher = React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .H; return dispatcher.readContext(Ctx); } class Cls extends React.Component { render() { return readContext(Context); } } function Fn() { return readContext(Context); } const Memo = React.memo(() => { return readContext(Context); }); const FwdRef = React.forwardRef((props, ref) => { return readContext(Context); }); const e = await render( {() => readContext(Context)} , ); expect(e.textContent).toBe('redredredredred'); }); itRenders('multiple contexts', async render => { const Theme = React.createContext('dark'); const Language = React.createContext('french'); class Parent extends React.Component { render() { return ( ); } } function Child() { return ( ); } const Grandchild = props => { return (
{theme =>
{theme}
}
{language =>
{language}
}
); }; const e = await render(); expect(e.querySelector('#theme').textContent).toBe('light'); expect(e.querySelector('#language').textContent).toBe('english'); }); itRenders('nested context unwinding', async render => { const Theme = React.createContext('dark'); const Language = React.createContext('french'); const App = () => (
{theme =>
{theme}
}
{theme =>
{theme}
}
{() => ( {language =>
{language}
}
)}
{language => ( <> {theme =>
{theme}
}
{language}
)}
{language =>
{language}
}
); const e = await render(); expect(e.querySelector('#theme1').textContent).toBe('dark'); expect(e.querySelector('#theme2').textContent).toBe('light'); expect(e.querySelector('#theme3').textContent).toBe('blue'); expect(e.querySelector('#language1').textContent).toBe('chinese'); expect(e.querySelector('#language2').textContent).toBe('sanskrit'); expect(e.querySelector('#language3').textContent).toBe('french'); }); itRenders('should treat Context as Context.Provider', async render => { const Theme = React.createContext('dark'); const Language = React.createContext('french'); expect(Theme.Provider).toBe(Theme); const App = () => (
{theme =>
{theme}
}
); const e = await render(, 0); expect(e.textContent).toBe('dark'); }); it('does not pollute sync renders after an error', () => { const LoggedInUser = React.createContext('default'); const Crash = () => { throw new Error('Boo!'); }; const AppWithUser = user => ( {whoAmI => whoAmI} ); expect(() => { ReactDOMServer.renderToString(AppWithUser('Casper')); }).toThrow('Boo'); // Should not report a value from failed render expect( ReactDOMServer.renderToString( {whoAmI => whoAmI}, ), ).toBe('default'); }); }); });