Skip to content

Commit

Permalink
Adds a test demonstrating preload credential transfer across prerende…
Browse files Browse the repository at this point in the history
…r/resume
  • Loading branch information
gnoff committed Sep 25, 2023
1 parent 5a1f45d commit ccbb26c
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 2 deletions.
275 changes: 275 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMFizzStaticFloat-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
/**
* 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 {
getVisibleChildren,
insertNodesAndExecuteScripts,
} from '../test-utils/FizzTestUtils';

// Polyfills for test environment
global.ReadableStream =
require('web-streams-polyfill/ponyfill/es6').ReadableStream;
global.TextEncoder = require('util').TextEncoder;

let React;
let ReactDOM;
let ReactDOMFizzServer;
let ReactDOMFizzStatic;
let Suspense;
let container;

describe('ReactDOMFizzStaticFloat', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMFizzServer = require('react-dom/server.browser');
if (__EXPERIMENTAL__) {
ReactDOMFizzStatic = require('react-dom/static.browser');
}
Suspense = React.Suspense;
container = document.createElement('div');
document.body.appendChild(container);
});

afterEach(() => {
document.body.removeChild(container);
});

async function readIntoContainer(stream) {
const reader = stream.getReader();
let result = '';
while (true) {
const {done, value} = await reader.read();
if (done) {
break;
}
result += Buffer.from(value).toString('utf8');
}
const temp = document.createElement('div');
temp.innerHTML = result;
await insertNodesAndExecuteScripts(temp, container, null);
}

// @gate experimental
it('should transfer connection credentials across prerender and resume for stylesheets, scripts, and moduleScripts', async () => {
let prerendering = true;
function Postpone() {
if (prerendering) {
React.unstable_postpone();
}
return (
<>
<link rel="stylesheet" href="style creds" precedence="default" />
<script async={true} src="script creds" data-meaningful="" />
<script
type="module"
async={true}
src="module creds"
data-meaningful=""
/>
<link rel="stylesheet" href="style anon" precedence="default" />
<script async={true} src="script anon" data-meaningful="" />
<script
type="module"
async={true}
src="module default"
data-meaningful=""
/>
</>
);
}

function App() {
ReactDOM.preload('style creds', {
as: 'style',
crossOrigin: 'use-credentials',
});
ReactDOM.preload('script creds', {
as: 'script',
crossOrigin: 'use-credentials',
integrity: 'script-hash',
});
ReactDOM.preloadModule('module creds', {
crossOrigin: 'use-credentials',
integrity: 'module-hash',
});
ReactDOM.preload('style anon', {
as: 'style',
crossOrigin: 'anonymous',
});
ReactDOM.preload('script anon', {
as: 'script',
crossOrigin: 'foobar',
});
ReactDOM.preloadModule('module default', {
integrity: 'module-hash',
});
return (
<div>
<Suspense fallback="Loading...">
<Postpone />
</Suspense>
</div>
);
}

jest.mock('script creds', () => {}, {
virtual: true,
});
jest.mock('module creds', () => {}, {
virtual: true,
});
jest.mock('script anon', () => {}, {
virtual: true,
});
jest.mock('module default', () => {}, {
virtual: true,
});

const prerendered = await ReactDOMFizzStatic.prerender(<App />);
expect(prerendered.postponed).not.toBe(null);

await readIntoContainer(prerendered.prelude);

expect(getVisibleChildren(container)).toEqual([
<link
rel="preload"
as="style"
href="style creds"
crossorigin="use-credentials"
/>,
<link
rel="preload"
as="script"
href="script creds"
crossorigin="use-credentials"
integrity="script-hash"
/>,
<link
rel="modulepreload"
href="module creds"
crossorigin="use-credentials"
integrity="module-hash"
/>,
<link rel="preload" as="style" href="style anon" crossorigin="" />,
<link rel="preload" as="script" href="script anon" crossorigin="" />,
<link
rel="modulepreload"
href="module default"
integrity="module-hash"
/>,
<div>Loading...</div>,
]);

prerendering = false;
const content = await ReactDOMFizzServer.resume(
<App />,
JSON.parse(JSON.stringify(prerendered.postponed)),
);

await readIntoContainer(content);

// Dispatch load event to injected stylesheet
const linkCreds = document.querySelector(
'link[rel="stylesheet"][href="style creds"]',
);
const linkAnon = document.querySelector(
'link[rel="stylesheet"][href="style anon"]',
);
const event = document.createEvent('Events');
event.initEvent('load', true, true);
linkCreds.dispatchEvent(event);
linkAnon.dispatchEvent(event);

// Wait for the instruction microtasks to flush.
await 0;
await 0;

expect(getVisibleChildren(document)).toEqual(
<html>
<head>
<link
rel="stylesheet"
data-precedence="default"
href="style creds"
crossorigin="use-credentials"
/>
<link
rel="stylesheet"
data-precedence="default"
href="style anon"
crossorigin=""
/>
</head>
<body>
<div>
<link
rel="preload"
as="style"
href="style creds"
crossorigin="use-credentials"
/>
<link
rel="preload"
as="script"
href="script creds"
crossorigin="use-credentials"
integrity="script-hash"
/>
<link
rel="modulepreload"
href="module creds"
crossorigin="use-credentials"
integrity="module-hash"
/>
<link rel="preload" as="style" href="style anon" crossorigin="" />
<link rel="preload" as="script" href="script anon" crossorigin="" />
<link
rel="modulepreload"
href="module default"
integrity="module-hash"
/>
<div />
<script
async=""
src="script creds"
crossorigin="use-credentials"
integrity="script-hash"
data-meaningful=""
/>
<script
type="module"
async=""
src="module creds"
crossorigin="use-credentials"
integrity="module-hash"
data-meaningful=""
/>
<script
async=""
src="script anon"
crossorigin=""
data-meaningful=""
/>
<script
type="module"
async=""
src="module default"
integrity="module-hash"
data-meaningful=""
/>
</div>
</body>
</html>,
);
});
});
5 changes: 3 additions & 2 deletions packages/react-dom/src/test-utils/FizzTestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ async function executeScript(script: Element) {
'You must set the current document to the global document to use script src in tests',
);
}

try {
// $FlowFixMe
require(scriptSrc);
Expand Down Expand Up @@ -177,8 +178,8 @@ function getVisibleChildren(element: Element): React$Node {
while (node) {
if (node.nodeType === 1) {
if (
node.tagName !== 'SCRIPT' &&
node.tagName !== 'script' &&
((node.tagName !== 'SCRIPT' && node.tagName !== 'script') ||
node.hasAttribute('data-meaningful')) &&
node.tagName !== 'TEMPLATE' &&
node.tagName !== 'template' &&
!node.hasAttribute('hidden') &&
Expand Down

0 comments on commit ccbb26c

Please sign in to comment.