diff --git a/packages/react-devtools-shared/src/__tests__/utils.js b/packages/react-devtools-shared/src/__tests__/utils.js index 50798c3ba70b2..8555302507ff5 100644 --- a/packages/react-devtools-shared/src/__tests__/utils.js +++ b/packages/react-devtools-shared/src/__tests__/utils.js @@ -15,6 +15,7 @@ import type {FrontendBridge} from 'react-devtools-shared/src/bridge'; import type Store from 'react-devtools-shared/src/devtools/store'; import type {ProfilingDataFrontend} from 'react-devtools-shared/src/devtools/views/Profiler/types'; import type {ElementType} from 'react-devtools-shared/src/frontend/types'; +import type {Node as ReactNode} from 'react'; import {ReactVersion} from '../../../../ReactVersions'; @@ -99,6 +100,123 @@ export async function actAsync( } } +type RenderImplementation = { + render: (elements: ?ReactNode) => () => void, + unmount: () => void, + createContainer: () => void, + getContainer: () => ?HTMLElement, +}; + +export function getLegacyRenderImplementation(): RenderImplementation { + let ReactDOM; + let container; + const containersToRemove = []; + + beforeEach(() => { + ReactDOM = require('react-dom'); + + createContainer(); + }); + + afterEach(() => { + containersToRemove.forEach(c => document.body.removeChild(c)); + containersToRemove.splice(0, containersToRemove.length); + + ReactDOM = null; + container = null; + }); + + function render(elements) { + withErrorsOrWarningsIgnored( + ['ReactDOM.render is no longer supported in React 18'], + () => { + ReactDOM.render(elements, container); + }, + ); + + return unmount; + } + + function unmount() { + ReactDOM.unmountComponentAtNode(container); + } + + function createContainer() { + container = document.createElement('div'); + document.body.appendChild(container); + + containersToRemove.push(container); + } + + function getContainer() { + return container; + } + + return { + render, + unmount, + createContainer, + getContainer, + }; +} + +export function getModernRenderImplementation(): RenderImplementation { + let ReactDOMClient; + let container; + let root; + const containersToRemove = []; + + beforeEach(() => { + ReactDOMClient = require('react-dom/client'); + + createContainer(); + }); + + afterEach(() => { + containersToRemove.forEach(c => document.body.removeChild(c)); + containersToRemove.splice(0, containersToRemove.length); + + ReactDOMClient = null; + container = null; + root = null; + }); + + function render(elements) { + root.render(elements); + + return unmount; + } + + function unmount() { + root.unmount(); + } + + function createContainer() { + container = document.createElement('div'); + document.body.appendChild(container); + + root = ReactDOMClient.createRoot(container); + + containersToRemove.push(container); + } + + function getContainer() { + return container; + } + + return { + render, + unmount, + createContainer, + getContainer, + }; +} + +export const getVersionedRenderImplementation: () => RenderImplementation = + semver.lt(requestedReactVersion, '18.0.0') + ? getLegacyRenderImplementation + : getModernRenderImplementation; + export function beforeEachProfiling(): void { // Mock React's timing information so that test runs are predictable. jest.mock('scheduler', () => jest.requireActual('scheduler/unstable_mock'));