From 0e476189aa902cd9d890f6139d0f4debca44eb06 Mon Sep 17 00:00:00 2001
From: Josh Story
Date: Thu, 20 Apr 2023 14:27:02 -0700
Subject: [PATCH] [Tests][Fizz] Better HTML parsing behavior for Fizz tests
(#26570)
In anticipation of making Fiber use the document global for dispatching
Float methods that arrive from Flight I needed to update some tests that
commonly recreated the JSDOM instance after importing react.
This change updates a few tests to only create JSDOM once per test,
before importing react-dom/client.
Additionally the current act implementation for server streaming did not
adequately model streaming semantics so I rewrite the act implementation
in a way that better mirrors how a browser would parse incoming HTML.
The new act implementation does the following
1. the first time it processes meaningful streamed content it figures
out whether it is rendering into the existing document container or if
it needs to reset the document. this is based on whether the streamed
content contains tags `` or `` etc...
2. Once the streaming container is set it will typically continue to
stream into that container for future calls to act. The exception is if
the streaming container is the `` in which case it will switch to
streaming into the body once it receives a `` tag.
This means for tests that render something like a `...
` it
will naturally stream into the default `...` and for
tests that render a full document the HTML will parse like a real
browser would (with some very minor edge case differences)
I also refactored the way we move nodes from buffered content into the
document and execute any scripts we find. Previously we were using
window.eval and I switched this to just setting the external script
content as script text. Additionally the nonce logic is reworked to be a
bit simpler.
---
.../src/__tests__/ReactDOMFizzServer-test.js | 835 ++++++++++--------
.../src/__tests__/ReactDOMFloat-test.js | 343 ++++---
.../react-dom/src/test-utils/FizzTestUtils.js | 140 +--
3 files changed, 759 insertions(+), 559 deletions(-)
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index da59ac66faef5..aa9222adb0766 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -10,7 +10,7 @@
'use strict';
import {
- replaceScriptsAndMove,
+ insertNodesAndExecuteScripts,
mergeOptions,
stripExternalRuntimeInNodes,
withLoadingReadyState,
@@ -29,8 +29,6 @@ let useSyncExternalStoreWithSelector;
let use;
let PropTypes;
let textCache;
-let window;
-let document;
let writable;
let CSPnonce = null;
let container;
@@ -43,20 +41,32 @@ let waitForAll;
let assertLog;
let waitForPaint;
let clientAct;
-
-function resetJSDOM(markup) {
- // Test Environment
- const jsdom = new JSDOM(markup, {
- runScripts: 'dangerously',
- });
- window = jsdom.window;
- document = jsdom.window.document;
-}
+let streamingContainer;
describe('ReactDOMFizzServer', () => {
beforeEach(() => {
jest.resetModules();
JSDOM = require('jsdom').JSDOM;
+
+ const jsdom = new JSDOM(
+ '
',
+ {
+ runScripts: 'dangerously',
+ },
+ );
+ // We mock matchMedia. for simplicity it only matches 'all' or '' and misses everything else
+ Object.defineProperty(jsdom.window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: query === 'all' || query === '',
+ media: query,
+ })),
+ });
+ streamingContainer = null;
+ global.window = jsdom.window;
+ global.document = jsdom.window.document;
+ container = document.getElementById('container');
+
Scheduler = require('scheduler');
React = require('react');
ReactDOMClient = require('react-dom/client');
@@ -93,9 +103,6 @@ describe('ReactDOMFizzServer', () => {
textCache = new Map();
- resetJSDOM('
');
- container = document.getElementById('container');
-
buffer = '';
hasErrored = false;
@@ -140,6 +147,9 @@ describe('ReactDOMFizzServer', () => {
.join('');
}
+ const bodyStartMatch = /| .*?>)/;
+ const headStartMatch = /| .*?>)/;
+
async function act(callback) {
await callback();
// Await one turn around the event loop.
@@ -153,40 +163,123 @@ describe('ReactDOMFizzServer', () => {
// JSDOM doesn't support stream HTML parser so we need to give it a proper fragment.
// We also want to execute any scripts that are embedded.
// We assume that we have now received a proper fragment of HTML.
- const bufferedContent = buffer;
+ let bufferedContent = buffer;
buffer = '';
- const fakeBody = document.createElement('body');
- fakeBody.innerHTML = bufferedContent;
- const parent =
- container.nodeName === '#document' ? container.body : container;
- await withLoadingReadyState(async () => {
- while (fakeBody.firstChild) {
- const node = fakeBody.firstChild;
- await replaceScriptsAndMove(window, CSPnonce, node, parent);
- }
- }, document);
- }
-
- async function actIntoEmptyDocument(callback) {
- await callback();
- // Await one turn around the event loop.
- // This assumes that we'll flush everything we have so far.
- await new Promise(resolve => {
- setImmediate(resolve);
- });
- if (hasErrored) {
- throw fatalError;
+ if (!bufferedContent) {
+ return;
}
- // JSDOM doesn't support stream HTML parser so we need to give it a proper fragment.
- // We also want to execute any scripts that are embedded.
- // We assume that we have now received a proper fragment of HTML.
- const bufferedContent = buffer;
- resetJSDOM(bufferedContent);
- container = document;
- buffer = '';
+
await withLoadingReadyState(async () => {
- await replaceScriptsAndMove(window, CSPnonce, document.documentElement);
+ const bodyMatch = bufferedContent.match(bodyStartMatch);
+ const headMatch = bufferedContent.match(headStartMatch);
+
+ if (streamingContainer === null) {
+ // This is the first streamed content. We decide here where to insert it. If we get , , or
+ // we abandon the pre-built document and start from scratch. If we get anything else we assume it goes into the
+ // container. This is not really production behavior because you can't correctly stream into a deep div effectively
+ // but it's pragmatic for tests.
+
+ if (
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith(' without a which is almost certainly a bug in React',
+ );
+ }
+
+ if (bufferedContent.startsWith('')) {
+ // we can just use the whole document
+ const tempDom = new JSDOM(bufferedContent);
+
+ // Wipe existing head and body content
+ document.head.innerHTML = '';
+ document.body.innerHTML = '';
+
+ // Copy the attributes over
+ const tempHtmlNode = tempDom.window.document.documentElement;
+ for (let i = 0; i < tempHtmlNode.attributes.length; i++) {
+ const attr = tempHtmlNode.attributes[i];
+ document.documentElement.setAttribute(attr.name, attr.value);
+ }
+
+ if (headMatch) {
+ // We parsed a head open tag. we need to copy head attributes and insert future
+ // content into
+ streamingContainer = document.head;
+ const tempHeadNode = tempDom.window.document.head;
+ for (let i = 0; i < tempHeadNode.attributes.length; i++) {
+ const attr = tempHeadNode.attributes[i];
+ document.head.setAttribute(attr.name, attr.value);
+ }
+ const source = document.createElement('head');
+ source.innerHTML = tempHeadNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.head, CSPnonce);
+ }
+
+ if (bodyMatch) {
+ // We parsed a body open tag. we need to copy head attributes and insert future
+ // content into
+ streamingContainer = document.body;
+ const tempBodyNode = tempDom.window.document.body;
+ for (let i = 0; i < tempBodyNode.attributes.length; i++) {
+ const attr = tempBodyNode.attributes[i];
+ document.body.setAttribute(attr.name, attr.value);
+ }
+ const source = document.createElement('body');
+ source.innerHTML = tempBodyNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.body, CSPnonce);
+ }
+
+ if (!headMatch && !bodyMatch) {
+ throw new Error('expected or after ');
+ }
+ } else {
+ // we assume we are streaming into the default container'
+ streamingContainer = container;
+ const div = document.createElement('div');
+ div.innerHTML = bufferedContent;
+ await insertNodesAndExecuteScripts(div, container, CSPnonce);
+ }
+ } else if (streamingContainer === document.head) {
+ bufferedContent = '' + bufferedContent;
+ const tempDom = new JSDOM(bufferedContent);
+
+ const tempHeadNode = tempDom.window.document.head;
+ const source = document.createElement('head');
+ source.innerHTML = tempHeadNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.head, CSPnonce);
+
+ if (bodyMatch) {
+ streamingContainer = document.body;
+
+ const tempBodyNode = tempDom.window.document.body;
+ for (let i = 0; i < tempBodyNode.attributes.length; i++) {
+ const attr = tempBodyNode.attributes[i];
+ document.body.setAttribute(attr.name, attr.value);
+ }
+ const bodySource = document.createElement('body');
+ bodySource.innerHTML = tempBodyNode.innerHTML;
+ await insertNodesAndExecuteScripts(
+ bodySource,
+ document.body,
+ CSPnonce,
+ );
+ }
+ } else {
+ const div = document.createElement('div');
+ div.innerHTML = bufferedContent;
+ await insertNodesAndExecuteScripts(div, streamingContainer, CSPnonce);
+ }
}, document);
}
@@ -3467,7 +3560,7 @@ describe('ReactDOMFizzServer', () => {
});
it('accepts an integrity property for bootstrapScripts and bootstrapModules', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -3584,7 +3677,7 @@ describe('ReactDOMFizzServer', () => {
// @gate enableFizzExternalRuntime
it('supports option to load runtime as an external script', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -3631,7 +3724,7 @@ describe('ReactDOMFizzServer', () => {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -3644,7 +3737,7 @@ describe('ReactDOMFizzServer', () => {
});
it('does not send the external runtime for static pages', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4446,7 +4539,7 @@ describe('ReactDOMFizzServer', () => {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4456,17 +4549,13 @@ describe('ReactDOMFizzServer', () => {
);
pipe(writable);
});
- await actIntoEmptyDocument(() => {
+ await act(() => {
resolveText('body');
});
- await actIntoEmptyDocument(() => {
+ await act(() => {
resolveText('nooutput');
});
- // We need to use actIntoEmptyDocument because act assumes that buffered
- // content should be fake streamed into the body which is normally true
- // but in this test the entire shell was delayed and we need the initial
- // construction to be done to get the parsing right
- await actIntoEmptyDocument(() => {
+ await act(() => {
resolveText('head');
});
expect(getVisibleChildren(document)).toEqual(
@@ -4487,7 +4576,7 @@ describe('ReactDOMFizzServer', () => {
chunks.push(chunk);
});
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -5583,7 +5670,7 @@ describe('ReactDOMFizzServer', () => {
};
try {
- await actIntoEmptyDocument(async () => {
+ await act(async () => {
const {pipe} = renderToPipeableStream(
diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
index 28fb79c1a75d5..728b91564c1e2 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
@@ -10,7 +10,7 @@
'use strict';
import {
- replaceScriptsAndMove,
+ insertNodesAndExecuteScripts,
mergeOptions,
withLoadingReadyState,
} from '../test-utils/FizzTestUtils';
@@ -24,8 +24,6 @@ let ReactDOMFizzServer;
let Suspense;
let textCache;
let loadCache;
-let window;
-let document;
let writable;
const CSPnonce = null;
let container;
@@ -38,28 +36,32 @@ let waitForThrow;
let assertLog;
let Scheduler;
let clientAct;
-
-function resetJSDOM(markup) {
- // Test Environment
- const jsdom = new JSDOM(markup, {
- runScripts: 'dangerously',
- });
- // We mock matchMedia. for simplicity it only matches 'all' or '' and misses everything else
- Object.defineProperty(jsdom.window, 'matchMedia', {
- writable: true,
- value: jest.fn().mockImplementation(query => ({
- matches: query === 'all' || query === '',
- media: query,
- })),
- });
- window = jsdom.window;
- document = jsdom.window.document;
-}
+let streamingContainer;
describe('ReactDOMFloat', () => {
beforeEach(() => {
jest.resetModules();
JSDOM = require('jsdom').JSDOM;
+
+ const jsdom = new JSDOM(
+ '',
+ {
+ runScripts: 'dangerously',
+ },
+ );
+ // We mock matchMedia. for simplicity it only matches 'all' or '' and misses everything else
+ Object.defineProperty(jsdom.window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: query === 'all' || query === '',
+ media: query,
+ })),
+ });
+ streamingContainer = null;
+ global.window = jsdom.window;
+ global.document = jsdom.window.document;
+ container = document.getElementById('container');
+
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
@@ -77,9 +79,6 @@ describe('ReactDOMFloat', () => {
textCache = new Map();
loadCache = new Set();
- resetJSDOM('
');
- container = document.getElementById('container');
-
buffer = '';
hasErrored = false;
@@ -100,6 +99,9 @@ describe('ReactDOMFloat', () => {
}
});
+ const bodyStartMatch = /| .*?>)/;
+ const headStartMatch = /| .*?>)/;
+
async function act(callback) {
await callback();
// Await one turn around the event loop.
@@ -113,44 +115,123 @@ describe('ReactDOMFloat', () => {
// JSDOM doesn't support stream HTML parser so we need to give it a proper fragment.
// We also want to execute any scripts that are embedded.
// We assume that we have now received a proper fragment of HTML.
- const bufferedContent = buffer;
+ let bufferedContent = buffer;
buffer = '';
- const fakeBody = document.createElement('body');
- fakeBody.innerHTML = bufferedContent;
- const parent =
- container.nodeName === '#document' ? container.body : container;
- await withLoadingReadyState(async () => {
- while (fakeBody.firstChild) {
- const node = fakeBody.firstChild;
- await replaceScriptsAndMove(
- document.defaultView,
- CSPnonce,
- node,
- parent,
- );
- }
- }, document);
- }
- async function actIntoEmptyDocument(callback) {
- await callback();
- // Await one turn around the event loop.
- // This assumes that we'll flush everything we have so far.
- await new Promise(resolve => {
- setImmediate(resolve);
- });
- if (hasErrored) {
- throw fatalError;
+ if (!bufferedContent) {
+ return;
}
- // JSDOM doesn't support stream HTML parser so we need to give it a proper fragment.
- // We also want to execute any scripts that are embedded.
- // We assume that we have now received a proper fragment of HTML.
- const bufferedContent = buffer;
- resetJSDOM(bufferedContent);
- container = document;
- buffer = '';
+
await withLoadingReadyState(async () => {
- await replaceScriptsAndMove(window, null, document.documentElement);
+ const bodyMatch = bufferedContent.match(bodyStartMatch);
+ const headMatch = bufferedContent.match(headStartMatch);
+
+ if (streamingContainer === null) {
+ // This is the first streamed content. We decide here where to insert it. If we get , , or
+ // we abandon the pre-built document and start from scratch. If we get anything else we assume it goes into the
+ // container. This is not really production behavior because you can't correctly stream into a deep div effectively
+ // but it's pragmatic for tests.
+
+ if (
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith('') ||
+ bufferedContent.startsWith(' without a which is almost certainly a bug in React',
+ );
+ }
+
+ if (bufferedContent.startsWith('')) {
+ // we can just use the whole document
+ const tempDom = new JSDOM(bufferedContent);
+
+ // Wipe existing head and body content
+ document.head.innerHTML = '';
+ document.body.innerHTML = '';
+
+ // Copy the attributes over
+ const tempHtmlNode = tempDom.window.document.documentElement;
+ for (let i = 0; i < tempHtmlNode.attributes.length; i++) {
+ const attr = tempHtmlNode.attributes[i];
+ document.documentElement.setAttribute(attr.name, attr.value);
+ }
+
+ if (headMatch) {
+ // We parsed a head open tag. we need to copy head attributes and insert future
+ // content into
+ streamingContainer = document.head;
+ const tempHeadNode = tempDom.window.document.head;
+ for (let i = 0; i < tempHeadNode.attributes.length; i++) {
+ const attr = tempHeadNode.attributes[i];
+ document.head.setAttribute(attr.name, attr.value);
+ }
+ const source = document.createElement('head');
+ source.innerHTML = tempHeadNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.head, CSPnonce);
+ }
+
+ if (bodyMatch) {
+ // We parsed a body open tag. we need to copy head attributes and insert future
+ // content into
+ streamingContainer = document.body;
+ const tempBodyNode = tempDom.window.document.body;
+ for (let i = 0; i < tempBodyNode.attributes.length; i++) {
+ const attr = tempBodyNode.attributes[i];
+ document.body.setAttribute(attr.name, attr.value);
+ }
+ const source = document.createElement('body');
+ source.innerHTML = tempBodyNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.body, CSPnonce);
+ }
+
+ if (!headMatch && !bodyMatch) {
+ throw new Error('expected or after ');
+ }
+ } else {
+ // we assume we are streaming into the default container'
+ streamingContainer = container;
+ const div = document.createElement('div');
+ div.innerHTML = bufferedContent;
+ await insertNodesAndExecuteScripts(div, container, CSPnonce);
+ }
+ } else if (streamingContainer === document.head) {
+ bufferedContent = '' + bufferedContent;
+ const tempDom = new JSDOM(bufferedContent);
+
+ const tempHeadNode = tempDom.window.document.head;
+ const source = document.createElement('head');
+ source.innerHTML = tempHeadNode.innerHTML;
+ await insertNodesAndExecuteScripts(source, document.head, CSPnonce);
+
+ if (bodyMatch) {
+ streamingContainer = document.body;
+
+ const tempBodyNode = tempDom.window.document.body;
+ for (let i = 0; i < tempBodyNode.attributes.length; i++) {
+ const attr = tempBodyNode.attributes[i];
+ document.body.setAttribute(attr.name, attr.value);
+ }
+ const bodySource = document.createElement('body');
+ bodySource.innerHTML = tempBodyNode.innerHTML;
+ await insertNodesAndExecuteScripts(
+ bodySource,
+ document.body,
+ CSPnonce,
+ );
+ }
+ } else {
+ const div = document.createElement('div');
+ div.innerHTML = bufferedContent;
+ await insertNodesAndExecuteScripts(div, streamingContainer, CSPnonce);
+ }
}, document);
}
@@ -350,7 +431,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can hydrate non Resources in head when Resources are also inserted there', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -375,7 +456,7 @@ describe('ReactDOMFloat', () => {
foo
-
<link rel="icon" href="icon"/>
+
<link rel="icon" href="icon">
foo
@@ -406,7 +487,7 @@ describe('ReactDOMFloat', () => {
foo
-
<link rel="icon" href="icon"/>
+
<link rel="icon" href="icon">
@@ -598,7 +679,7 @@ describe('ReactDOMFloat', () => {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
buffer = `${ReactDOMFizzServer.renderToString(
,
)}foo`;
@@ -625,7 +706,7 @@ describe('ReactDOMFloat', () => {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
buffer = `${ReactDOMFizzServer.renderToString(
,
)}foo`;
@@ -649,7 +730,7 @@ describe('ReactDOMFloat', () => {
chunks.push(chunk);
});
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
<>
foo
@@ -681,7 +762,7 @@ describe('ReactDOMFloat', () => {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -703,7 +784,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can avoid inserting a late stylesheet if it already rendered on the client', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -829,7 +910,7 @@ body {
background-color: red;
}`;
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -1125,7 +1206,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
);
pipe(writable);
});
@@ -1211,7 +1292,7 @@ body {
// @gate enableFloat
it('treats stylesheet links with a precedence as a resource', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -1264,7 +1345,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -2659,7 +2740,7 @@ body {
- <meta name="noscript" content="noscript"/>
+ <meta name="noscript" content="noscript">
@@ -2713,7 +2794,7 @@ body {
});
it('does not preload nomodule scripts', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -2839,7 +2920,7 @@ body {
});
it('assumes stylesheets that load in the shell loaded already', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -3321,7 +3402,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream( ).pipe(writable);
});
}).toErrorDev([
@@ -3390,7 +3471,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream( ).pipe(writable);
});
}).toErrorDev(
@@ -3469,7 +3550,7 @@ body {
return hello
;
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -3555,7 +3636,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -3620,7 +3701,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -3645,7 +3726,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -3689,7 +3770,7 @@ body {
return hello
;
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -3799,7 +3880,7 @@ body {
return hello
;
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -3916,7 +3997,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -3952,7 +4033,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -3977,7 +4058,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -4007,7 +4088,7 @@ body {
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -4025,7 +4106,7 @@ body {
describe('Stylesheet Resources', () => {
// @gate enableFloat
it('treats link rel stylesheet elements as a stylesheet resource when it includes a precedence when server rendering', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4953,23 +5042,21 @@ describe('ReactDOMFizzServer', () => {
});
describe('title children', () => {
- function prepareJSDOMForTitle() {
- resetJSDOM('
\u0000');
- container = document.getElementsByTagName('head')[0];
- }
-
it('should accept a single string child', async () => {
// a Single string child
function App() {
- return
hello ;
+ return (
+
+
hello
+
+ );
}
- prepareJSDOMForTitle();
await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
const errors = [];
ReactDOMClient.hydrateRoot(container, , {
@@ -4979,21 +5066,24 @@ describe('ReactDOMFizzServer', () => {
});
await waitForAll([]);
expect(errors).toEqual([]);
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
});
it('should accept children array of length 1 containing a string', async () => {
// a Single string child
function App() {
- return
{['hello']} ;
+ return (
+
+
{['hello']}
+
+ );
}
- prepareJSDOMForTitle();
await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
const errors = [];
ReactDOMClient.hydrateRoot(container, , {
@@ -5003,16 +5093,18 @@ describe('ReactDOMFizzServer', () => {
});
await waitForAll([]);
expect(errors).toEqual([]);
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
});
it('should warn in dev when given an array of length 2 or more', async () => {
function App() {
- return
{['hello1', 'hello2']} ;
+ return (
+
+
{['hello1', 'hello2']}
+
+ );
}
- prepareJSDOMForTitle();
-
await expect(async () => {
await act(() => {
const {pipe} = renderToPipeableStream( );
@@ -5023,15 +5115,15 @@ describe('ReactDOMFizzServer', () => {
]);
if (gate(flags => flags.enableFloat)) {
- expect(getVisibleChildren(container)).toEqual(
);
+ expect(getVisibleChildren(document.head)).toEqual(
);
} else {
- expect(getVisibleChildren(container)).toEqual(
+ expect(getVisibleChildren(document.head)).toEqual(
{'hello1hello2'} ,
);
}
const errors = [];
- ReactDOMClient.hydrateRoot(container, , {
+ ReactDOMClient.hydrateRoot(document.head, , {
onRecoverableError(error) {
errors.push(error.message);
},
@@ -5040,7 +5132,7 @@ describe('ReactDOMFizzServer', () => {
if (gate(flags => flags.enableFloat)) {
expect(errors).toEqual([]);
// with float, the title doesn't render on the client or on the server
- expect(getVisibleChildren(container)).toEqual(
);
+ expect(getVisibleChildren(document.head)).toEqual(
);
} else {
expect(errors).toEqual(
[
@@ -5051,7 +5143,7 @@ describe('ReactDOMFizzServer', () => {
'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
].filter(Boolean),
);
- expect(getVisibleChildren(container)).toEqual(
+ expect(getVisibleChildren(document.head)).toEqual(
{['hello1', 'hello2']} ,
);
}
@@ -5064,16 +5156,14 @@ describe('ReactDOMFizzServer', () => {
function App() {
return (
- <>
+
- >
+
);
}
- prepareJSDOMForTitle();
-
if (gate(flags => flags.enableFloat)) {
await expect(async () => {
await act(() => {
@@ -5096,15 +5186,15 @@ describe('ReactDOMFizzServer', () => {
if (gate(flags => flags.enableFloat)) {
// object titles are toStringed when float is on
- expect(getVisibleChildren(container)).toEqual(
+ expect(getVisibleChildren(document.head)).toEqual(
{'[object Object]'} ,
);
} else {
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
}
const errors = [];
- ReactDOMClient.hydrateRoot(container, , {
+ ReactDOMClient.hydrateRoot(document.head, , {
onRecoverableError(error) {
errors.push(error.message);
},
@@ -5113,344 +5203,341 @@ describe('ReactDOMFizzServer', () => {
expect(errors).toEqual([]);
if (gate(flags => flags.enableFloat)) {
// object titles are toStringed when float is on
- expect(getVisibleChildren(container)).toEqual(
+ expect(getVisibleChildren(document.head)).toEqual(
{'[object Object]'} ,
);
} else {
- expect(getVisibleChildren(container)).toEqual(
hello );
+ expect(getVisibleChildren(document.head)).toEqual(
hello );
}
});
+ });
- // @gate enableUseHook
- it('basic use(promise)', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.resolve('B');
- const promiseC = Promise.resolve('C');
+ // @gate enableUseHook
+ it('basic use(promise)', async () => {
+ const promiseA = Promise.resolve('A');
+ const promiseB = Promise.resolve('B');
+ const promiseC = Promise.resolve('C');
- function Async() {
- return use(promiseA) + use(promiseB) + use(promiseC);
- }
+ function Async() {
+ return use(promiseA) + use(promiseB) + use(promiseC);
+ }
- function App() {
- return (
-
-
-
- );
- }
+ function App() {
+ return (
+
+
+
+ );
+ }
- await act(() => {
- const {pipe} = renderToPipeableStream( );
- pipe(writable);
- });
+ await act(() => {
+ const {pipe} = renderToPipeableStream( );
+ pipe(writable);
+ });
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await promiseB;
- });
- await act(async () => {
- await promiseC;
- });
+ // TODO: The `act` implementation in this file doesn't unwrap microtasks
+ // automatically. We can't use the same `act` we use for Fiber tests
+ // because that relies on the mock Scheduler. Doesn't affect any public
+ // API but we might want to fix this for our own internal tests.
+ //
+ // For now, wait for each promise in sequence.
+ await act(async () => {
+ await promiseA;
+ });
+ await act(async () => {
+ await promiseB;
+ });
+ await act(async () => {
+ await promiseC;
+ });
- expect(getVisibleChildren(container)).toEqual('ABC');
+ expect(getVisibleChildren(container)).toEqual('ABC');
- ReactDOMClient.hydrateRoot(container, );
- await waitForAll([]);
- expect(getVisibleChildren(container)).toEqual('ABC');
- });
+ ReactDOMClient.hydrateRoot(container, );
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual('ABC');
+ });
- // @gate enableUseHook
- it('basic use(context)', async () => {
- const ContextA = React.createContext('default');
- const ContextB = React.createContext('B');
- const ServerContext = React.createServerContext(
- 'ServerContext',
- 'default',
+ // @gate enableUseHook
+ it('basic use(context)', async () => {
+ const ContextA = React.createContext('default');
+ const ContextB = React.createContext('B');
+ const ServerContext = React.createServerContext('ServerContext', 'default');
+ function Client() {
+ return use(ContextA) + use(ContextB);
+ }
+ function ServerComponent() {
+ return use(ServerContext);
+ }
+ function Server() {
+ return (
+
+
+
);
- function Client() {
- return use(ContextA) + use(ContextB);
- }
- function ServerComponent() {
- return use(ServerContext);
- }
- function Server() {
- return (
-
-
-
- );
- }
- function App() {
- return (
- <>
-
-
-
-
- >
- );
- }
-
- await act(() => {
- const {pipe} = renderToPipeableStream( );
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
+ }
+ function App() {
+ return (
+ <>
+
+
+
+
+ >
+ );
+ }
- // Hydration uses a different renderer runtime (Fiber instead of Fizz).
- // We reset _currentRenderer here to not trigger a warning about multiple
- // renderers concurrently using these contexts
- ContextA._currentRenderer = null;
- ServerContext._currentRenderer = null;
- ReactDOMClient.hydrateRoot(container, );
- await waitForAll([]);
- expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
+ await act(() => {
+ const {pipe} = renderToPipeableStream( );
+ pipe(writable);
});
+ expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
- // @gate enableUseHook
- it('use(promise) in multiple components', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.resolve('B');
- const promiseC = Promise.resolve('C');
- const promiseD = Promise.resolve('D');
-
- function Child({prefix}) {
- return prefix + use(promiseC) + use(promiseD);
- }
+ // Hydration uses a different renderer runtime (Fiber instead of Fizz).
+ // We reset _currentRenderer here to not trigger a warning about multiple
+ // renderers concurrently using these contexts
+ ContextA._currentRenderer = null;
+ ServerContext._currentRenderer = null;
+ ReactDOMClient.hydrateRoot(container, );
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
+ });
- function Parent() {
- return ;
- }
+ // @gate enableUseHook
+ it('use(promise) in multiple components', async () => {
+ const promiseA = Promise.resolve('A');
+ const promiseB = Promise.resolve('B');
+ const promiseC = Promise.resolve('C');
+ const promiseD = Promise.resolve('D');
- function App() {
- return (
-
-
-
- );
- }
+ function Child({prefix}) {
+ return prefix + use(promiseC) + use(promiseD);
+ }
- await act(() => {
- const {pipe} = renderToPipeableStream( );
- pipe(writable);
- });
+ function Parent() {
+ return ;
+ }
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await promiseB;
- });
- await act(async () => {
- await promiseC;
- });
- await act(async () => {
- await promiseD;
- });
+ function App() {
+ return (
+
+
+
+ );
+ }
- expect(getVisibleChildren(container)).toEqual('ABCD');
+ await act(() => {
+ const {pipe} = renderToPipeableStream( );
+ pipe(writable);
+ });
- ReactDOMClient.hydrateRoot(container, );
- await waitForAll([]);
- expect(getVisibleChildren(container)).toEqual('ABCD');
+ // TODO: The `act` implementation in this file doesn't unwrap microtasks
+ // automatically. We can't use the same `act` we use for Fiber tests
+ // because that relies on the mock Scheduler. Doesn't affect any public
+ // API but we might want to fix this for our own internal tests.
+ //
+ // For now, wait for each promise in sequence.
+ await act(async () => {
+ await promiseA;
+ });
+ await act(async () => {
+ await promiseB;
+ });
+ await act(async () => {
+ await promiseC;
+ });
+ await act(async () => {
+ await promiseD;
});
- // @gate enableUseHook
- it('using a rejected promise will throw', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.reject(new Error('Oops!'));
- const promiseC = Promise.resolve('C');
+ expect(getVisibleChildren(container)).toEqual('ABCD');
- // Jest/Node will raise an unhandled rejected error unless we await this. It
- // works fine in the browser, though.
- await expect(promiseB).rejects.toThrow('Oops!');
+ ReactDOMClient.hydrateRoot(container, );
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual('ABCD');
+ });
- function Async() {
- return use(promiseA) + use(promiseB) + use(promiseC);
- }
+ // @gate enableUseHook
+ it('using a rejected promise will throw', async () => {
+ const promiseA = Promise.resolve('A');
+ const promiseB = Promise.reject(new Error('Oops!'));
+ const promiseC = Promise.resolve('C');
- class ErrorBoundary extends React.Component {
- state = {error: null};
- static getDerivedStateFromError(error) {
- return {error};
- }
- render() {
- if (this.state.error) {
- return this.state.error.message;
- }
- return this.props.children;
+ // Jest/Node will raise an unhandled rejected error unless we await this. It
+ // works fine in the browser, though.
+ await expect(promiseB).rejects.toThrow('Oops!');
+
+ function Async() {
+ return use(promiseA) + use(promiseB) + use(promiseC);
+ }
+
+ class ErrorBoundary extends React.Component {
+ state = {error: null};
+ static getDerivedStateFromError(error) {
+ return {error};
+ }
+ render() {
+ if (this.state.error) {
+ return this.state.error.message;
}
+ return this.props.children;
}
+ }
- function App() {
- return (
-
-
-
-
-
- );
- }
+ function App() {
+ return (
+
+
+
+
+
+ );
+ }
- const reportedServerErrors = [];
- await act(() => {
- const {pipe} = renderToPipeableStream( , {
- onError(error) {
- reportedServerErrors.push(error);
- },
- });
- pipe(writable);
+ const reportedServerErrors = [];
+ await act(() => {
+ const {pipe} = renderToPipeableStream( , {
+ onError(error) {
+ reportedServerErrors.push(error);
+ },
});
+ pipe(writable);
+ });
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await expect(promiseB).rejects.toThrow('Oops!');
- });
- await act(async () => {
- await promiseC;
- });
+ // TODO: The `act` implementation in this file doesn't unwrap microtasks
+ // automatically. We can't use the same `act` we use for Fiber tests
+ // because that relies on the mock Scheduler. Doesn't affect any public
+ // API but we might want to fix this for our own internal tests.
+ //
+ // For now, wait for each promise in sequence.
+ await act(async () => {
+ await promiseA;
+ });
+ await act(async () => {
+ await expect(promiseB).rejects.toThrow('Oops!');
+ });
+ await act(async () => {
+ await promiseC;
+ });
- expect(getVisibleChildren(container)).toEqual('Loading...');
- expect(reportedServerErrors.length).toBe(1);
- expect(reportedServerErrors[0].message).toBe('Oops!');
+ expect(getVisibleChildren(container)).toEqual('Loading...');
+ expect(reportedServerErrors.length).toBe(1);
+ expect(reportedServerErrors[0].message).toBe('Oops!');
- const reportedClientErrors = [];
- ReactDOMClient.hydrateRoot(container, , {
- onRecoverableError(error) {
- reportedClientErrors.push(error);
- },
- });
- await waitForAll([]);
- expect(getVisibleChildren(container)).toEqual('Oops!');
- expect(reportedClientErrors.length).toBe(1);
- if (__DEV__) {
- expect(reportedClientErrors[0].message).toBe('Oops!');
- } else {
- expect(reportedClientErrors[0].message).toBe(
- 'The server could not finish this Suspense boundary, likely due to ' +
- 'an error during server rendering. Switched to client rendering.',
- );
- }
+ const reportedClientErrors = [];
+ ReactDOMClient.hydrateRoot(container, , {
+ onRecoverableError(error) {
+ reportedClientErrors.push(error);
+ },
});
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual('Oops!');
+ expect(reportedClientErrors.length).toBe(1);
+ if (__DEV__) {
+ expect(reportedClientErrors[0].message).toBe('Oops!');
+ } else {
+ expect(reportedClientErrors[0].message).toBe(
+ 'The server could not finish this Suspense boundary, likely due to ' +
+ 'an error during server rendering. Switched to client rendering.',
+ );
+ }
+ });
- // @gate enableUseHook
- it("use a promise that's already been instrumented and resolved", async () => {
- const thenable = {
- status: 'fulfilled',
- value: 'Hi',
- then() {},
- };
-
- // This will never suspend because the thenable already resolved
- function App() {
- return use(thenable);
- }
+ // @gate enableUseHook
+ it("use a promise that's already been instrumented and resolved", async () => {
+ const thenable = {
+ status: 'fulfilled',
+ value: 'Hi',
+ then() {},
+ };
- await act(() => {
- const {pipe} = renderToPipeableStream( );
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual('Hi');
+ // This will never suspend because the thenable already resolved
+ function App() {
+ return use(thenable);
+ }
- ReactDOMClient.hydrateRoot(container, );
- await waitForAll([]);
- expect(getVisibleChildren(container)).toEqual('Hi');
+ await act(() => {
+ const {pipe} = renderToPipeableStream( );
+ pipe(writable);
});
+ expect(getVisibleChildren(container)).toEqual('Hi');
- // @gate enableUseHook
- it('unwraps thenable that fulfills synchronously without suspending', async () => {
- function App() {
- const thenable = {
- then(resolve) {
- // This thenable immediately resolves, synchronously, without waiting
- // a microtask.
- resolve('Hi');
- },
- };
- try {
- return ;
- } catch {
- throw new Error(
- '`use` should not suspend because the thenable resolved synchronously.',
- );
- }
+ ReactDOMClient.hydrateRoot(container, );
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual('Hi');
+ });
+
+ // @gate enableUseHook
+ it('unwraps thenable that fulfills synchronously without suspending', async () => {
+ function App() {
+ const thenable = {
+ then(resolve) {
+ // This thenable immediately resolves, synchronously, without waiting
+ // a microtask.
+ resolve('Hi');
+ },
+ };
+ try {
+ return ;
+ } catch {
+ throw new Error(
+ '`use` should not suspend because the thenable resolved synchronously.',
+ );
}
- // Because the thenable resolves synchronously, we should be able to finish
- // rendering synchronously, with no fallback.
- await act(() => {
- const {pipe} = renderToPipeableStream( );
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual('Hi');
+ }
+ // Because the thenable resolves synchronously, we should be able to finish
+ // rendering synchronously, with no fallback.
+ await act(() => {
+ const {pipe} = renderToPipeableStream( );
+ pipe(writable);
});
+ expect(getVisibleChildren(container)).toEqual('Hi');
+ });
- it('promise as node', async () => {
- const promise = Promise.resolve('Hi');
- await act(async () => {
- const {pipe} = renderToPipeableStream(promise);
- pipe(writable);
- });
-
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- await act(async () => {
- await promise;
- });
-
- expect(getVisibleChildren(container)).toEqual('Hi');
+ it('promise as node', async () => {
+ const promise = Promise.resolve('Hi');
+ await act(async () => {
+ const {pipe} = renderToPipeableStream(promise);
+ pipe(writable);
});
- it('context as node', async () => {
- const Context = React.createContext('Hi');
- await act(async () => {
- const {pipe} = renderToPipeableStream(Context);
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual('Hi');
+ // TODO: The `act` implementation in this file doesn't unwrap microtasks
+ // automatically. We can't use the same `act` we use for Fiber tests
+ // because that relies on the mock Scheduler. Doesn't affect any public
+ // API but we might want to fix this for our own internal tests.
+ await act(async () => {
+ await promise;
});
- it('recursive Usable as node', async () => {
- const Context = React.createContext('Hi');
- const promiseForContext = Promise.resolve(Context);
- await act(async () => {
- const {pipe} = renderToPipeableStream(promiseForContext);
- pipe(writable);
- });
+ expect(getVisibleChildren(container)).toEqual('Hi');
+ });
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- await act(async () => {
- await promiseForContext;
- });
+ it('context as node', async () => {
+ const Context = React.createContext('Hi');
+ await act(async () => {
+ const {pipe} = renderToPipeableStream(Context);
+ pipe(writable);
+ });
+ expect(getVisibleChildren(container)).toEqual('Hi');
+ });
- expect(getVisibleChildren(container)).toEqual('Hi');
+ it('recursive Usable as node', async () => {
+ const Context = React.createContext('Hi');
+ const promiseForContext = Promise.resolve(Context);
+ await act(async () => {
+ const {pipe} = renderToPipeableStream(promiseForContext);
+ pipe(writable);
});
+
+ // TODO: The `act` implementation in this file doesn't unwrap microtasks
+ // automatically. We can't use the same `act` we use for Fiber tests
+ // because that relies on the mock Scheduler. Doesn't affect any public
+ // API but we might want to fix this for our own internal tests.
+ await act(async () => {
+ await promiseForContext;
+ });
+
+ expect(getVisibleChildren(container)).toEqual('Hi');
});
describe('useEffectEvent', () => {
@@ -5555,7 +5642,7 @@ describe('ReactDOMFizzServer', () => {
});
it('can render scripts with simple children', async () => {
- await actIntoEmptyDocument(async () => {
+ await act(async () => {
const {pipe} = renderToPipeableStream(
@@ -1302,7 +1383,7 @@ body {
function PresetPrecedence() {
ReactDOM.preinit('preset', {as: 'style', precedence: 'preset'});
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -1584,7 +1665,7 @@ body {
// @gate enableFloat
it('normalizes stylesheet resource precedence for all boundaries inlined as part of the shell flush', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -1668,7 +1749,7 @@ body {
// @gate enableFloat
it('stylesheet resources are inserted according to precedence order on the client', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -1791,7 +1872,7 @@ body {
// @gate enableFloat
it('will include child boundary stylesheet resources in the boundary reveal instruction', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -1910,7 +1991,7 @@ body {
// @gate enableFloat
it('will hoist resources of child boundaries emitted as part of a partial boundary to the parent boundary', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -2132,7 +2213,7 @@ body {
);
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -2218,7 +2299,7 @@ body {
// @gate enableFloat
it('boundary stylesheet resource dependencies hoist to a parent boundary when flushed inline', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -2353,7 +2434,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -2417,7 +2498,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream( ).pipe(writable);
});
@@ -2573,7 +2654,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream( ).pipe(writable);
});
@@ -2593,7 +2674,7 @@ body {
- <meta name="noscript" content="noscript"/>
+ <meta name="noscript" content="noscript">
@@ -4078,7 +4159,7 @@ body {
// @gate enableFloat
it('treats link rel stylesheet elements as a stylesheet resource when it includes a precedence when hydrating', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4116,7 +4197,7 @@ body {
// @gate enableFloat
it('preloads stylesheets without a precedence prop when server rendering', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4144,7 +4225,7 @@ body {
// @gate enableFloat
it('hoists stylesheet resources to the correct precedence', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4239,7 +4320,7 @@ body {
// @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
it('retains styles even when a new html, head, and/body mount', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4291,7 +4372,7 @@ body {
// @gate enableFloat && !enableHostSingletons
it('retains styles even when a new html, head, and/body mount - without HostSingleton', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4521,7 +4602,7 @@ body {
);
}
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -4575,7 +4656,7 @@ body {
// @gate enableFloat
it('escapes hrefs when selecting matching elements in the document when using preload and preinit', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4638,7 +4719,7 @@ body {
// @gate enableFloat
it('does not create stylesheet resources when inside an context', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4699,7 +4780,7 @@ body {
// @gate enableFloat
it('does not create stylesheet resources when inside a context', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream(
@@ -4716,7 +4797,7 @@ body {
- <link rel="stylesheet" href="foo" precedence="default"/>
+ <link rel="stylesheet" href="foo" precedence="default">
,
@@ -4742,7 +4823,7 @@ body {
// @gate enableFloat
it('warns if you provide a `precedence` prop with other props that invalidate the creation of a stylesheet resource', async () => {
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -4826,7 +4907,7 @@ body {
);
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -4854,7 +4935,7 @@ body {
);
}
await expect(async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
const {pipe} = renderToPipeableStream( );
pipe(writable);
});
@@ -4865,7 +4946,7 @@ body {
// @gate enableFloat
it('will not block displaying a Suspense boundary on a stylesheet with media that does not match', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -4986,7 +5067,7 @@ body {
body {
background-color: red;
}`;
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -5024,7 +5105,7 @@ background-color: blue;
body {
background-color: green;
}`;
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -5129,7 +5210,7 @@ background-color: green;
// @gate enableFloat
it('can emit styles early when a partial boundary flushes', async () => {
const css = 'body { background-color: red; }';
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -5197,7 +5278,7 @@ background-color: green;
});
it('can hoist styles flushed early even when no other style dependencies are flushed on completion', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -5261,7 +5342,7 @@ background-color: green;
});
it('can emit multiple style rules into a single style tag for a given precedence', async () => {
- await actIntoEmptyDocument(() => {
+ await act(() => {
renderToPipeableStream(
@@ -5440,7 +5521,7 @@ background-color: green;
it('warns if you render a