/**
 * 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
 */

'use strict';
import {
  replaceScriptsAndMove,
  mergeOptions,
  withLoadingReadyState,
} from '../test-utils/FizzTestUtils';

let JSDOM;
let Stream;
let Scheduler;
let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMFizzServer;
let Suspense;
let textCache;
let document;
let writable;
const CSPnonce = null;
let container;
let buffer = '';
let hasErrored = false;
let fatalError = undefined;
let renderOptions;

describe('ReactDOMFloat', () => {
  beforeEach(() => {
    jest.resetModules();
    JSDOM = require('jsdom').JSDOM;
    Scheduler = require('scheduler');
    React = require('react');
    ReactDOM = require('react-dom');
    ReactDOMClient = require('react-dom/client');
    ReactDOMFizzServer = require('react-dom/server');
    Stream = require('stream');
    Suspense = React.Suspense;

    textCache = new Map();

    // Test Environment
    const jsdom = new JSDOM(
      '<!DOCTYPE html><html><head></head><body><div id="container">',
      {
        runScripts: 'dangerously',
      },
    );
    document = jsdom.window.document;
    container = document.getElementById('container');

    buffer = '';
    hasErrored = false;

    writable = new Stream.PassThrough();
    writable.setEncoding('utf8');
    writable.on('data', chunk => {
      buffer += chunk;
    });
    writable.on('error', error => {
      hasErrored = true;
      fatalError = error;
    });

    renderOptions = {};
    if (gate(flags => flags.enableFizzExternalRuntime)) {
      renderOptions.unstable_externalRuntimeSrc =
        'react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js';
    }
  });

  function normalizeCodeLocInfo(str) {
    return (
      typeof str === 'string' &&
      str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function(m, name) {
        return '\n    in ' + name + ' (at **)';
      })
    );
  }

  function componentStack(components) {
    return components
      .map(component => `\n    in ${component} (at **)`)
      .join('');
  }

  async function act(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;
    }
    // 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;
    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;
    }
    // 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;
    // Test Environment
    const jsdom = new JSDOM(bufferedContent, {
      runScripts: 'dangerously',
    });
    document = jsdom.window.document;
    container = document;
    buffer = '';
    await withLoadingReadyState(async () => {
      await replaceScriptsAndMove(jsdom.window, null, document.documentElement);
    }, document);
  }

  function getMeaningfulChildren(element) {
    const children = [];
    let node = element.firstChild;
    while (node) {
      if (node.nodeType === 1) {
        if (
          // some tags are ambiguous and might be hidden because they look like non-meaningful children
          // so we have a global override where if this data attribute is included we also include the node
          node.hasAttribute('data-meaningful') ||
          (node.tagName === 'SCRIPT' &&
            node.hasAttribute('src') &&
            node.getAttribute('src') !==
              renderOptions.unstable_externalRuntimeSrc &&
            node.hasAttribute('async')) ||
          (node.tagName !== 'SCRIPT' &&
            node.tagName !== 'TEMPLATE' &&
            node.tagName !== 'template' &&
            !node.hasAttribute('hidden') &&
            !node.hasAttribute('aria-hidden'))
        ) {
          const props = {};
          const attributes = node.attributes;
          for (let i = 0; i < attributes.length; i++) {
            if (
              attributes[i].name === 'id' &&
              attributes[i].value.includes(':')
            ) {
              // We assume this is a React added ID that's a non-visual implementation detail.
              continue;
            }
            props[attributes[i].name] = attributes[i].value;
          }
          props.children = getMeaningfulChildren(node);
          children.push(React.createElement(node.tagName.toLowerCase(), props));
        }
      } else if (node.nodeType === 3) {
        children.push(node.data);
      }
      node = node.nextSibling;
    }
    return children.length === 0
      ? undefined
      : children.length === 1
      ? children[0]
      : children;
  }

  function resolveText(text) {
    const record = textCache.get(text);
    if (record === undefined) {
      const newRecord = {
        status: 'resolved',
        value: text,
      };
      textCache.set(text, newRecord);
    } else if (record.status === 'pending') {
      const thenable = record.value;
      record.status = 'resolved';
      record.value = text;
      thenable.pings.forEach(t => t());
    }
  }

  function readText(text) {
    const record = textCache.get(text);
    if (record !== undefined) {
      switch (record.status) {
        case 'pending':
          throw record.value;
        case 'rejected':
          throw record.value;
        case 'resolved':
          return record.value;
      }
    } else {
      const thenable = {
        pings: [],
        then(resolve) {
          if (newRecord.status === 'pending') {
            thenable.pings.push(resolve);
          } else {
            Promise.resolve().then(() => resolve(newRecord.value));
          }
        },
      };

      const newRecord = {
        status: 'pending',
        value: thenable,
      };
      textCache.set(text, newRecord);

      throw thenable;
    }
  }

  function AsyncText({text}) {
    return readText(text);
  }

  function renderToPipeableStream(jsx, options) {
    // Merge options with renderOptions, which may contain featureFlag specific behavior
    return ReactDOMFizzServer.renderToPipeableStream(
      jsx,
      mergeOptions(options, renderOptions),
    );
  }

  // @gate enableFloat
  it('can render resources before singletons', async () => {
    const root = ReactDOMClient.createRoot(document);
    root.render(
      <>
        <title>foo</title>
        <html>
          <head>
            <link rel="foo" href="foo" />
          </head>
          <body>hello world</body>
        </html>
      </>,
    );
    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(
      <html>
        <head>
          <title>foo</title>
          <link rel="foo" href="foo" />
        </head>
        <body>hello world</body>
      </html>,
    );
  });

  function renderSafelyAndExpect(root, children) {
    root.render(children);
    return expect(() => {
      try {
        expect(Scheduler).toFlushWithoutYielding();
      } catch (e) {
        try {
          expect(Scheduler).toFlushWithoutYielding();
        } catch (f) {}
      }
    });
  }

  // @gate enableFloat
  it('can hydrate non Resources in head when Resources are also inserted there', async () => {
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head>
            <meta property="foo" content="bar" />
            <link rel="foo" href="bar" onLoad={() => {}} />
            <title>foo</title>
            <noscript>
              <link rel="icon" href="icon" />
            </noscript>
            <base target="foo" href="bar" />
            <script async={true} src="foo" onLoad={() => {}} />
          </head>
          <body>foo</body>
        </html>,
      );
      pipe(writable);
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <base target="foo" href="bar" />
          <link rel="preload" href="foo" as="script" />
          <meta property="foo" content="bar" />
          <title>foo</title>
          <noscript>&lt;link rel="icon" href="icon"/&gt;</noscript>
        </head>
        <body>foo</body>
      </html>,
    );

    ReactDOMClient.hydrateRoot(
      document,
      <html>
        <head>
          <meta property="foo" content="bar" />
          <link rel="foo" href="bar" onLoad={() => {}} />
          <title>foo</title>
          <noscript>
            <link rel="icon" href="icon" />
          </noscript>
          <base target="foo" href="bar" />
          <script async={true} src="foo" onLoad={() => {}} />
        </head>
        <body>foo</body>
      </html>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <base target="foo" href="bar" />
          <link rel="preload" href="foo" as="script" />
          <meta property="foo" content="bar" />
          <title>foo</title>
          <link rel="foo" href="bar" />
          <noscript>&lt;link rel="icon" href="icon"/&gt;</noscript>
          <script async="" src="foo" />
        </head>
        <body>foo</body>
      </html>,
    );
  });

  // @gate enableFloat || !__DEV__
  it('warns if you render resource-like elements above <head> or <body>', async () => {
    const root = ReactDOMClient.createRoot(document);

    renderSafelyAndExpect(
      root,
      <>
        <noscript>foo</noscript>
        <html>
          <body>foo</body>
        </html>
      </>,
    ).toErrorDev(
      [
        'Cannot render <noscript> outside the main document. Try moving it into the root <head> tag.',
        'Warning: validateDOMNesting(...): <noscript> cannot appear as a child of <#document>.',
      ],
      {withoutStack: 1},
    );

    renderSafelyAndExpect(
      root,
      <html>
        <template>foo</template>
        <body>foo</body>
      </html>,
    ).toErrorDev([
      'Cannot render <template> outside the main document. Try moving it into the root <head> tag.',
      'Warning: validateDOMNesting(...): <template> cannot appear as a child of <html>.',
    ]);

    renderSafelyAndExpect(
      root,
      <html>
        <body>foo</body>
        <style>foo</style>
      </html>,
    ).toErrorDev([
      'Cannot render <style> outside the main document. Try moving it into the root <head> tag.',
      'Warning: validateDOMNesting(...): <style> cannot appear as a child of <html>.',
    ]);

    renderSafelyAndExpect(
      root,
      <>
        <html>
          <body>foo</body>
        </html>
        <link rel="stylesheet" href="foo" />
      </>,
    ).toErrorDev(
      [
        'Cannot render a <link rel="stylesheet" /> outside the main document without knowing its precedence. Consider adding precedence="default" or moving it into the root <head> tag.',
        'Warning: validateDOMNesting(...): <link> cannot appear as a child of <#document>.',
      ],
      {withoutStack: 1},
    );

    renderSafelyAndExpect(
      root,
      <>
        <html>
          <body>foo</body>
          <script href="foo" />
        </html>
      </>,
    ).toErrorDev([
      'Cannot render a sync or defer <script> outside the main document without knowing its order. Try adding async="" or moving it into the root <head> tag.',
      'Warning: validateDOMNesting(...): <script> cannot appear as a child of <html>.',
    ]);

    renderSafelyAndExpect(
      root,
      <>
        <html>
          <script async={true} onLoad={() => {}} href="bar" />
          <body>foo</body>
        </html>
      </>,
    ).toErrorDev([
      'Cannot render a <script> with onLoad or onError listeners outside the main document. Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or somewhere in the <body>.',
    ]);

    renderSafelyAndExpect(
      root,
      <>
        <link rel="foo" onLoad={() => {}} href="bar" />
        <html>
          <body>foo</body>
        </html>
      </>,
    ).toErrorDev(
      [
        'Cannot render a <link> with onLoad or onError listeners outside the main document. Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or somewhere in the <body>.',
      ],
      {withoutStack: 1},
    );
  });

  // @gate enableFloat
  it('can acquire a resource after releasing it in the same commit', async () => {
    const root = ReactDOMClient.createRoot(container);
    root.render(
      <>
        <title>foo</title>
      </>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <title>foo</title>
        </head>
        <body>
          <div id="container" />
        </body>
      </html>,
    );

    // title is keyed off children so this second resource should match the first one
    root.render(
      <>
        {null}
        <title data-new="new">foo</title>
      </>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    // we don't see the attribute because the resource is the same and was not reconstructed
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <title>foo</title>
        </head>
        <body>
          <div id="container" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('errors if the document does not contain a head when inserting a resource', async () => {
    document.head.parentNode.removeChild(document.head);
    const root = ReactDOMClient.createRoot(document);
    root.render(
      <html>
        <body>
          <link rel="stylesheet" href="foo" precedence="default" />
          foo
        </body>
      </html>,
    );
    expect(() => {
      expect(Scheduler).toFlushWithoutYielding();
    }).toThrow(
      'While attempting to insert a Resource, React expected the Document to contain a head element but it was not found.',
    );
  });

  // @gate enableFloat
  it('emits resources before everything else when rendering with no head', async () => {
    function App() {
      return (
        <>
          <title>foo</title>
          <link rel="preload" href="foo" as="style" />
        </>
      );
    }

    await actIntoEmptyDocument(() => {
      buffer = `<!DOCTYPE html><html><head>${ReactDOMFizzServer.renderToString(
        <App />,
      )}</head><body>foo</body></html>`;
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="preload" href="foo" as="style" />
          <title>foo</title>
        </head>
        <body>foo</body>
      </html>,
    );
  });

  // @gate enableFloat
  it('emits resources before everything else when rendering with just a head', async () => {
    function App() {
      return (
        <head>
          <title>foo</title>
          <link rel="preload" href="foo" as="style" />
        </head>
      );
    }

    await actIntoEmptyDocument(() => {
      buffer = `<!DOCTYPE html><html>${ReactDOMFizzServer.renderToString(
        <App />,
      )}<body>foo</body></html>`;
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="preload" href="foo" as="style" />
          <title>foo</title>
        </head>
        <body>foo</body>
      </html>,
    );
  });

  // @gate enableFloat
  it('does not emit closing tags in out of order position when rendering a non-void resource type', async () => {
    const chunks = [];

    writable.on('data', chunk => {
      chunks.push(chunk);
    });

    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <>
          <title>foo</title>
          <html>
            <body>bar</body>
          </html>
          <script async={true} src="foo" />
        </>,
      );
      pipe(writable);
    });
    expect(chunks).toEqual([
      '<!DOCTYPE html><html><script async="" src="foo"></script><title>foo</title><body>bar',
      '</body></html>',
    ]);
  });

  // @gate enableFloat
  it('dedupes if the external runtime is explicitly loaded using preinit', async () => {
    const unstable_externalRuntimeSrc = 'src-of-external-runtime';
    function App() {
      ReactDOM.preinit(unstable_externalRuntimeSrc, {as: 'script'});
      return (
        <div>
          <Suspense fallback={<h1>Loading...</h1>}>
            <AsyncText text="Hello" />
          </Suspense>
        </div>
      );
    }

    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <App />
          </body>
        </html>,
        {
          unstable_externalRuntimeSrc,
        },
      );
      pipe(writable);
    });

    expect(
      Array.from(document.getElementsByTagName('script')).map(n => n.outerHTML),
    ).toEqual(['<script src="src-of-external-runtime" async=""></script>']);
  });

  describe('HostResource', () => {
    // @gate enableFloat
    it('warns when you update props to an invalid type', async () => {
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <div>
          <link rel="stylesheet" href="foo" precedence="foo" />
          <link rel="stylesheet" href="bar" precedence="foo" />
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      root.render(
        <div>
          <link rel={() => {}} href="bar" />
          <link rel="foo" href={() => {}} />
        </div>,
      );
      expect(() => {
        expect(Scheduler).toFlushWithoutYielding();
      }).toErrorDev([
        'Warning: A <link> 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 <link> anymore.',
        'Warning: A <link> 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 <link> anymore.',
      ]);
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <link rel="stylesheet" href="bar" data-precedence="foo" />
            <link rel="preload" as="style" href="foo" />
            <link rel="preload" as="style" href="bar" />
          </head>
          <body>
            <div id="container">
              <div />
            </div>
          </body>
        </html>,
      );
    });
  });

  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(
          <html>
            <head />
            <body>
              <Component />
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="foo" />
          </head>
          <body>foo</body>
        </html>,
      );
    });

    // @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(<Component />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="foo" />
          </head>
          <body>
            <div id="container">foo</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('inserts a preload resource when called in a layout effect', async () => {
      function App() {
        React.useLayoutEffect(() => {
          ReactDOM.preload('foo', {as: 'style'});
        }, []);
        return 'foobar';
      }
      const root = ReactDOMClient.createRoot(container);
      root.render(<App />);
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="foo" />
          </head>
          <body>
            <div id="container">foobar</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('inserts a preload resource when called in a passive effect', async () => {
      function App() {
        React.useEffect(() => {
          ReactDOM.preload('foo', {as: 'style'});
        }, []);
        return 'foobar';
      }
      const root = ReactDOMClient.createRoot(container);
      root.render(<App />);
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="foo" />
          </head>
          <body>
            <div id="container">foobar</div>
          </body>
        </html>,
      );
    });

    // @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(
        <html>
          <head>
            <link rel="preload" as="style" href="bar" />
          </head>
          <body />
        </html>,
      );
    });

    // @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 (
          <html>
            <link rel="preload" href="baz" as="script" />
            <head>
              <title>hi</title>
            </head>
            <body>foo</body>
          </html>
        );
      }
      function ClientApp() {
        ReactDOM.preload('foo', {as: 'script', integrity: 'foo hash'});
        ReactDOM.preload('qux', {as: 'script'});
        return (
          <html>
            <head>
              <title>hi</title>
            </head>
            <body>foo</body>
            <link
              rel="preload"
              href="quux"
              as="script"
              crossOrigin=""
              integrity="quux hash"
            />
          </html>
        );
      }

      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(<ServerApp />);
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="script" href="foo" integrity="foo hash" />
            <link
              rel="preload"
              as="script"
              href="bar"
              crossorigin="use-credentials"
              integrity="bar hash"
            />
            <link rel="preload" as="script" href="baz" />
            <title>hi</title>
          </head>
          <body>foo</body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(document, <ClientApp />);
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="script" href="foo" integrity="foo hash" />
            <link
              rel="preload"
              as="script"
              href="bar"
              crossorigin="use-credentials"
              integrity="bar hash"
            />
            <link rel="preload" as="script" href="baz" />
            <title>hi</title>
            <link rel="preload" as="script" href="qux" />
            <link
              rel="preload"
              as="script"
              href="quux"
              crossorigin=""
              integrity="quux hash"
            />
          </head>
          <body>foo</body>
        </html>,
      );
    });
  });

  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';
      }
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <Component />
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="default" />
          </head>
          <body>foo</body>
        </html>,
      );
    });

    // @gate enableFloat
    it('creates a preload Resource when called during server rendering after first flush', async () => {
      function BlockedOn({text, children}) {
        readText(text);
        return children;
      }
      function Component() {
        ReactDOM.preinit('foo', {as: 'style', precedence: 'foo'});
        return 'foo';
      }
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <Suspense fallback="loading...">
                <BlockedOn text="unblock">
                  <Component />
                </BlockedOn>
              </Suspense>
            </body>
          </html>,
        );
        pipe(writable);
      });
      await act(() => {
        resolveText('unblock');
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            foo
            <link rel="preload" as="style" href="foo" />
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('inserts a style Resource into the document during render when called during client rendering', async () => {
      function Component() {
        ReactDOM.preinit('foo', {as: 'style', precedence: 'foo'});
        return 'foo';
      }
      const root = ReactDOMClient.createRoot(container);
      root.render(<Component />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
          </head>
          <body>
            <div id="container">foo</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('inserts a preload resource into the document when called in an insertion effect, layout effect, or passive effect', async () => {
      function App() {
        React.useEffect(() => {
          ReactDOM.preinit('passive', {as: 'style', precedence: 'default'});
        }, []);
        React.useLayoutEffect(() => {
          ReactDOM.preinit('layout', {as: 'style', precedence: 'default'});
        });
        React.useInsertionEffect(() => {
          ReactDOM.preinit('insertion', {as: 'style', precedence: 'default'});
        });
        return 'foobar';
      }
      const root = ReactDOMClient.createRoot(container);
      root.render(<App />);
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="insertion" />
            <link rel="preload" as="style" href="layout" />
            <link rel="preload" as="style" href="passive" />
          </head>
          <body>
            <div id="container">foobar</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('inserts a preload resource when called in module scope', 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.preinit('foo', {as: 'style'});
      ReactDOMClient.hydrateRoot(container, null);
      ReactDOM.preinit('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(
        <html>
          <head>
            <link rel="preload" as="style" href="bar" />
          </head>
          <body />
        </html>,
      );
    });
  });

  describe('ReactDOM.preinit as script', () => {
    // @gate enableFloat
    it('can preinit a script', async () => {
      function App({srcs}) {
        srcs.forEach(src => ReactDOM.preinit(src, {as: 'script'}));
        return (
          <html>
            <head>
              <title>title</title>
            </head>
            <body>foo</body>
          </html>
        );
      }
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <App srcs={['server', 'shared']} />,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <script src="server" async="" />
            <script src="shared" async="" />
            <title>title</title>
          </head>
          <body>foo</body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(document, <App srcs={['client', 'shared']} />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <script src="server" async="" />
            <script src="shared" async="" />
            <title>title</title>
            <script src="client" async="" />
          </head>
          <body>foo</body>
        </html>,
      );
    });
  });

  describe('document encapsulation', () => {
    // @gate enableFloat
    it('can support styles inside portals to a shadowRoot', async () => {
      const shadow = document.body.attachShadow({mode: 'open'});
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <>
          <link rel="stylesheet" href="foo" precedence="default" />
          {ReactDOM.createPortal(
            <div>
              <link
                rel="stylesheet"
                href="foo"
                data-extra-prop="foo"
                precedence="different"
              />
              shadow
            </div>,
            shadow,
          )}
          container
        </>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="default" />
            <link rel="preload" href="foo" as="style" />
          </head>
          <body>
            <div id="container">container</div>
          </body>
        </html>,
      );
      expect(getMeaningfulChildren(shadow)).toEqual([
        <link
          rel="stylesheet"
          href="foo"
          data-precedence="different"
          data-extra-prop="foo"
        />,
        <div>shadow</div>,
      ]);
    });
    // @gate enableFloat
    it('can support styles inside portals to an element in shadowRoots', async () => {
      const template = document.createElement('template');
      template.innerHTML =
        "<div><div id='shadowcontainer1'></div><div id='shadowcontainer2'></div></div>";
      const shadow = document.body.attachShadow({mode: 'open'});
      shadow.appendChild(template.content);

      const shadowContainer1 = shadow.getElementById('shadowcontainer1');
      const shadowContainer2 = shadow.getElementById('shadowcontainer2');
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <>
          <link rel="stylesheet" href="foo" precedence="default" />
          {ReactDOM.createPortal(
            <div>
              <link rel="stylesheet" href="foo" precedence="one" />
              <link rel="stylesheet" href="bar" precedence="two" />1
            </div>,
            shadow,
          )}
          {ReactDOM.createPortal(
            <div>
              <link rel="stylesheet" href="foo" precedence="one" />
              <link rel="stylesheet" href="baz" precedence="one" />2
            </div>,
            shadowContainer1,
          )}
          {ReactDOM.createPortal(
            <div>
              <link rel="stylesheet" href="bar" precedence="two" />
              <link rel="stylesheet" href="qux" precedence="three" />3
            </div>,
            shadowContainer2,
          )}
          container
        </>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="default" />
            <link rel="preload" href="foo" as="style" />
            <link rel="preload" href="bar" as="style" />
            <link rel="preload" href="baz" as="style" />
            <link rel="preload" href="qux" as="style" />
          </head>
          <body>
            <div id="container">container</div>
          </body>
        </html>,
      );
      expect(getMeaningfulChildren(shadow)).toEqual([
        <link rel="stylesheet" href="foo" data-precedence="one" />,
        <link rel="stylesheet" href="baz" data-precedence="one" />,
        <link rel="stylesheet" href="bar" data-precedence="two" />,
        <link rel="stylesheet" href="qux" data-precedence="three" />,
        <div>
          <div id="shadowcontainer1">
            <div>2</div>
          </div>
          <div id="shadowcontainer2">
            <div>3</div>
          </div>
        </div>,
        <div>1</div>,
      ]);
    });
  });

  describe('head resources', () => {
    // @gate enableFloat
    it('supports preconnects, prefetc-dns, and arbitrary other link types', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <div>hello world</div>
            </body>
            <link rel="foo" href="bar" />
            <link rel="preload" href="bar" />
            <link rel="preload" href="bar" as="style" />
            <link rel="stylesheet" href="bar" precedence="default" />
            <link rel="preconnect" href="bar" />
            <link rel="dns-prefetch" href="bar" />
            <link rel="icon" href="bar" />
            <link rel="icon" href="bar" sizes="1x1" />
            <link rel="icon" href="bar" media="foo" />
            <link rel="shortcut icon" href="bar" />
            <link rel="apple-touch-icon" href="bar" />
          </html>,
        );
        pipe(writable);
      });
      // "preconnect" and "dns-prefetch" get hoisted to the front.
      // All other generic links (not styles, or typed preloads)
      // get emitted after styles and other higher priority Resources
      // Sizes and Media are part of generic link keys
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preconnect" href="bar" />
            <link rel="dns-prefetch" href="bar" />
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <link rel="foo" href="bar" />
            <link rel="preload" href="bar" />
            <link rel="icon" href="bar" />
            <link rel="icon" href="bar" sizes="1x1" />
            <link rel="icon" href="bar" media="foo" />
            <link rel="shortcut icon" href="bar" />
            <link rel="apple-touch-icon" href="bar" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      const root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <div>hello world</div>
          </body>
          <link rel="foo" href="bar" />
          <link rel="preload" href="bar" />
          <link rel="preload" href="bar" as="style" />
          <link rel="stylesheet" href="bar" precedence="default" />
          <link rel="preconnect" href="bar" />
          <link rel="dns-prefetch" href="bar" />
          <link rel="icon" href="bar" />
          <link rel="icon" href="bar" sizes="1x1" />
          <link rel="icon" href="bar" media="foo" />
          <link rel="shortcut icon" href="bar" />
          <link rel="apple-touch-icon" href="bar" />
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preconnect" href="bar" />
            <link rel="dns-prefetch" href="bar" />
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <link rel="foo" href="bar" />
            <link rel="preload" href="bar" />
            <link rel="icon" href="bar" />
            <link rel="icon" href="bar" sizes="1x1" />
            <link rel="icon" href="bar" media="foo" />
            <link rel="shortcut icon" href="bar" />
            <link rel="apple-touch-icon" href="bar" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <html>
          <head />
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <link rel="preload" href="bar" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can render <base> as a Resource', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <base target="_blank" />
              <base href="foo" />
              <base target="_self" href="bar" />
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <base target="_blank" />
            <base href="foo" />
            <base target="_self" href="bar" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <base target="_blank" />
            <base href="foo" />
            <base target="_self" href="bar" />
            <base target="_top" href="baz" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <base target="_top" href="baz" />
            <base target="_blank" />
            <base href="foo" />
            <base target="_self" href="bar" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can render icons and apple-touch-icons as Resources', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <html>
              <head />
              <body>
                <link rel="icon" href="foo" />
                <div>hello world</div>
              </body>
            </html>
            <link rel="apple-touch-icon" href="foo" />
          </>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="icon" href="foo" />
            <link rel="apple-touch-icon" href="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <link rel="apple-touch-icon" href="foo" />
          <head />
          <body>
            <link rel="icon" href="foo" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="icon" href="foo" />
            <link rel="apple-touch-icon" href="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can hydrate the right instances for deeply nested structured metas', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <html>
              <head />
              <body>
                <div>hello world</div>
              </body>
            </html>
            <meta property="og:foo" content="one" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
            <meta property="og:foo" content="two" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
          </>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:foo" content="one" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
            <meta property="og:foo" content="two" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      const root = ReactDOMClient.hydrateRoot(
        document,
        <>
          <html>
            <head />
            <body>
              <div>hello world</div>
            </body>
          </html>
          <meta property="og:foo" content="one" />
          <meta property="og:foo:bar" content="bar" />
          <meta property="og:foo:bar:baz" content="baz" />
          <meta property="og:foo" content="two" />
          <meta property="og:foo:bar" content="bar" />
          <meta property="og:foo:bar:baz" content="baz" />
        </>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:foo" content="one" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
            <meta property="og:foo" content="two" />
            <meta property="og:foo:bar" content="bar" />
            <meta property="og:foo:bar:baz" content="baz" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <>
          <html>
            <head />
            <body>
              <div>hello world</div>
            </body>
          </html>
        </>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can insert meta tags in the expected location', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <html>
              <head />
              <body>
                <div>hello world</div>
              </body>
            </html>
            <meta charSet="utf-8" />
            <meta name="google-site-verification" content="somehash1" />
            <meta name="google-site-verification" content="somehash2" />
            <meta
              name="description"
              property="og:description"
              content="my site"
            />
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta httpEquiv="refresh" content="dont actually" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta itemProp="someprop" content="somevalue" />
            <meta property="og:image:height" content="100" />
            <meta property="og:description:foo" content="foo" />
          </>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta charset="utf-8" />
            <meta name="google-site-verification" content="somehash1" />
            <meta name="google-site-verification" content="somehash2" />
            <meta
              name="description"
              property="og:description"
              content="my site"
            />
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta http-equiv="refresh" content="dont actually" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta itemprop="someprop" content="somevalue" />
            <meta property="og:image:height" content="100" />
            <meta property="og:description:foo" content="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      const root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head>
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta charset="utf-8" />
            <meta name="google-site-verification" content="somehash1" />
            <meta name="google-site-verification" content="somehash2" />
            <meta
              name="description"
              property="og:description"
              content="my site"
            />
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta http-equiv="refresh" content="dont actually" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta itemprop="someprop" content="somevalue" />
            <meta property="og:image:height" content="100" />
            <meta property="og:description:foo" content="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <html>
          <head>
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:description" content="my site" />
            <meta
              itemProp="description bar"
              property="og:description:bar"
              content="bar"
            />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta charset="utf-8" />
            <meta name="google-site-verification" content="somehash1" />
            <meta name="google-site-verification" content="somehash2" />
            <meta
              name="description"
              property="og:description"
              content="my site"
            />
            <meta
              itemprop="description bar"
              property="og:description:bar"
              content="bar"
            />
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta http-equiv="refresh" content="dont actually" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta itemprop="someprop" content="somevalue" />
            <meta property="og:image:height" content="100" />
            <meta property="og:description:foo" content="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can render meta tags with og properties with structured data', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <html>
              <head />
              <body>
                <div>hello world</div>
              </body>
            </html>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
          </>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      const root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <html>
          <head />
          <body>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <html>
          <head />
          <body>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image:foo" content="foo" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:image" content="foo" />
            <meta property="og:image:foo" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      root.render(
        <html>
          <head />
          <body>
            <meta property="og:image" content="foo" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:width:bar" content="bar" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image:foo" content="foo" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta property="og:image" content="foo" />
            <meta property="og:image:foo" content="foo" />
            <meta property="og:image:height" content="100" />
            <meta property="og:image:width" content="100" />
            <meta property="og:image:width:bar" content="bar" />
            <meta property="og:image" content="bar" />
            <meta property="og:image:height" content="100" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can render meta tags as resources', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <html>
              <head />
              <body>
                <div>hello world</div>
              </body>
            </html>
            <meta name="robots" content="noindex" />
            <meta httpEquiv="content-security-policy" content="foo" />
            <meta itemProp="description" content="desc" />
            <meta property="description" content="desc2" />
            <meta charSet="utf-8" />
          </>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta charset="utf-8" />
            <meta name="robots" content="noindex" />
            <meta http-equiv="content-security-policy" content="foo" />
            <meta itemprop="description" content="desc" />
            <meta property="description" content="desc2" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head>
            <meta charSet="utf-8" />
          </head>
          <body>
            <meta name="robots" content="noindex" />
            <meta httpEquiv="content-security-policy" content="foo" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <meta charset="utf-8" />
            <meta name="robots" content="noindex" />
            <meta http-equiv="content-security-policy" content="foo" />
            <meta itemprop="description" content="desc" />
            <meta property="description" content="desc2" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('can rendering title tags anywhere in the tree', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <title>before</title>
            <>
              <html>
                <head>
                  <title>in head</title>
                </head>
                <body>
                  <div>
                    <title>during</title>
                    hello world
                  </div>
                </body>
              </html>
            </>
            <title>after</title>
          </>,
        );
        pipe(writable);
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>before</title>
            <title>in head</title>
            <title>during</title>
            <title>after</title>
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <>
          <title>before</title>
          <>
            <html>
              <head>
                <title>in head</title>
              </head>
              <body>
                <div>
                  <title>during</title>
                  hello world
                </div>
              </body>
            </html>
          </>
          <title>after</title>
        </>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>before</title>
            <title>in head</title>
            <title>during</title>
            <title>after</title>
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('prepends new titles on the client so newer ones override older ones, including orphaned server rendered titles', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head>
              <title>server</title>
            </head>
            <body>
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>server</title>
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <title>html</title>
          <head>
            <title>head</title>
          </head>
          <body>
            <title>body</title>
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>body</title>
            <title>head</title>
            <title>html</title>
            <title>server</title>
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('keys titles on text children and only removes them when no more instances refer to that title', async () => {
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <div>
          <title>{[2]}</title>hello world<title>2</title>
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>2</title>
          </head>
          <body>
            <div id="container">
              <div>hello world</div>
            </div>
          </body>
        </html>,
      );

      root.render(
        <div>
          {null}hello world<title>2</title>
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>2</title>
          </head>
          <body>
            <div id="container">
              <div>hello world</div>
            </div>
          </body>
        </html>,
      );
      root.render(
        <div>
          {null}hello world{null}
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <div id="container">
              <div>hello world</div>
            </div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat && enableHostSingletons && (enableClientRenderFallbackOnTextMismatch || !__DEV__)
    it('can render a title before a singleton even if that singleton clears its contents', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <>
            <title>foo</title>
            <html>
              <head />
              <body>
                <div>server</div>
              </body>
            </html>
          </>,
        );
        pipe(writable);
      });

      const errors = [];
      ReactDOMClient.hydrateRoot(
        document,
        <>
          <title>foo</title>
          <html>
            <head />
            <body>
              <div>client</div>
            </body>
          </html>
        </>,
        {
          onRecoverableError(err) {
            errors.push(err.message);
          },
        },
      );
      try {
        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},
        );
      } catch (e) {
        // When gates are false this test fails on a DOMException if you don't clear the scheduler after catching.
        // When gates are true this branch should not be hit
        expect(Scheduler).toFlushWithoutYielding();
        throw e;
      }
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>foo</title>
          </head>
          <body>
            <div>client</div>
          </body>
        </html>,
      );
    });
  });

  describe('style resources', () => {
    // @gate enableFloat
    it('treats link rel stylesheet elements as a style resource when it includes a precedence when server rendering', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="aresource" precedence="foo" />
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="aresource" data-precedence="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('treats link rel stylesheet elements as a style resource when it includes a precedence when client rendering', async () => {
      const root = ReactDOMClient.createRoot(document);
      root.render(
        <html>
          <head />
          <body>
            <link rel="stylesheet" href="aresource" precedence="foo" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="aresource" data-precedence="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('treats link rel stylesheet elements as a style resource when it includes a precedence when hydrating', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="aresource" precedence="foo" />
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });
      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <link rel="stylesheet" href="aresource" precedence="foo" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="aresource" data-precedence="foo" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('preloads stylesheets without a precedence prop when server rendering', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="notaresource" />
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="style" href="notaresource" />
          </head>
          <body>
            <link rel="stylesheet" href="notaresource" />
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('hoists style resources to the correct precedence', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="foo1" precedence="foo" />
              <link rel="stylesheet" href="default1" precedence="default" />
              <link rel="stylesheet" href="foo2" precedence="foo" />
              <div>hello world</div>
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo1" data-precedence="foo" />
            <link rel="stylesheet" href="foo2" data-precedence="foo" />
            <link rel="stylesheet" href="default1" data-precedence="default" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <link rel="stylesheet" href="bar1" precedence="bar" />
            <link rel="stylesheet" href="foo3" precedence="foo" />
            <link rel="stylesheet" href="default2" precedence="default" />
            <div>hello world</div>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo1" data-precedence="foo" />
            <link rel="stylesheet" href="foo2" data-precedence="foo" />
            <link rel="stylesheet" href="foo3" data-precedence="foo" />
            <link rel="stylesheet" href="default1" data-precedence="default" />
            <link rel="stylesheet" href="default2" data-precedence="default" />
            <link rel="stylesheet" href="bar1" data-precedence="bar" />
            <link rel="preload" as="style" href="bar1" />
            <link rel="preload" as="style" href="foo3" />
            <link rel="preload" as="style" href="default2" />
          </head>
          <body>
            <div>hello world</div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('retains styles even after the last referring Resource unmounts', async () => {
      // This test is true until a future update where there is some form of garbage collection.
      const root = ReactDOMClient.createRoot(document);

      root.render(
        <html>
          <head />
          <body>
            hello world
            <link rel="stylesheet" href="foo" precedence="foo" />
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();

      root.render(
        <html>
          <head />
          <body>hello world</body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
          </head>
          <body>hello world</body>
        </html>,
      );
    });

    // @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
    it('retains styles even when a new html, head, and/body mount', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="foo" precedence="foo" />
              <link rel="stylesheet" href="bar" precedence="bar" />
              server
            </body>
          </html>,
        );
        pipe(writable);
      });
      const errors = [];
      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head>
            <link rel="stylesheet" href="qux" precedence="qux" />
            <link rel="stylesheet" href="foo" precedence="foo" />
          </head>
          <body>client</body>
        </html>,
        {
          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(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <link rel="stylesheet" href="bar" data-precedence="bar" />
            <link rel="stylesheet" href="qux" data-precedence="qux" />
          </head>
          <body>client</body>
        </html>,
      );
    });

    // @gate enableFloat && !enableHostSingletons
    it('retains styles even when a new html, head, and/body mount - without HostSingleton', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="stylesheet" href="foo" precedence="foo" />
              <link rel="stylesheet" href="bar" precedence="bar" />
              server
            </body>
          </html>,
        );
        pipe(writable);
      });
      const errors = [];
      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head>
            <link rel="stylesheet" href="qux" precedence="qux" />
            <link rel="stylesheet" href="foo" precedence="foo" />
          </head>
          <body>client</body>
        </html>,
        {
          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(
        <html>
          <head>
            <link rel="stylesheet" href="qux" data-precedence="qux" />
            <link rel="stylesheet" href="foo" data-precedence="foo" />
          </head>
          <body>client</body>
        </html>,
      );
    });

    // @gate enableFloat && enableHostSingletons
    it('retains styles in head through head remounts', async () => {
      const root = ReactDOMClient.createRoot(document);
      root.render(
        <html>
          <head key={1} />
          <body>
            <link rel="stylesheet" href="foo" precedence="foo" />
            <link rel="stylesheet" href="bar" precedence="bar" />
            {null}
            hello
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <link rel="stylesheet" href="bar" data-precedence="bar" />
          </head>
          <body>hello</body>
        </html>,
      );

      root.render(
        <html>
          <head key={2} />
          <body>
            <link rel="stylesheet" href="foo" precedence="foo" />
            {null}
            <link rel="stylesheet" href="baz" precedence="baz" />
            hello
          </body>
        </html>,
      );
      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(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <link rel="stylesheet" href="bar" data-precedence="bar" />
            <link rel="stylesheet" href="baz" data-precedence="baz" />
            <link rel="preload" href="baz" as="style" />
          </head>
          <body>hello</body>
        </html>,
      );
    });
  });

  describe('script resources', () => {
    // @gate enableFloat
    it('treats async scripts without onLoad or onError as Resources', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <script src="foo" async={true} />
              <script src="bar" async={true} onLoad={() => {}} />
              <script src="baz" data-meaningful="" />
              hello world
            </body>
          </html>,
        );
        pipe(writable);
      });
      // 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(
        <html>
          <head>
            <script src="foo" async="" />
            <link rel="preload" href="bar" as="script" />
          </head>
          <body>
            <script src="baz" data-meaningful="" />
            hello world
          </body>
        </html>,
      );

      ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <script src="foo" async={true} />
            <script src="bar" async={true} onLoad={() => {}} />
            <script src="baz" data-meaningful="" />
            hello world
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      // The async script with onLoad is inserted in the right place but does not cause the hydration
      // to fail.
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <script src="foo" async="" />
            <link rel="preload" href="bar" as="script" />
          </head>
          <body>
            <script src="bar" async="" />
            <script src="baz" data-meaningful="" />
            hello world
          </body>
        </html>,
      );
    });
  });

  // @gate enableFloat
  it('client renders a boundary if a style Resource dependency fails to load', async () => {
    function BlockedOn({text, children}) {
      readText(text);
      return children;
    }
    function App() {
      return (
        <html>
          <head />
          <body>
            <Suspense fallback="loading...">
              <BlockedOn text="unblock">
                <link rel="stylesheet" href="foo" precedence="arbitrary" />
                <link rel="stylesheet" href="bar" precedence="arbitrary" />
                Hello
              </BlockedOn>
            </Suspense>
          </body>
        </html>
      );
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(<App />);
      pipe(writable);
    });

    await act(() => {
      resolveText('unblock');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="arbitrary" />
          <link rel="stylesheet" href="bar" data-precedence="arbitrary" />
        </head>
        <body>
          loading...
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
        </body>
      </html>,
    );

    await act(() => {
      const barLink = document.querySelector(
        'link[rel="stylesheet"][href="bar"]',
      );
      const event = document.createEvent('Events');
      event.initEvent('error', true, true);
      barLink.dispatchEvent(event);
    });

    const boundaryTemplateInstance = document.getElementById('B:0');
    const suspenseInstance = boundaryTemplateInstance.previousSibling;

    expect(suspenseInstance.data).toEqual('$!');
    expect(boundaryTemplateInstance.dataset.dgst).toBe(
      'Resource failed to load',
    );

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="arbitrary" />
          <link rel="stylesheet" href="bar" data-precedence="arbitrary" />
        </head>
        <body>
          loading...
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
        </body>
      </html>,
    );

    const errors = [];
    ReactDOMClient.hydrateRoot(document, <App />, {
      onRecoverableError(err, errInfo) {
        errors.push(err.message);
        errors.push(err.digest);
      },
    });
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="arbitrary" />
          <link rel="stylesheet" href="bar" data-precedence="arbitrary" />
        </head>
        <body>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          Hello
        </body>
      </html>,
    );
    expect(errors).toEqual([
      'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
      'Resource failed to load',
    ]);
  });

  // @gate enableFloat
  it('treats stylesheet links with a precedence as a resource', async () => {
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <link rel="stylesheet" href="foo" precedence="arbitrary" />
            Hello
          </body>
        </html>,
      );
      pipe(writable);
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="arbitrary" />
        </head>
        <body>Hello</body>
      </html>,
    );

    ReactDOMClient.hydrateRoot(
      document,
      <html>
        <head />
        <body>Hello</body>
      </html>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="arbitrary" />
        </head>
        <body>Hello</body>
      </html>,
    );
  });

  // @gate enableFloat
  it('inserts text separators following text when followed by an element that is converted to a resource and thus removed from the html inline', async () => {
    // If you render many of these as siblings the values get emitted as a single text with no separator sometimes
    // because the link gets elided as a resource
    function AsyncTextWithResource({text, href, precedence}) {
      const value = readText(text);
      return (
        <>
          {value}
          <link rel="stylesheet" href={href} precedence={precedence} />
        </>
      );
    }

    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <AsyncTextWithResource text="foo" href="foo" precedence="one" />
            <AsyncTextWithResource text="bar" href="bar" precedence="two" />
            <AsyncTextWithResource text="baz" href="baz" precedence="three" />
          </body>
        </html>,
      );
      pipe(writable);
      resolveText('foo');
      resolveText('bar');
      resolveText('baz');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
          <link rel="stylesheet" href="baz" data-precedence="three" />
        </head>
        <body>
          {'foo'}
          {'bar'}
          {'baz'}
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('hoists late stylesheets the correct precedence', async () => {
    function AsyncListItemWithResource({text, href, precedence, ...rest}) {
      const value = readText(text);
      return (
        <li>
          <link
            rel="stylesheet"
            href={href}
            precedence={precedence}
            {...rest}
          />
          {value}
        </li>
      );
    }
    function BlockingChildren({text, children}) {
      readText(text);
      return children;
    }
    function PresetPrecedence() {
      ReactDOM.preinit('preset', {as: 'style', precedence: 'preset'});
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <link rel="stylesheet" href="initial" precedence="one" />
            <PresetPrecedence />
            <div>
              <Suspense fallback="loading foo bar...">
                <link rel="stylesheet" href="foo" precedence="one" />
                <ul>
                  <li>
                    <AsyncText text="foo" />
                  </li>
                  <AsyncListItemWithResource
                    text="bar"
                    href="bar"
                    precedence="default"
                    data-foo="foo"
                    crossOrigin="anonymous"
                  />
                </ul>
              </Suspense>
            </div>
            <div>
              <Suspense fallback="loading bar baz qux...">
                <ul>
                  <AsyncListItemWithResource
                    text="bar"
                    href="bar"
                    precedence="default"
                  />
                  <AsyncListItemWithResource
                    text="baz"
                    href="baz"
                    precedence="two"
                  />
                  <AsyncListItemWithResource
                    text="qux"
                    href="qux"
                    precedence="one"
                  />
                </ul>
              </Suspense>
            </div>
            <div>
              <Suspense fallback="loading bar baz qux...">
                <BlockingChildren text="unblock">
                  <ul>
                    <AsyncListItemWithResource
                      text="bar"
                      href="bar"
                      precedence="default"
                    />
                    <AsyncListItemWithResource
                      text="baz"
                      href="baz"
                      precedence="two"
                    />
                    <AsyncListItemWithResource
                      text="qux"
                      href="qux"
                      precedence="one"
                    />
                  </ul>
                </BlockingChildren>
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>loading foo bar...</div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('foo');
      resolveText('bar');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>loading foo bar...</div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
          <link rel="preload" href="bar" as="style" crossorigin="anonymous" />
        </body>
      </html>,
    );

    await act(() => {
      const link = document.querySelector('link[rel="stylesheet"][href="foo"]');
      const event = document.createEvent('Events');
      event.initEvent('load', true, true);
      link.dispatchEvent(event);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>loading foo bar...</div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
          <link rel="preload" href="bar" as="style" crossorigin="anonymous" />
        </body>
      </html>,
    );

    await act(() => {
      const link = document.querySelector('link[rel="stylesheet"][href="bar"]');
      const event = document.createEvent('Events');
      event.initEvent('load', true, true);
      link.dispatchEvent(event);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>
            <ul>
              <li>foo</li>
              <li>bar</li>
            </ul>
          </div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
          <link rel="preload" href="bar" as="style" crossorigin="anonymous" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('baz');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>
            <ul>
              <li>foo</li>
              <li>bar</li>
            </ul>
          </div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
          <link rel="preload" as="style" href="bar" crossorigin="anonymous" />
          <link rel="preload" as="style" href="baz" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('qux');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="qux" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="stylesheet" href="baz" data-precedence="two" />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>
            <ul>
              <li>foo</li>
              <li>bar</li>
            </ul>
          </div>
          <div>loading bar baz qux...</div>
          <div>loading bar baz qux...</div>
          <link rel="preload" as="style" href="bar" crossorigin="anonymous" />
          <link rel="preload" as="style" href="baz" />
          <link rel="preload" as="style" href="qux" />
        </body>
      </html>,
    );

    await act(() => {
      const bazlink = document.querySelector(
        'link[rel="stylesheet"][href="baz"]',
      );
      const quxlink = document.querySelector(
        'link[rel="stylesheet"][href="qux"]',
      );
      const presetLink = document.querySelector(
        'link[rel="stylesheet"][href="preset"]',
      );
      const event = document.createEvent('Events');
      event.initEvent('load', true, true);
      bazlink.dispatchEvent(event);
      quxlink.dispatchEvent(event);
      presetLink.dispatchEvent(event);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="qux" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="stylesheet" href="baz" data-precedence="two" />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>
            <ul>
              <li>foo</li>
              <li>bar</li>
            </ul>
          </div>
          <div>
            <ul>
              <li>bar</li>
              <li>baz</li>
              <li>qux</li>
            </ul>
          </div>
          <div>loading bar baz qux...</div>
          <link rel="preload" as="style" href="bar" crossorigin="anonymous" />
          <link rel="preload" as="style" href="baz" />
          <link rel="preload" as="style" href="qux" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('unblock');
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="initial" data-precedence="one" />
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="qux" data-precedence="one" />
          <link rel="stylesheet" href="preset" data-precedence="preset" />
          <link
            rel="stylesheet"
            href="bar"
            data-precedence="default"
            data-foo="foo"
            crossorigin="anonymous"
          />
          <link rel="stylesheet" href="baz" data-precedence="two" />
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div>
            <ul>
              <li>foo</li>
              <li>bar</li>
            </ul>
          </div>
          <div>
            <ul>
              <li>bar</li>
              <li>baz</li>
              <li>qux</li>
            </ul>
          </div>
          <div>
            <ul>
              <li>bar</li>
              <li>baz</li>
              <li>qux</li>
            </ul>
          </div>
          <link rel="preload" as="style" href="bar" crossorigin="anonymous" />
          <link rel="preload" as="style" href="baz" />
          <link rel="preload" as="style" href="qux" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('normalizes style resource precedence for all boundaries inlined as part of the shell flush', async () => {
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              outer
              <link rel="stylesheet" href="1one" precedence="one" />
              <link rel="stylesheet" href="1two" precedence="two" />
              <link rel="stylesheet" href="1three" precedence="three" />
              <link rel="stylesheet" href="1four" precedence="four" />
              <Suspense fallback={null}>
                <div>
                  middle
                  <link rel="stylesheet" href="2one" precedence="one" />
                  <link rel="stylesheet" href="2two" precedence="two" />
                  <link rel="stylesheet" href="2three" precedence="three" />
                  <link rel="stylesheet" href="2four" precedence="four" />
                  <Suspense fallback={null}>
                    <div>
                      inner
                      <link rel="stylesheet" href="3five" precedence="five" />
                      <link rel="stylesheet" href="3one" precedence="one" />
                      <link rel="stylesheet" href="3two" precedence="two" />
                      <link rel="stylesheet" href="3three" precedence="three" />
                      <link rel="stylesheet" href="3four" precedence="four" />
                    </div>
                  </Suspense>
                </div>
              </Suspense>
              <Suspense fallback={null}>
                <div>middle</div>
                <link rel="stylesheet" href="4one" precedence="one" />
                <link rel="stylesheet" href="4two" precedence="two" />
                <link rel="stylesheet" href="4three" precedence="three" />
                <link rel="stylesheet" href="4four" precedence="four" />
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    // The reason the href's aren't ordered linearly is that when boundaries complete their resources
    // get hoisted to the shell directly so they can flush in the head. If a boundary doesn't suspend then
    // child boundaries will complete before the parent boundary and thus have their resources hoist
    // early. The reason precedences are still ordered correctly between child and parent is because
    // the precedence ordering is determined upon first discovernig a resource rather than on hoist and
    // so it follows render order
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="1one" data-precedence="one" />
          <link rel="stylesheet" href="3one" data-precedence="one" />
          <link rel="stylesheet" href="2one" data-precedence="one" />
          <link rel="stylesheet" href="4one" data-precedence="one" />

          <link rel="stylesheet" href="1two" data-precedence="two" />
          <link rel="stylesheet" href="3two" data-precedence="two" />
          <link rel="stylesheet" href="2two" data-precedence="two" />
          <link rel="stylesheet" href="4two" data-precedence="two" />

          <link rel="stylesheet" href="1three" data-precedence="three" />
          <link rel="stylesheet" href="3three" data-precedence="three" />
          <link rel="stylesheet" href="2three" data-precedence="three" />
          <link rel="stylesheet" href="4three" data-precedence="three" />

          <link rel="stylesheet" href="1four" data-precedence="four" />
          <link rel="stylesheet" href="3four" data-precedence="four" />
          <link rel="stylesheet" href="2four" data-precedence="four" />
          <link rel="stylesheet" href="4four" data-precedence="four" />

          <link rel="stylesheet" href="3five" data-precedence="five" />
        </head>
        <body>
          <div>
            outer
            <div>
              middle<div>inner</div>
            </div>
            <div>middle</div>
          </div>
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('style resources are inserted according to precedence order on the client', async () => {
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              <link rel="stylesheet" href="foo" precedence="one" />
              <link rel="stylesheet" href="bar" precedence="two" />
              Hello
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
        </head>
        <body>
          <div>Hello</div>
        </body>
      </html>,
    );

    const root = ReactDOMClient.hydrateRoot(
      document,
      <html>
        <head />
        <body>
          <div>
            <link rel="stylesheet" href="foo" precedence="one" />
            <link rel="stylesheet" href="bar" precedence="two" />
            Hello
          </div>
        </body>
      </html>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
        </head>
        <body>
          <div>Hello</div>
        </body>
      </html>,
    );

    root.render(
      <html>
        <head />
        <body>
          <div>Hello</div>
          <link rel="stylesheet" href="baz" precedence="one" />
        </body>
      </html>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="baz" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
          <link rel="preload" as="style" href="baz" />
        </head>
        <body>
          <div>Hello</div>
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('inserts preloads in render phase eagerly', async () => {
    function Throw() {
      throw new Error('Uh oh!');
    }
    class ErrorBoundary extends React.Component {
      state = {hasError: false, error: null};
      static getDerivedStateFromError(error) {
        return {
          hasError: true,
          error,
        };
      }
      render() {
        if (this.state.hasError) {
          return this.state.error.message;
        }
        return this.props.children;
      }
    }

    const root = ReactDOMClient.createRoot(container);
    root.render(
      <ErrorBoundary>
        <link rel="stylesheet" href="foo" precedence="default" />
        <div>foo</div>
        <Throw />
      </ErrorBoundary>,
    );
    expect(Scheduler).toFlushWithoutYielding();
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="preload" href="foo" as="style" />
        </head>
        <body>
          <div id="container">Uh oh!</div>
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('does not emit preinit stylesheets if they are invoked after the shell flushes', async () => {
    function PreinitsBlockedOn({text}) {
      readText(text);
      ReactDOM.preinit('one', {precedence: 'one', as: 'style'});
      ReactDOM.preinit('two', {precedence: 'two', as: 'style'});
      return null;
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              <link rel="stylesheet" href="foo" precedence="one" />
              <link rel="stylesheet" href="bar" precedence="two" />
              Hello
            </div>
            <div>
              <Suspense fallback={'loading...'}>
                <PreinitsBlockedOn text="foo" />
                <AsyncText text="bar" />
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
        </head>
        <body>
          <div>Hello</div>
          <div>loading...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('foo');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
        </head>
        <body>
          <div>Hello</div>
          <div>loading...</div>
          <link rel="preload" href="one" as="style" />
          <link rel="preload" href="two" as="style" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('bar');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="one" />
          <link rel="stylesheet" href="bar" data-precedence="two" />
        </head>
        <body>
          <div>Hello</div>
          <div>bar</div>
          <link rel="preload" href="one" as="style" />
          <link rel="preload" href="two" as="style" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('will include child boundary style resources in the boundary reveal instruction', async () => {
    function BlockedOn({text, children}) {
      readText(text);
      return children;
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              <Suspense fallback="loading foo...">
                <BlockedOn text="foo">
                  <div>foo</div>
                  <link rel="stylesheet" href="foo" precedence="default" />
                  <Suspense fallback="loading bar...">
                    <BlockedOn text="bar">
                      <div>bar</div>
                      <link rel="stylesheet" href="bar" precedence="default" />
                      <Suspense fallback="loading baz...">
                        <BlockedOn text="baz">
                          <div>baz</div>
                          <link
                            rel="stylesheet"
                            href="baz"
                            precedence="default"
                          />
                        </BlockedOn>
                      </Suspense>
                    </BlockedOn>
                  </Suspense>
                </BlockedOn>
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading foo...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('bar');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading foo...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('baz');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading foo...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('foo');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="default" />
          <link rel="stylesheet" href="bar" data-precedence="default" />
          <link rel="stylesheet" href="baz" data-precedence="default" />
        </head>
        <body>
          <div>loading foo...</div>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          <link rel="preload" href="baz" as="style" />
        </body>
      </html>,
    );

    await act(() => {
      const event = document.createEvent('Events');
      event.initEvent('load', true, true);
      Array.from(document.querySelectorAll('link[rel="stylesheet"]')).forEach(
        el => {
          el.dispatchEvent(event);
        },
      );
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="default" />
          <link rel="stylesheet" href="bar" data-precedence="default" />
          <link rel="stylesheet" href="baz" data-precedence="default" />
        </head>
        <body>
          <div>
            <div>foo</div>
            <div>bar</div>
            <div>baz</div>
          </div>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          <link rel="preload" href="baz" as="style" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('will hoist resources of child boundaries emitted as part of a partial boundary to the parent boundary', async () => {
    function BlockedOn({text, children}) {
      readText(text);
      return children;
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              <Suspense fallback="loading...">
                <div>
                  <BlockedOn text="foo">
                    <div>foo</div>
                    <link rel="stylesheet" href="foo" precedence="default" />
                    <Suspense fallback="loading bar...">
                      <BlockedOn text="bar">
                        <div>bar</div>
                        <link
                          rel="stylesheet"
                          href="bar"
                          precedence="default"
                        />
                        <Suspense fallback="loading baz...">
                          <div>
                            <BlockedOn text="baz">
                              <div>baz</div>
                              <link
                                rel="stylesheet"
                                href="baz"
                                precedence="default"
                              />
                            </BlockedOn>
                          </div>
                        </Suspense>
                      </BlockedOn>
                    </Suspense>
                  </BlockedOn>
                  <BlockedOn text="qux">
                    <div>qux</div>
                    <link rel="stylesheet" href="qux" precedence="default" />
                  </BlockedOn>
                </div>
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });

    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading...</div>
        </body>
      </html>,
    );

    // This will enqueue a style resource in a deep blocked boundary (loading baz...).
    await act(() => {
      resolveText('baz');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading...</div>
        </body>
      </html>,
    );

    // This will enqueue a style resource in the intermediate blocked boundary (loading bar...).
    await act(() => {
      resolveText('bar');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading...</div>
        </body>
      </html>,
    );

    // This will complete a segment in the top level boundary that is still blocked on another segment.
    // It will flush the completed segment however the inner boundaries should not emit their style dependencies
    // because they are not going to be revealed yet. instead their dependencies are hoisted to the blocked
    // boundary (top level).
    await act(() => {
      resolveText('foo');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading...</div>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          <link rel="preload" href="baz" as="style" />
        </body>
      </html>,
    );

    // This resolves the last blocked segment on the top level boundary so we see all dependencies of the
    // nested boundaries emitted at this level
    await act(() => {
      resolveText('qux');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="default" />
          <link rel="stylesheet" href="bar" data-precedence="default" />
          <link rel="stylesheet" href="baz" data-precedence="default" />
          <link rel="stylesheet" href="qux" data-precedence="default" />
        </head>
        <body>
          <div>loading...</div>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          <link rel="preload" href="baz" as="style" />
          <link rel="preload" href="qux" as="style" />
        </body>
      </html>,
    );

    // We load all stylesheets and confirm the content is revealed
    await act(() => {
      const event = document.createEvent('Events');
      event.initEvent('load', true, true);
      Array.from(document.querySelectorAll('link[rel="stylesheet"]')).forEach(
        el => {
          el.dispatchEvent(event);
        },
      );
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="foo" data-precedence="default" />
          <link rel="stylesheet" href="bar" data-precedence="default" />
          <link rel="stylesheet" href="baz" data-precedence="default" />
          <link rel="stylesheet" href="qux" data-precedence="default" />
        </head>
        <body>
          <div>
            <div>
              <div>foo</div>
              <div>bar</div>
              <div>
                <div>baz</div>
              </div>
              <div>qux</div>
            </div>
          </div>
          <link rel="preload" href="foo" as="style" />
          <link rel="preload" href="bar" as="style" />
          <link rel="preload" href="baz" as="style" />
          <link rel="preload" href="qux" as="style" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('encodes attributes consistently whether resources are flushed in shell or in late boundaries', async () => {
    const originalConsoleError = console.error;
    const mockError = jest.fn();
    console.error = (...args) => {
      mockError(...args.map(normalizeCodeLocInfo));
    };
    function BlockedOn({text, children}) {
      readText(text);
      return children;
    }
    function App() {
      return (
        <html>
          <head />
          <body>
            <div>
              <link
                // This preload is explicit so it can flush with a lot of potential attrs
                // We will duplicate this as a style that flushes after the shell
                rel="preload"
                as="style"
                href="foo"
                // precedence is not a special attribute for preloads so this will just flush as is
                precedence="default"
                // Some standard link props
                crossOrigin="anonymous"
                media="all"
                integrity="somehash"
                referrerPolicy="origin"
                // data and non starndard attributes that should flush
                data-foo={'"quoted"'}
                nonStandardAttr="attr"
                properlyformattednonstandardattr="attr"
                // attributes that should be filtered out for violating certain rules
                onSomething="this should be removed b/c event handler"
                shouldnotincludefunctions={() => {}}
                norsymbols={Symbol('foo')}
              />
              <Suspense fallback={'loading...'}>
                <BlockedOn text="unblock">
                  <link
                    // This preload is explicit so it can flush with a lot of potential attrs
                    // We will duplicate this as a style that flushes after the shell
                    rel="stylesheet"
                    href="foo"
                    // opt-in property to get this treated as a resource
                    precedence="default"
                    // Some standard link props
                    crossOrigin="anonymous"
                    media="all"
                    integrity="somehash"
                    referrerPolicy="origin"
                    // data and non starndard attributes that should flush
                    data-foo={'"quoted"'}
                    nonStandardAttr="attr"
                    properlyformattednonstandardattr="attr"
                    // attributes that should be filtered out for violating certain rules
                    onSomething="this should be removed b/c event handler"
                    shouldnotincludefunctions={() => {}}
                    norsymbols={Symbol('foo')}
                  />
                </BlockedOn>
              </Suspense>
            </div>
          </body>
        </html>
      );
    }
    try {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(<App />);
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link
              rel="preload"
              as="style"
              href="foo"
              precedence="default"
              crossorigin="anonymous"
              media="all"
              integrity="somehash"
              referrerpolicy="origin"
              data-foo={'"quoted"'}
              nonstandardattr="attr"
              properlyformattednonstandardattr="attr"
            />
          </head>
          <body>
            <div>loading...</div>
          </body>
        </html>,
      );
      if (__DEV__) {
        expect(mockError).toHaveBeenCalledTimes(2);
        expect(mockError).toHaveBeenCalledWith(
          'Warning: React does not recognize the `%s` prop on a DOM element.' +
            ' If you intentionally want it to appear in the DOM as a custom attribute,' +
            ' spell it as lowercase `%s` instead. If you accidentally passed it from a' +
            ' parent component, remove it from the DOM element.%s',
          'nonStandardAttr',
          'nonstandardattr',
          componentStack(['link', 'div', 'body', 'html', 'App']),
        );
        expect(mockError).toHaveBeenCalledWith(
          'Warning: Invalid values for props %s on <%s> tag. Either remove them from' +
            ' the element, or pass a string or number value to keep them in the DOM. For' +
            ' details, see https://reactjs.org/link/attribute-behavior %s',
          '`shouldnotincludefunctions`, `norsymbols`',
          'link',
          componentStack(['link', 'div', 'body', 'html', 'App']),
        );
        mockError.mockClear();
      } else {
        expect(mockError).not.toHaveBeenCalled();
      }

      // Now we flush the stylesheet with the boundary
      await act(() => {
        resolveText('unblock');
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link
              rel="stylesheet"
              href="foo"
              data-precedence="default"
              crossorigin="anonymous"
              media="all"
              integrity="somehash"
              referrerpolicy="origin"
              data-foo={'"quoted"'}
              nonstandardattr="attr"
              properlyformattednonstandardattr="attr"
            />
            <link
              rel="preload"
              as="style"
              href="foo"
              precedence="default"
              crossorigin="anonymous"
              media="all"
              integrity="somehash"
              referrerpolicy="origin"
              data-foo={'"quoted"'}
              nonstandardattr="attr"
              properlyformattednonstandardattr="attr"
            />
          </head>
          <body>
            <div>loading...</div>
          </body>
        </html>,
      );
      if (__DEV__) {
        // The way the test is currently set up the props that would warn have already warned
        // so no new warnings appear. This is really testing the same code pathway so
        // exercising that more here isn't all that useful
        expect(mockError).toHaveBeenCalledTimes(0);
      } else {
        expect(mockError).not.toHaveBeenCalled();
      }
    } finally {
      console.error = originalConsoleError;
    }
  });

  // @gate enableFloat
  it('boundary style resource dependencies hoist to a parent boundary when flushed inline', async () => {
    function BlockedOn({text, children}) {
      readText(text);
      return children;
    }
    await actIntoEmptyDocument(() => {
      const {pipe} = renderToPipeableStream(
        <html>
          <head />
          <body>
            <div>
              <Suspense fallback="loading A...">
                <BlockedOn text="unblock">
                  <AsyncText text="A" />
                  <link rel="stylesheet" href="A" precedence="A" />
                  <Suspense fallback="loading AA...">
                    <AsyncText text="AA" />
                    <link rel="stylesheet" href="AA" precedence="AA" />
                    <Suspense fallback="loading AAA...">
                      <AsyncText text="AAA" />
                      <link rel="stylesheet" href="AAA" precedence="AAA" />
                      <Suspense fallback="loading AAAA...">
                        <AsyncText text="AAAA" />
                        <link rel="stylesheet" href="AAAA" precedence="AAAA" />
                      </Suspense>
                    </Suspense>
                  </Suspense>
                </BlockedOn>
              </Suspense>
            </div>
          </body>
        </html>,
      );
      pipe(writable);
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading A...</div>
        </body>
      </html>,
    );

    await act(() => {
      resolveText('unblock');
      resolveText('AAAA');
      resolveText('AA');
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head />
        <body>
          <div>loading A...</div>
          <link rel="preload" as="style" href="A" />
          <link rel="preload" as="style" href="AA" />
          <link rel="preload" as="style" href="AAA" />
          <link rel="preload" as="style" href="AAAA" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('A');
    });
    await act(() => {
      document.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
        const event = document.createEvent('Events');
        event.initEvent('load', true, true);
        l.dispatchEvent(event);
      });
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="A" data-precedence="A" />
          <link rel="stylesheet" href="AA" data-precedence="AA" />
        </head>
        <body>
          <div>
            {'A'}
            {'AA'}
            {'loading AAA...'}
          </div>
          <link rel="preload" as="style" href="A" />
          <link rel="preload" as="style" href="AA" />
          <link rel="preload" as="style" href="AAA" />
          <link rel="preload" as="style" href="AAAA" />
        </body>
      </html>,
    );

    await act(() => {
      resolveText('AAA');
    });
    await act(() => {
      document.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
        const event = document.createEvent('Events');
        event.initEvent('load', true, true);
        l.dispatchEvent(event);
      });
    });
    expect(getMeaningfulChildren(document)).toEqual(
      <html>
        <head>
          <link rel="stylesheet" href="A" data-precedence="A" />
          <link rel="stylesheet" href="AA" data-precedence="AA" />
          <link rel="stylesheet" href="AAA" data-precedence="AAA" />
          <link rel="stylesheet" href="AAAA" data-precedence="AAAA" />
        </head>
        <body>
          <div>
            {'A'}
            {'AA'}
            {'AAA'}
            {'AAAA'}
          </div>
          <link rel="preload" as="style" href="A" />
          <link rel="preload" as="style" href="AA" />
          <link rel="preload" as="style" href="AAA" />
          <link rel="preload" as="style" href="AAAA" />
        </body>
      </html>,
    );
  });

  // @gate enableFloat
  it('always enforces crossOrigin "anonymous" for font preloads', async () => {
    const originalConsoleError = console.error;
    const mockError = jest.fn();
    console.error = (...args) => {
      mockError(...args.map(normalizeCodeLocInfo));
    };
    try {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="preload" as="font" href="foo" />
              <link rel="preload" as="font" href="bar" crossOrigin="foo" />
              <link
                rel="preload"
                as="font"
                href="baz"
                crossOrigin="use-credentials"
              />
              <link
                rel="preload"
                as="font"
                href="qux"
                crossOrigin="anonymous"
              />
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="preload" as="font" href="foo" crossorigin="" />
            <link rel="preload" as="font" href="bar" crossorigin="" />
            <link rel="preload" as="font" href="baz" crossorigin="" />
            <link rel="preload" as="font" href="qux" crossorigin="" />
          </head>
          <body />
        </html>,
      );

      if (__DEV__) {
        expect(mockError).toHaveBeenCalledTimes(2);
        expect(mockError).toHaveBeenCalledWith(
          'Warning: A %s with href "%s" did not specify the crossOrigin prop. Font preloads must always use' +
            ' anonymouse CORS mode. To fix add an empty string, "anonymous", or any other string' +
            ' value except "use-credentials" for the crossOrigin prop of all font preloads.%s',
          'preload Resource (as "font")',
          'foo',
          componentStack(['link', 'body', 'html']),
        );
        expect(mockError).toHaveBeenCalledWith(
          'Warning: A %s with href "%s" specified a crossOrigin value of "use-credentials". Font preloads must always use' +
            ' anonymouse CORS mode. To fix use an empty string, "anonymous", or any other string' +
            ' value except "use-credentials" for the crossOrigin prop of all font preloads.%s',
          'preload Resource (as "font")',
          'baz',
          componentStack(['link', 'body', 'html']),
        );
      } else {
        expect(mockError).not.toHaveBeenCalled();
      }
    } finally {
      console.error = originalConsoleError;
    }
  });

  describe('ReactDOM.pre* function validation', () => {
    function Preloads({scenarios}) {
      for (let i = 0; i < scenarios.length; i++) {
        const href = scenarios[i][0];
        const options = scenarios[i][1];
        ReactDOM.preload(href, options);
      }
    }
    function Preinits({scenarios}) {
      for (let i = 0; i < scenarios.length; i++) {
        const href = scenarios[i][0];
        const options = scenarios[i][1];
        ReactDOM.preinit(href, options);
      }
    }
    async function renderOnServer(Component, scenarios) {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <Component scenarios={scenarios} />
              </head>
            </html>,
          );
          pipe(writable);
        });
        for (let i = 0; i < scenarios.length; i++) {
          const assertion = scenarios[i][2];
          assertion(mockError, i);
        }
      } finally {
        console.error = originalConsoleError;
      }
    }
    async function renderOnClient(Component, scenarios) {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        const root = ReactDOMClient.createRoot(document);
        root.render(
          <html>
            <head>
              <Component scenarios={scenarios} />
            </head>
          </html>,
        );
        expect(Scheduler).toFlushWithoutYielding();
        for (let i = 0; i < scenarios.length; i++) {
          const assertion = scenarios[i][2];
          assertion(mockError, i);
        }
      } finally {
        console.error = originalConsoleError;
      }
    }

    [
      ['server', renderOnServer],
      ['client', renderOnClient],
    ].forEach(([environment, render]) => {
      // @gate enableFloat
      it(
        'warns when an invalid href argument is provided to ReactDOM.preload on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preload() expected the first argument to be a string representing an href but found %s instead.%s';
          const expectedStack = componentStack(['Preloads', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preloads, [
            [
              '',
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('an empty string'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              undefined,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('undefined'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              null,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              232132,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "number"'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              {},
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "object"'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );

      // @gate enableFloat
      it(
        'warns when an invalid href argument is provided to ReactDOM.preinit on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preinit() expected the first argument to be a string representing an href but found %s instead.%s';
          const expectedStack = componentStack(['Preinits', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preinits, [
            [
              '',
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('an empty string'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              undefined,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('undefined'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              null,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              232132,
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "number"'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              {},
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "object"'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );

      // @gate enableFloat
      it(
        'warns when an invalid options argument is provided to ReactDOM.preload on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preload() expected the second argument to be an options argument containing at least an "as" property' +
            ' specifying the Resource type. It found %s instead. The href for the preload call where this warning originated is "%s".%s';
          const expectedStack = componentStack(['Preloads', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preloads, [
            [
              'foo',
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('undefined', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              null,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              'bar',
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "string"', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              123,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "number"', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );

      // @gate enableFloat
      it(
        'warns when an invalid options argument is provided to ReactDOM.preinit on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preinit() expected the second argument to be an options argument containing at least an "as" property' +
            ' specifying the Resource type. It found %s instead. The href for the preload call where this warning originated is "%s".%s';
          const expectedStack = componentStack(['Preinits', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preinits, [
            [
              'foo',
              undefined,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('undefined', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              null,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              'bar',
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "string"', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'foo',
              123,
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "number"', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );

      // @gate enableFloat
      it(
        'warns when an invalid "as" option is provided to ReactDOM.preload on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preload() expected a valid "as" type in the options (second) argument but found %s instead.' +
            ' Please use one of the following valid values instead: %s. The href for the preload call where this' +
            ' warning originated is "%s".%s';
          const expectedStack = componentStack(['Preloads', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preloads, [
            [
              'foo',
              {},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs(
                      'undefined',
                      '"style", "font", or "script"',
                      'foo',
                    ),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'bar',
              {as: null},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null', '"style", "font", or "script"', 'bar'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'baz',
              {as: 123},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs(
                      'something with type "number"',
                      '"style", "font", or "script"',
                      'baz',
                    ),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'qux',
              {as: {}},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs(
                      'something with type "object"',
                      '"style", "font", or "script"',
                      'qux',
                    ),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'quux',
              {as: 'bar'},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('"bar"', '"style", "font", or "script"', 'quux'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );

      // @gate enableFloat
      it(
        'warns when an invalid "as" option is provided to ReactDOM.preinit on the ' +
          environment,
        async () => {
          const expectedMessage =
            'Warning: ReactDOM.preinit() expected the second argument to be an options argument containing at least an "as" property' +
            ' specifying the Resource type. It found %s instead. Currently, valid resource types for for preinit are "style"' +
            ' and "script". The href for the preinit call where this warning originated is "%s".%s';
          const expectedStack = componentStack(['Preinits', 'head', 'html']);
          function makeArgs(...substitutions) {
            return [expectedMessage, ...substitutions, expectedStack];
          }
          await render(Preinits, [
            [
              'foo',
              {},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('undefined', 'foo'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'bar',
              {as: null},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('null', 'bar'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'baz',
              {as: 123},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "number"', 'baz'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'qux',
              {as: {}},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('something with type "object"', 'qux'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
            [
              'quux',
              {as: 'bar'},
              (mockError, scenarioNumber) => {
                if (__DEV__) {
                  expect(mockError.mock.calls[scenarioNumber]).toEqual(
                    makeArgs('"bar"', 'quux'),
                  );
                } else {
                  expect(mockError).not.toHaveBeenCalled();
                }
              },
            ],
          ]);
        },
      );
    });
  });

  describe('prop validation', () => {
    // @gate enableFloat
    it('warns when you change props on a resource unless you also change the href', async () => {
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <div>
          <link
            rel="stylesheet"
            href="foo"
            precedence="foo"
            data-something-extra="extra"
          />
          <link
            rel="stylesheet"
            href="bar"
            precedence="bar"
            data-something-extra="extra"
          />
          <script src="sfoo" async={true} data-something-extra="extra" />
          <script src="sbar" async={true} data-something-extra="extra" />
          hello
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();

      root.render(
        <div>
          <link
            rel="stylesheet"
            href="foo"
            precedence="fu"
            data-something-new="new"
          />
          <link
            rel="stylesheet"
            href="baz"
            precedence="baz"
            data-something-new="new"
          />
          <script src="sfoo" async={true} data-something-new="new" />
          <script src="sbaz" async={true} data-something-new="new" />
          hello
        </div>,
      );
      expect(() => {
        expect(Scheduler).toFlushWithoutYielding();
      }).toErrorDev([
        'Warning: A style Resource with href "foo" received new props with different values from the props used' +
          ' when this Resource was first rendered. React will only use the props provided when' +
          ' this resource was first rendered until a new href is provided. Unlike conventional' +
          ' DOM elements, Resources instances do not have a one to one correspondence with Elements' +
          ' in the DOM and as such, every instance of a Resource for a single Resource identifier' +
          ' (href) must have props that agree with each other. The differences are described below.' +
          '\n  data-something-extra: missing or null in latest props, "extra" in original props' +
          '\n  data-something-new: "new" in latest props, missing or null in original props' +
          '\n  precedence: "fu" in latest props, "foo" in original props',
        'Warning: A script Resource with src "sfoo" received new props with different values from the props used' +
          ' when this Resource was first rendered. React will only use the props provided when' +
          ' this resource was first rendered until a new src is provided. Unlike conventional' +
          ' DOM elements, Resources instances do not have a one to one correspondence with Elements' +
          ' in the DOM and as such, every instance of a Resource for a single Resource identifier' +
          ' (src) must have props that agree with each other. The differences are described below.' +
          '\n  data-something-extra: missing or null in latest props, "extra" in original props' +
          '\n  data-something-new: "new" in latest props, missing or null in original props',
      ]);
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link
              rel="stylesheet"
              href="foo"
              data-precedence="foo"
              data-something-extra="extra"
            />
            <link
              rel="stylesheet"
              href="bar"
              data-precedence="bar"
              data-something-extra="extra"
            />
            <link
              rel="stylesheet"
              href="baz"
              data-precedence="baz"
              data-something-new="new"
            />
            <link rel="preload" as="style" href="foo" />
            <link rel="preload" as="style" href="bar" />
            <script src="sfoo" async="" data-something-extra="extra" />
            <script src="sbar" async="" data-something-extra="extra" />
            <link rel="preload" as="style" href="baz" />
            <script src="sbaz" async="" data-something-new="new" />
          </head>
          <body>
            <div id="container">
              <div>hello</div>
            </div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('warns when style Resource have different values for media for the same href', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <link
                  rel="stylesheet"
                  href="foo"
                  precedence="foo"
                  media="all"
                />
                <link rel="stylesheet" href="foo" precedence="foo" />

                <link rel="stylesheet" href="bar" precedence="bar" />
                <link
                  rel="stylesheet"
                  href="bar"
                  precedence="bar"
                  media="all"
                />

                <link
                  rel="stylesheet"
                  href="baz"
                  precedence="baz"
                  media="some"
                />
                <link
                  rel="stylesheet"
                  href="baz"
                  precedence="baz"
                  media="all"
                />
              </head>
            </html>,
          );
          pipe(writable);
        });
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <link
                rel="stylesheet"
                href="foo"
                data-precedence="foo"
                media="all"
              />
              <link rel="stylesheet" href="bar" data-precedence="bar" />

              <link
                rel="stylesheet"
                href="baz"
                data-precedence="baz"
                media="some"
              />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(3);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'foo',
            'an earlier instance of this Resource',
            'href',
            '\n  media: missing or null in latest props, "all" in original props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'bar',
            'an earlier instance of this Resource',
            'href',
            '\n  media: "all" in latest props, missing or null in original props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'baz',
            'an earlier instance of this Resource',
            'href',
            '\n  media: "all" in latest props, "some" in original props',
            componentStack(['link', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });

    // @gate enableFloat
    it('warns when style Resource props differ or are added for the same href', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <link
                  rel="stylesheet"
                  href="foo"
                  precedence="foo"
                  data-foo="an original value"
                />
                <link rel="stylesheet" href="foo" precedence="foo" />
                <link rel="stylesheet" href="foo" precedence="foonew" />

                <link rel="stylesheet" href="bar" precedence="bar" />
                <link
                  rel="stylesheet"
                  href="bar"
                  precedence="bar"
                  data-foo="a new value"
                />

                <link
                  rel="stylesheet"
                  href="baz"
                  precedence="baz"
                  data-foo="an original value"
                />
                <link
                  rel="stylesheet"
                  href="baz"
                  precedence="baz"
                  data-foo="a new value"
                />
              </head>
            </html>,
          );
          pipe(writable);
        });
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <link
                rel="stylesheet"
                href="foo"
                data-precedence="foo"
                data-foo="an original value"
              />
              <link rel="stylesheet" href="bar" data-precedence="bar" />
              <link
                rel="stylesheet"
                href="baz"
                data-precedence="baz"
                data-foo="an original value"
              />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(3);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'foo',
            'an earlier instance of this Resource',
            'href',
            '\n  precedence: "foonew" in latest props, "foo" in original props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'bar',
            'an earlier instance of this Resource',
            'href',
            '\n  data-foo: "a new value" in latest props, missing or null in original props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'baz',
            'an earlier instance of this Resource',
            'href',
            '\n  data-foo: "a new value" in latest props, "an original value" in original props',
            componentStack(['link', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });

    // @gate enableFloat
    it('warns when style Resource includes any combination of onLoad, onError, or disabled props', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <link
                  rel="stylesheet"
                  href="foo"
                  precedence="foo"
                  onLoad={() => {}}
                  onError={() => {}}
                />
                <link
                  rel="stylesheet"
                  href="bar"
                  precedence="bar"
                  onLoad={() => {}}
                />
                <link
                  rel="stylesheet"
                  href="baz"
                  precedence="baz"
                  onError={() => {}}
                />
                <link
                  rel="stylesheet"
                  href="qux"
                  precedence="qux"
                  disabled={true}
                />
              </head>
              <body />
            </html>,
          );
          pipe(writable);
        });
        // precedence is removed from the stylesheets because it is considered a reserved prop for
        // stylesheets to opt into resource semantics.
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <link rel="preload" as="style" href="foo" />
              <link rel="preload" as="style" href="bar" />
              <link rel="preload" as="style" href="baz" />
              <link rel="preload" as="style" href="qux" />
              <link rel="stylesheet" href="foo" />
              <link rel="stylesheet" href="bar" />
              <link rel="stylesheet" href="baz" />
              <link rel="stylesheet" href="qux" disabled="" />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(4);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A link (rel="stylesheet") element with href "%s" has the precedence prop but also included the %s.' +
              ' When using %s React will opt out of Resource behavior. If you meant for this' +
              ' element to be treated as a Resource remove the %s. Otherwise remove the precedence prop.%s',
            'foo',
            'onLoad and onError props',
            'onLoad, onError, or disabled',
            'onLoad and onError props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A link (rel="stylesheet") element with href "%s" has the precedence prop but also included the %s.' +
              ' When using %s React will opt out of Resource behavior. If you meant for this' +
              ' element to be treated as a Resource remove the %s. Otherwise remove the precedence prop.%s',
            'bar',
            'onLoad prop',
            'onLoad, onError, or disabled',
            'onLoad prop',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A link (rel="stylesheet") element with href "%s" has the precedence prop but also included the %s.' +
              ' When using %s React will opt out of Resource behavior. If you meant for this' +
              ' element to be treated as a Resource remove the %s. Otherwise remove the precedence prop.%s',
            'baz',
            'onError prop',
            'onLoad, onError, or disabled',
            'onError prop',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A link (rel="stylesheet") element with href "%s" has the precedence prop but also included the %s.' +
              ' When using %s React will opt out of Resource behavior. If you meant for this' +
              ' element to be treated as a Resource remove the %s. Otherwise remove the precedence prop.%s',
            'qux',
            'disabled prop',
            'onLoad, onError, or disabled',
            'disabled prop',
            componentStack(['link', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });

    // @gate enableFloat
    it('warns when script Resources have new or different values for props', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <script src="foo" async={true} data-foo="a current value" />
                <script src="foo" async={true} data-foo="a new value" />
              </head>
            </html>,
          );
          pipe(writable);
        });
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <script src="foo" async="" data-foo="a current value" />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(1);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'script Resource',
            'src',
            'foo',
            'an earlier instance of this Resource',
            'src',
            '\n  data-foo: "a new value" in latest props, "a current value" in original props',
            componentStack(['script', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });

    // @gate enableFloat
    it('warns when preload Resources have new or different values for props', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <link
                  rel="preload"
                  as="style"
                  href="foo"
                  data-foo="a current value"
                />
                <link
                  rel="preload"
                  as="style"
                  href="foo"
                  data-foo="a new value"
                />

                <link
                  rel="preload"
                  as="style"
                  href="bar"
                  data-bar="a current value"
                />
                <link
                  rel="preload"
                  as="font"
                  href="bar"
                  data-bar="a current value"
                  crossOrigin=""
                />
              </head>
            </html>,
          );
          pipe(writable);
        });
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <link
                rel="preload"
                as="style"
                href="foo"
                data-foo="a current value"
              />
              <link
                rel="preload"
                as="style"
                href="bar"
                data-bar="a current value"
              />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(2);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'preload Resource (as "style")',
            'href',
            'foo',
            'an earlier instance of this Resource',
            'href',
            '\n  data-foo: "a new value" in latest props, "a current value" in original props',
            componentStack(['link', 'head', 'html']),
          );
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s is using the same href "%s" as a %s. This is always an error and React will only keep the first preload' +
              ' for any given href, discarding subsequent instances. To fix, find where you are using this href in link' +
              ' tags or in calls to ReactDOM.preload() or ReactDOM.preinit() and either make the Resource types agree or' +
              ' update the hrefs to be distinct for different Resource types.%s',
            'preload Resource (as "font")',
            'bar',
            'preload Resource (as "style")',
            componentStack(['link', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });

    // @gate enableFloat
    it('warns when an existing preload Resource has certain specific different props from a style Resource of the same href', async () => {
      const originalConsoleError = console.error;
      const mockError = jest.fn();
      console.error = (...args) => {
        mockError(...args.map(normalizeCodeLocInfo));
      };
      try {
        await actIntoEmptyDocument(() => {
          const {pipe} = renderToPipeableStream(
            <html>
              <head>
                <link
                  rel="preload"
                  as="style"
                  href="foo"
                  crossOrigin="preload value"
                />
                <link
                  rel="stylesheet"
                  href="foo"
                  precedence="foo"
                  crossOrigin="style value"
                />
              </head>
            </html>,
          );
          pipe(writable);
        });
        expect(getMeaningfulChildren(document)).toEqual(
          <html>
            <head>
              <link
                rel="stylesheet"
                href="foo"
                data-precedence="foo"
                crossorigin="style value"
              />
            </head>
            <body />
          </html>,
        );

        if (__DEV__) {
          expect(mockError).toHaveBeenCalledTimes(1);
          expect(mockError).toHaveBeenCalledWith(
            'Warning: A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' +
              ' that were provided the first time they are encountered so any differences will be ignored. Please' +
              ' update Resources that share an %s to have props that agree. The differences are described below.%s%s',
            'style Resource',
            'href',
            'foo',
            'a preload Resource (as "style") with the same href',
            'href',
            '\n  crossOrigin: "style value" in latest props, "preload value" in original props',
            componentStack(['link', 'head', 'html']),
          );
        } else {
          expect(mockError).not.toHaveBeenCalled();
        }
      } finally {
        console.error = originalConsoleError;
      }
    });
  });

  describe('escaping', () => {
    // @gate enableFloat
    it('escapes hrefs when selecting matching elements in the document when rendering Resources', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="preload" href="preload" as="style" />
              <link rel="stylesheet" href="style" precedence="style" />
              <link rel="stylesheet" href="with\slashes" precedence="style" />
              <link rel="preload" href={'with\nnewline'} as="style" />
              <div id="container" />
            </body>
          </html>,
        );
        pipe(writable);
      });

      container = document.getElementById('container');
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <div>
          <link rel="preload" href={'preload"][rel="preload'} as="style" />
          <link
            rel="stylesheet"
            href={'style"][rel="stylesheet'}
            precedence="style"
          />
          <link rel="stylesheet" href={'with\\slashes'} precedence="style" />
          <link rel="preload" href={'with\nnewline'} as="style" />
          foo
        </div>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="style" data-precedence="style" />
            <link
              rel="stylesheet"
              href="with\slashes"
              data-precedence="style"
            />
            <link
              rel="stylesheet"
              href={'style"][rel="stylesheet'}
              data-precedence="style"
            />
            <link rel="preload" as="style" href="preload" />
            <link rel="preload" href={'with\nnewline'} as="style" />
            <link rel="preload" href={'preload"][rel="preload'} as="style" />
            <link rel="preload" href={'style"][rel="stylesheet'} as="style" />
          </head>
          <body>
            <div id="container">
              <div>foo</div>
            </div>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('escapes hrefs when selecting matching elements in the document when using preload and preinit', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <head />
            <body>
              <link rel="preload" href="preload" as="style" />
              <link rel="stylesheet" href="style" precedence="style" />
              <link rel="stylesheet" href="with\slashes" precedence="style" />
              <link rel="preload" href={'with\nnewline'} as="style" />
              <div id="container" />
            </body>
          </html>,
        );
        pipe(writable);
      });

      function App() {
        ReactDOM.preload('preload"][rel="preload', {as: 'style'});
        ReactDOM.preinit('style"][rel="stylesheet', {
          as: 'style',
          precedence: 'style',
        });
        ReactDOM.preinit('with\\slashes', {
          as: 'style',
          precedence: 'style',
        });
        ReactDOM.preload('with\nnewline', {as: 'style'});
        return <div>foo</div>;
      }

      container = document.getElementById('container');
      const root = ReactDOMClient.createRoot(container);
      root.render(<App />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="style" data-precedence="style" />
            <link
              rel="stylesheet"
              href="with\slashes"
              data-precedence="style"
            />
            <link
              rel="stylesheet"
              href={'style"][rel="stylesheet'}
              data-precedence="style"
            />
            <link rel="preload" as="style" href="preload" />
            <link rel="preload" href={'with\nnewline'} as="style" />
            <link rel="preload" href={'preload"][rel="preload'} as="style" />
          </head>
          <body>
            <div id="container">
              <div>foo</div>
            </div>
          </body>
        </html>,
      );
    });
  });

  describe('resource free contexts', () => {
    // @gate enableFloat
    it('allows resources inside foreignobject within an svg context', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <body>
              <svg>
                <foreignObject>
                  <title>foo</title>
                </foreignObject>
              </svg>
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>foo</title>
          </head>
          <body>
            <svg>
              <foreignobject />
            </svg>
          </body>
        </html>,
      );

      let root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <body>
            <svg>
              <foreignObject>
                <title>foo</title>
              </foreignObject>
            </svg>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      // @TODO the preload should not get inserted on hydration
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>foo</title>
          </head>
          <body>
            <svg>
              <foreignobject />
            </svg>
          </body>
        </html>,
      );

      root.unmount();
      root = ReactDOMClient.createRoot(document);
      root.render(
        <html>
          <body>
            <svg>
              <foreignObject>
                <title>foo</title>
              </foreignObject>
            </svg>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <title>foo</title>
          </head>
          <body>
            <svg>
              <foreignobject />
            </svg>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('warns if you render something that is almost a resource inside an svg tree', async () => {
      const root = ReactDOMClient.createRoot(container);
      root.render(
        <svg>
          <path>
            <link rel="stylesheet" href="foo" />
            <script src="foo" />
            <script async={true} src="bar" onLoad={() => {}} />
            <link rel="foo" href="bar" onLoad={() => {}} />
          </path>
        </svg>,
      );
      expect(() => {
        expect(Scheduler).toFlushWithoutYielding();
      }).toErrorDev([
        'Warning: Cannot render a <link rel="stylesheet" /> as a descendent of an <svg> element without knowing its precedence. Consider adding precedence="default" or moving it above the <svg> ancestor.',
        'Warning: Cannot render a sync or defer <script> as a descendent of an <svg> element. Try adding async="" or moving it above the ancestor <svg> element.',
        'Warning: Cannot render a <script> with onLoad or onError listeners as a descendent of an <svg> element. Try removing onLoad={...} and onError={...} or moving it above the ancestor <svg> element.',
        'Warning: Cannot render a <link> with onLoad or onError listeners as a descendent of <svg>. Try removing onLoad={...} and onError={...} or moving it above the <svg> ancestor.',
      ]);
    });

    // @gate enableFloat
    it('should support non-title resources in svg context', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <body>
              <svg>
                <title>foo</title>
                <link rel="foo" href="bar" />
                <link rel="stylesheet" href="bar" precedence="default" />
                <link rel="preload" href="bar" as="style" />
                <path>
                  <title>bar</title>
                  <script src="baz" async={true} />
                  <meta name="foo" content="bar" />
                  <base href="foo" />
                </path>
              </svg>
              <title>qux</title>
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <base href="foo" />
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <script src="baz" async="" />
            <link rel="foo" href="bar" />
            <meta name="foo" content="bar" />
            <title>qux</title>
          </head>
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );

      let root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <body>
            <svg>
              <title>foo</title>
              <link rel="foo" href="bar" />
              <link rel="stylesheet" href="bar" precedence="default" />
              <link rel="preload" href="bar" as="style" />
              <path>
                <title>bar</title>
                <script src="baz" async={true} />
                <meta name="foo" content="bar" />
                <base href="foo" />
              </path>
            </svg>
            <title>qux</title>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      // @TODO the preload should not get inserted on hydration
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <base href="foo" />
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <script src="baz" async="" />
            <link rel="foo" href="bar" />
            <meta name="foo" content="bar" />
            <title>qux</title>
            <link rel="preload" href="bar" as="style" />
          </head>
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );

      root.unmount();
      root = ReactDOMClient.createRoot(document);
      root.render(
        <html>
          <body>
            <svg>
              <title>foo</title>
              <link rel="foo" href="bar" />
              <link rel="stylesheet" href="bar" precedence="default" />
              <link rel="preload" href="bar" as="style" />
              <path>
                <title>bar</title>
                <script src="baz" async={true} />
                <meta name="foo" content="bar" />
                <base href="foo" />
              </path>
            </svg>
            <title>qux</title>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="bar" data-precedence="default" />
            <link rel="foo" href="bar" />
            <script src="baz" async="" />
            <meta name="foo" content="bar" />
            <base href="foo" />
            <title>qux</title>
          </head>
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );
    });

    it('should not treat title descendants of svg into resources', async () => {
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(
          <html>
            <body>
              <svg>
                <title>foo</title>
                <path>
                  <title>bar</title>
                </path>
              </svg>
            </body>
          </html>,
        );
        pipe(writable);
      });
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );

      let root = ReactDOMClient.hydrateRoot(
        document,
        <html>
          <head />
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );

      root.unmount();
      root = ReactDOMClient.createRoot(document);
      root.render(
        <html>
          <head />
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <svg>
              <title>foo</title>
              <path>
                <title>bar</title>
              </path>
            </svg>
          </body>
        </html>,
      );
    });

    // @gate enableFloat
    it('should not turn children of noscript into resources', async () => {
      function SomeResources() {
        return (
          <>
            <link rel="stylesheet" href="foo" precedence="foo" />
            <title>foo</title>
            <link rel="foobar" href="foobar" />
            <meta charSet="utf-8" />
            <meta property="og:image" content="foo" />
            <script async={true} src="script" />
          </>
        );
      }
      function Indirection({level, children}) {
        if (level > 0) {
          return <Indirection level={level - 1}>{children}</Indirection>;
        } else {
          return children;
        }
      }
      function App() {
        return (
          <html>
            <head>
              <SomeResources />
              <noscript>
                <SomeResources />
                <Indirection level={3}>
                  <SomeResources />
                </Indirection>
              </noscript>
              <SomeResources />
            </head>
          </html>
        );
      }
      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(<App />);
        pipe(writable);
      });

      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            {/* the actual resources */}
            <meta charset="utf-8" />
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <script async="" src="script" />
            <title>foo</title>
            <link rel="foobar" href="foobar" />
            <meta property="og:image" content="foo" />
            {/* the noscript children are encoded as a textNode when scripting is enabled */}
            <noscript>
              &lt;link rel="stylesheet"
              href="foo"/&gt;&lt;title&gt;foo&lt;/title&gt;&lt;link rel="foobar"
              href="foobar"/&gt;&lt;meta charSet="utf-8"/&gt;&lt;meta
              property="og:image" content="foo"/&gt;&lt;script async=""
              src="script"&gt;&lt;/script&gt;&lt;link rel="stylesheet"
              href="foo"/&gt;&lt;title&gt;foo&lt;/title&gt;&lt;link rel="foobar"
              href="foobar"/&gt;&lt;meta charSet="utf-8"/&gt;&lt;meta
              property="og:image" content="foo"/&gt;&lt;script async=""
              src="script"&gt;&lt;/script&gt;
            </noscript>
          </head>
          <body />
        </html>,
      );

      const root = ReactDOMClient.hydrateRoot(document, <App />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            {/* the actual resources */}
            <meta charset="utf-8" />
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <script async="" src="script" />
            <title>foo</title>
            <link rel="foobar" href="foobar" />
            <meta property="og:image" content="foo" />
            {/* the noscript children are encoded as a textNode when scripting is enabled */}
            <noscript>
              &lt;link rel="stylesheet"
              href="foo"/&gt;&lt;title&gt;foo&lt;/title&gt;&lt;link rel="foobar"
              href="foobar"/&gt;&lt;meta charSet="utf-8"/&gt;&lt;meta
              property="og:image" content="foo"/&gt;&lt;script async=""
              src="script"&gt;&lt;/script&gt;&lt;link rel="stylesheet"
              href="foo"/&gt;&lt;title&gt;foo&lt;/title&gt;&lt;link rel="foobar"
              href="foobar"/&gt;&lt;meta charSet="utf-8"/&gt;&lt;meta
              property="og:image" content="foo"/&gt;&lt;script async=""
              src="script"&gt;&lt;/script&gt;
            </noscript>
          </head>
          <body />
        </html>,
      );

      root.render(null);
      expect(Scheduler).toFlushWithoutYielding();
      // stylesheets and scripts currently don't unmount ever
      // noscript is never hydrated so it also does not get cleared
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head>
            <link rel="stylesheet" href="foo" data-precedence="foo" />
            <script async="" src="script" />
          </head>
          <body />
        </html>,
      );
    });

    it('noscript runs on the server but does not emit resources and does not run on the client', async () => {
      function App() {
        return (
          <html>
            <body>
              <div>
                foo
                <noscript>
                  <Foo />
                </noscript>
              </div>
            </body>
          </html>
        );
      }
      function Foo() {
        Scheduler.unstable_yieldValue('Foo');

        return <title>noscript title</title>;
      }

      await actIntoEmptyDocument(() => {
        const {pipe} = renderToPipeableStream(<App />);
        pipe(writable);
      });
      expect(getMeaningfulChildren(container)).toEqual(
        <html>
          <head />
          <body>
            <div>
              foo<noscript>&lt;title&gt;noscript title&lt;/title&gt;</noscript>
            </div>
          </body>
        </html>,
      );
      expect(Scheduler).toHaveYielded(['Foo']);

      ReactDOMClient.hydrateRoot(document, <App />);
      expect(Scheduler).toFlushWithoutYielding();
      expect(getMeaningfulChildren(document)).toEqual(
        <html>
          <head />
          <body>
            <div>
              foo<noscript>&lt;title&gt;noscript title&lt;/title&gt;</noscript>
            </div>
          </body>
        </html>,
      );
    });
  });
});