diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationCheckbox-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationCheckbox-test.js index 606ec88b89893..178ed7982a44f 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationCheckbox-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationCheckbox-test.js @@ -15,7 +15,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio const {disableInputAttributeSyncing} = require('shared/ReactFeatureFlags'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -23,13 +23,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationClassContextType-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationClassContextType-test.js index dda475b7ced3e..2df2d66b9b9fa 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationClassContextType-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationClassContextType-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -21,13 +21,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index c482010d39647..3bbdd379813e1 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -16,19 +16,23 @@ const TEXT_NODE_TYPE = 3; let React; let ReactDOM; +let ReactDOMClient; let ReactDOMServer; +let ReactFeatureFlags; let ReactTestUtils; function initModules() { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); + ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; @@ -136,7 +140,13 @@ describe('ReactDOMServerIntegration', () => { // DOM nodes on the client side. We force it to fire early // so that it gets deduplicated later, and doesn't fail the test. expect(() => { - ReactDOM.render(, document.createElement('div')); + ReactDOM.flushSync(() => { + const root = ReactDOMClient.createRoot( + document.createElement('div'), + ); + + root.render(); + }); }).toErrorDev('The tag is unrecognized in this browser.'); const e = await render(Text); @@ -833,15 +843,21 @@ describe('ReactDOMServerIntegration', () => { 'an element with one text child with special characters', async render => { const e = await render(
{'foo\rbar\r\nbaz\nqux\u0000'}
); - if (render === serverRender || render === streamRender) { + if ( + render === serverRender || + render === streamRender || + (render === clientRenderOnServerString && + ReactFeatureFlags.enableClientRenderFallbackOnTextMismatch) + ) { expect(e.childNodes.length).toBe(1); - // Everything becomes LF when parsed from server HTML. + // Everything becomes LF when parsed from server HTML or hydrated if enableClientRenderFallbackOnTextMismatch is on. // Null character is ignored. expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar\nbaz\nqux'); } else { expect(e.childNodes.length).toBe(1); - // Client rendering (or hydration) uses JS value with CR. + // Client rendering (or hydration without enableClientRenderFallbackOnTextMismatch) uses JS value with CR. // Null character stays. + expectNode( e.childNodes[0], TEXT_NODE_TYPE, @@ -860,17 +876,23 @@ describe('ReactDOMServerIntegration', () => { {'\r\nbaz\nqux\u0000'} , ); - if (render === serverRender || render === streamRender) { + if ( + render === serverRender || + render === streamRender || + (render === clientRenderOnServerString && + ReactFeatureFlags.enableClientRenderFallbackOnTextMismatch) + ) { // We have three nodes because there is a comment between them. expect(e.childNodes.length).toBe(3); - // Everything becomes LF when parsed from server HTML. + // Everything becomes LF when parsed from server HTML or hydrated if enableClientRenderFallbackOnTextMismatch is on. // Null character is ignored. expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar'); expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\nbaz\nqux'); } else if (render === clientRenderOnServerString) { // We have three nodes because there is a comment between them. expect(e.childNodes.length).toBe(3); - // Hydration uses JS value with CR and null character. + // Hydration without enableClientRenderFallbackOnTextMismatch uses JS value with CR and null character. + expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar'); expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\r\nbaz\nqux\u0000'); } else { diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationFragment-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationFragment-test.js index 680f283b6dbf2..8e8fc2aa8fe27 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationFragment-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationFragment-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -21,13 +21,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationInput-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationInput-test.js index afbbf28a41ecb..54780dae52cdb 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationInput-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationInput-test.js @@ -15,7 +15,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio const {disableInputAttributeSyncing} = require('shared/ReactFeatureFlags'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -23,13 +23,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationModes-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationModes-test.js index e551a72b9ace4..99cf33b821f17 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationModes-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationModes-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -21,13 +21,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js index 76da3e92c82ad..e5564d3d9348c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js @@ -12,7 +12,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -20,13 +20,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js index f3a8b869ad818..8ea1c9d53baee 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; let forwardRef; @@ -26,7 +26,7 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); forwardRef = React.forwardRef; @@ -44,7 +44,7 @@ function initModules() { // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationTextarea-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationTextarea-test.js index 697ec7f340d88..dd19385e62c56 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationTextarea-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationTextarea-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -21,13 +21,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationUserInteraction-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationUserInteraction-test.js index b335b03b01d38..bc5980f23dda2 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationUserInteraction-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationUserInteraction-test.js @@ -12,7 +12,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -20,13 +20,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/ReactDOMserverIntegrationProgress-test.js b/packages/react-dom/src/__tests__/ReactDOMserverIntegrationProgress-test.js index b949e0ab522f9..cf51eff4aced3 100644 --- a/packages/react-dom/src/__tests__/ReactDOMserverIntegrationProgress-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMserverIntegrationProgress-test.js @@ -13,7 +13,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); let React; -let ReactDOM; +let ReactDOMClient; let ReactDOMServer; let ReactTestUtils; @@ -21,13 +21,13 @@ function initModules() { // Reset warning cache. jest.resetModules(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); // Make them available to the helpers. return { - ReactDOM, + ReactDOMClient, ReactDOMServer, ReactTestUtils, }; diff --git a/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js b/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js index 8392b57af03cf..e9c4479b028fb 100644 --- a/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js +++ b/packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js @@ -14,11 +14,12 @@ const shouldIgnoreConsoleError = require('../../../../../scripts/jest/shouldIgno module.exports = function (initModules) { let ReactDOM; + let ReactDOMClient; let ReactDOMServer; let act; function resetModules() { - ({ReactDOM, ReactDOMServer} = initModules()); + ({ReactDOM, ReactDOMClient, ReactDOMServer} = initModules()); act = require('internal-test-utils').act; } @@ -51,11 +52,24 @@ module.exports = function (initModules) { async function asyncReactDOMRender(reactElement, domElement, forceHydrate) { if (forceHydrate) { await act(() => { - ReactDOM.hydrate(reactElement, domElement); + if (ReactDOMClient) { + ReactDOMClient.hydrateRoot(domElement, reactElement, { + onRecoverableError: () => { + // TODO: assert on recoverable error count. + }, + }); + } else { + ReactDOM.hydrate(reactElement, domElement); + } }); } else { await act(() => { - ReactDOM.render(reactElement, domElement); + if (ReactDOMClient) { + const root = ReactDOMClient.createRoot(domElement); + root.render(reactElement); + } else { + ReactDOM.render(reactElement, domElement); + } }); } } @@ -80,7 +94,11 @@ module.exports = function (initModules) { for (let i = 0; i < console.error.mock.calls.length; i++) { const args = console.error.mock.calls[i]; const [format, ...rest] = args; - if (!shouldIgnoreConsoleError(format, rest)) { + if ( + !shouldIgnoreConsoleError(format, rest, { + TODO_ignoreHydrationErrors: true, + }) + ) { filteredWarnings.push(args); } } diff --git a/scripts/jest/shouldIgnoreConsoleError.js b/scripts/jest/shouldIgnoreConsoleError.js index 42aae220debed..79ec7fc2ad7d6 100644 --- a/scripts/jest/shouldIgnoreConsoleError.js +++ b/scripts/jest/shouldIgnoreConsoleError.js @@ -1,6 +1,10 @@ 'use strict'; -module.exports = function shouldIgnoreConsoleError(format, args) { +module.exports = function shouldIgnoreConsoleError( + format, + args, + {TODO_ignoreHydrationErrors} = {TODO_ignoreHydrationErrors: false} +) { if (__DEV__) { if (typeof format === 'string') { if (format.indexOf('Error: Uncaught [') === 0) { @@ -23,6 +27,15 @@ module.exports = function shouldIgnoreConsoleError(format, args) { // We haven't finished migrating our tests to use createRoot. return true; } + if ( + TODO_ignoreHydrationErrors && + format.indexOf( + 'An error occurred during hydration. The server HTML was replaced with client content in' + ) !== -1 + ) { + // This also gets logged by onRecoverableError, so we can ignore it. + return true; + } } else if ( format != null && typeof format.message === 'string' &&