,
- );
- expect(() => {
- expect(Scheduler).toFlushWithoutYielding();
- }).toErrorDev([
- 'Warning: A previously rendered as a Resource with href "foo" with rel ""stylesheet"" but was updated with an invalid rel: something with type "function". When a link does not have a valid rel prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
- 'Warning: A previously rendered as a Resource with href "bar" but was updated with an invalid href prop: something with type "function". When a link does not have a valid href prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
- ]);
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
-
-
-
-
-
- ,
- );
- });
-
- // @gate enableFloat
- it('inserts a preload resource when called in module scope if a root has already been created', async () => {
- // The requirement that a root be created has to do with bootstrapping the dispatcher.
- // We are intentionally avoiding setting it to the default via import due to cycles and
- // we are trying to avoid doing a mutable initailation in module scope.
- ReactDOM.preload('foo', {as: 'style'});
- ReactDOMClient.createRoot(container);
- ReactDOM.preload('bar', {as: 'style'});
- // We need to use global.document because preload falls back
- // to the window.document global when no other documents have been used
- // The way the JSDOM runtim is created for these tests the local document
- // global does not point to the global.document
- expect(getMeaningfulChildren(global.document)).toEqual(
-
-
-
-
-
- ,
- );
- });
- // @gate enableFloat
- it('supports script preloads', async () => {
- function ServerApp() {
- ReactDOM.preload('foo', {as: 'script', integrity: 'foo hash'});
- ReactDOM.preload('bar', {
- as: 'script',
- crossOrigin: 'use-credentials',
- integrity: 'bar hash',
- });
- return (
-
-
-
- hi
-
- foo
-
+ // @gate enableFloat
+ it('can render resources before singletons', async () => {
+ const root = ReactDOMClient.createRoot(document);
+ root.render(
+ <>
+ foo
+
+
+
+
+ hello world
+
+ >,
);
- }
- function ClientApp() {
- ReactDOM.preload('foo', {as: 'script', integrity: 'foo hash'});
- ReactDOM.preload('qux', {as: 'script'});
- return (
+ try {
+ expect(Scheduler).toFlushWithoutYielding();
+ } catch (e) {
+ // for DOMExceptions that happen when expecting this test to fail we need
+ // to clear the scheduler first otherwise the expected failure will fail
+ expect(Scheduler).toFlushWithoutYielding();
+ throw e;
+ }
+ expect(getMeaningfulChildren(document)).toEqual(
- hi
+ foo
+
- foo
-
-
+ hello world
+ ,
);
- }
-
- await actIntoEmptyDocument(() => {
- const {pipe} = renderToPipeableStream();
- pipe(writable);
});
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
- hi
-
- foo
- ,
- );
-
- ReactDOMClient.hydrateRoot(document, );
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
- hi
-
-
-
- foo
- ,
- );
- });
- });
- describe('ReactDOM.preinit as style', () => {
- // @gate enableFloat
- it('creates a style Resource when called during server rendering before first flush', async () => {
- function Component() {
- ReactDOM.preinit('foo', {as: 'style'});
- return 'foo';
+ function renderSafelyAndExpect(root, children) {
+ root.render(children);
+ return expect(() => {
+ try {
+ expect(Scheduler).toFlushWithoutYielding();
+ } catch (e) {
+ try {
+ expect(Scheduler).toFlushWithoutYielding();
+ } catch (f) {}
+ }
+ });
}
- await actIntoEmptyDocument(() => {
- const {pipe} = renderToPipeableStream(
+
+ // @gate enableFloat
+ it('can hydrate non Resources in head when Resources are also inserted there', async () => {
+ await actIntoEmptyDocument(() => {
+ const {pipe} = renderToPipeableStream(
+
+
+
+ {}} />
+ foo
+
+
+ foobar',
+ '',
+ ]);
});
- // The plain async script is converted to a resource and emitted as part of the shell
- // The async script with onLoad is preloaded in the shell but is expecting to be added
- // during hydration. This is novel, the script is NOT a HostResource but it also will
- // never hydrate
- // The regular script is just a normal html that should hydrate with a HostComponent
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
-
-
- hello world
-
- ,
- );
-
- ReactDOMClient.hydrateRoot(
- document,
-
-
-
,
+ );
+ expect(() => {
+ expect(Scheduler).toFlushWithoutYielding();
+ }).toErrorDev([
+ 'Warning: A previously rendered as a Resource with href "foo" with rel ""stylesheet"" but was updated with an invalid rel: something with type "function". When a link does not have a valid rel prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
+ 'Warning: A previously rendered as a Resource with href "bar" but was updated with an invalid href prop: something with type "function". When a link does not have a valid href prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
+ ]);
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ });
+
+ describe('ReactDOM.preload', () => {
+ // @gate enableFloat
+ it('inserts a preload resource into the stream when called during server rendering', async () => {
+ function Component() {
+ ReactDOM.preload('foo', {as: 'style'});
+ return 'foo';
+ }
+ await actIntoEmptyDocument(() => {
+ const {pipe} = renderToPipeableStream(
+
+
+
+
,
);
+ pipe(writable);
});
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+ foo
+ ,
+ );
+ });
- function renderSafelyAndExpect(root, children) {
- root.render(children);
- return expect(() => {
- try {
- expect(Scheduler).toFlushWithoutYielding();
- } catch (e) {
- try {
- expect(Scheduler).toFlushWithoutYielding();
- } catch (f) {}
- }
- });
+ // @gate enableFloat
+ it('inserts a preload resource into the document during render when called during client rendering', async () => {
+ function Component() {
+ ReactDOM.preload('foo', {as: 'style'});
+ return 'foo';
}
+ const root = ReactDOMClient.createRoot(container);
+ root.render();
+ expect(Scheduler).toFlushWithoutYielding();
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+
foo
+
+ ,
+ );
+ });
- // @gate enableFloat
- it('can hydrate non Resources in head when Resources are also inserted there', async () => {
- await actIntoEmptyDocument(() => {
- const {pipe} = renderToPipeableStream(
-
-
-
- {}} />
- foo
-
-
- foobar',
- '',
- ]);
+ // @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
+ it('retains styles even when a new html, head, and/body mount', async () => {
+ await actIntoEmptyDocument(() => {
+ const {pipe} = renderToPipeableStream(
+
+
+
+
+ server
+
+ ,
+ );
+ pipe(writable);
});
+ const errors = [];
+ ReactDOMClient.hydrateRoot(
+ document,
+
+
+
+
+
+ client
+ ,
+ {
+ onRecoverableError(error) {
+ errors.push(error.message);
+ },
+ },
+ );
+ expect(() => {
+ expect(Scheduler).toFlushWithoutYielding();
+ }).toErrorDev(
+ [
+ 'Warning: Text content did not match. Server: "server" Client: "client"',
+ 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
+ ],
+ {withoutStack: 1},
+ );
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+
+ client
+ ,
+ );
+ });
- describe('HostResource', () => {
- // @gate enableFloat
- it('warns when you update props to an invalid type', async () => {
- const root = ReactDOMClient.createRoot(container);
- root.render(
-
+ // @gate enableFloat && !enableHostSingletons
+ it('retains styles even when a new html, head, and/body mount - without HostSingleton', async () => {
+ await actIntoEmptyDocument(() => {
+ const {pipe} = renderToPipeableStream(
+
+
,
- );
- expect(() => {
- expect(Scheduler).toFlushWithoutYielding();
- }).toErrorDev([
- 'Warning: A previously rendered as a Resource with href "foo" with rel ""stylesheet"" but was updated with an invalid rel: something with type "function". When a link does not have a valid rel prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
- 'Warning: A previously rendered as a Resource with href "bar" but was updated with an invalid href prop: something with type "function". When a link does not have a valid href prop it is not represented in the DOM. If this is intentional, instead do not render the anymore.',
- ]);
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- );
- });
+
+ server
+
+ ,
+ );
+ pipe(writable);
});
+ const errors = [];
+ ReactDOMClient.hydrateRoot(
+ document,
+
+
+
+
+
+ client
+ ,
+ {
+ onRecoverableError(error) {
+ errors.push(error.message);
+ },
+ },
+ );
+ expect(() => {
+ expect(Scheduler).toFlushWithoutYielding();
+ }).toErrorDev(
+ [
+ 'Warning: Text content did not match. Server: "server" Client: "client"',
+ 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
+ ],
+ {withoutStack: 1},
+ );
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+ client
+ ,
+ );
+ });
- describe('ReactDOM.preload', () => {
- // @gate enableFloat
- it('inserts a preload resource into the stream when called during server rendering', async () => {
- function Component() {
- ReactDOM.preload('foo', {as: 'style'});
- return 'foo';
- }
- await actIntoEmptyDocument(() => {
- const {pipe} = renderToPipeableStream(
-
-
-
-
- ,
- );
- pipe(writable);
- });
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
- foo
- ,
- );
- });
+ // @gate enableFloat && enableHostSingletons
+ it('retains styles in head through head remounts', async () => {
+ const root = ReactDOMClient.createRoot(document);
+ root.render(
+
+
+
+
+ {null}
+ hello
+
+ ,
+ );
+ expect(Scheduler).toFlushWithoutYielding();
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+ hello
+ ,
+ );
+
+ root.render(
+
+
+
+ {null}
+
+ hello
+
+ ,
+ );
+ expect(Scheduler).toFlushWithoutYielding();
+ // The reason we do not see preloads in the head is they are inserted synchronously
+ // during render and then when the new singleton mounts it resets it's content, retaining only styles
+ expect(getMeaningfulChildren(document)).toEqual(
+
+
+
+
+
+
+
+ hello
+ ,
+ );
+ });
+ });
- // @gate enableFloat
- it('inserts a preload resource into the document during render when called during client rendering', async () => {
- function Component() {
- ReactDOM.preload('foo', {as: 'style'});
- return 'foo';
- }
- const root = ReactDOMClient.createRoot(container);
- root.render();
- expect(Scheduler).toFlushWithoutYielding();
- expect(getMeaningfulChildren(document)).toEqual(
-
-
-
-
-
-