Skip to content

Commit 9617d39

Browse files
authored
[Fizz] Add proper assertion for stream fix (#27543)
In #27541 I added tests to assert that the write after close bug was fixed. However the Node implementation has an abort behavior preventing reproduction of the original issue and the Browser build does not use AsyncLocalStorage which also prevents reproduction. This change moves the Browser test to a Edge runtime where AsyncLocalStorage is polyfilled. In this implementation the test does correctly fail when the patch is removed.
1 parent 601e5c3 commit 9617d39

File tree

2 files changed

+78
-35
lines changed

2 files changed

+78
-35
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ global.ReadableStream =
1515
global.TextEncoder = require('util').TextEncoder;
1616

1717
let React;
18-
let ReactDOM;
1918
let ReactDOMFizzServer;
2019
let Suspense;
2120

2221
describe('ReactDOMFizzServerBrowser', () => {
2322
beforeEach(() => {
2423
jest.resetModules();
2524
React = require('react');
26-
ReactDOM = require('react-dom');
2725
ReactDOMFizzServer = require('react-dom/server.browser');
2826
Suspense = React.Suspense;
2927
});
@@ -549,37 +547,4 @@ describe('ReactDOMFizzServerBrowser', () => {
549547
// However, it does error the shell.
550548
expect(caughtError.message).toEqual('testing postpone');
551549
});
552-
553-
// https://github.com/facebook/react/issues/27540
554-
// This test is not actually asserting much because in our test environment the Float method cannot find the request after
555-
// an await and thus is a noop. If we fix our test environment to support AsyncLocalStorage we can assert that the
556-
// stream does not write after closing.
557-
it('does not try to write to the stream after it has been closed', async () => {
558-
async function preloadLate() {
559-
await 1;
560-
ReactDOM.preconnect('foo');
561-
}
562-
563-
function Preload() {
564-
preloadLate();
565-
return null;
566-
}
567-
568-
function App() {
569-
return (
570-
<html>
571-
<body>
572-
<main>hello</main>
573-
<Preload />
574-
</body>
575-
</html>
576-
);
577-
}
578-
const stream = await ReactDOMFizzServer.renderToReadableStream(<App />);
579-
const result = await readResult(stream);
580-
581-
expect(result).toMatchInlineSnapshot(
582-
`"<!DOCTYPE html><html><head></head><body><main>hello</main></body></html>"`,
583-
);
584-
});
585550
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails react-core
8+
*/
9+
10+
'use strict';
11+
12+
// Polyfills for test environment
13+
global.ReadableStream =
14+
require('web-streams-polyfill/ponyfill/es6').ReadableStream;
15+
global.TextEncoder = require('util').TextEncoder;
16+
global.AsyncLocalStorage = require('async_hooks').AsyncLocalStorage;
17+
18+
let React;
19+
let ReactDOM;
20+
let ReactDOMFizzServer;
21+
22+
describe('ReactDOMFizzServerEdge', () => {
23+
beforeEach(() => {
24+
jest.resetModules();
25+
jest.useRealTimers();
26+
React = require('react');
27+
ReactDOM = require('react-dom');
28+
ReactDOMFizzServer = require('react-dom/server.edge');
29+
});
30+
31+
async function readResult(stream) {
32+
const reader = stream.getReader();
33+
let result = '';
34+
while (true) {
35+
const {done, value} = await reader.read();
36+
if (done) {
37+
return result;
38+
}
39+
result += Buffer.from(value).toString('utf8');
40+
}
41+
}
42+
43+
// https://github.com/facebook/react/issues/27540
44+
it('does not try to write to the stream after it has been closed', async () => {
45+
async function preloadLate() {
46+
await 1;
47+
await 1;
48+
// need to wait a few microtasks to get the stream to close before this is called
49+
ReactDOM.preconnect('foo');
50+
}
51+
52+
function Preload() {
53+
preloadLate();
54+
return null;
55+
}
56+
57+
function App() {
58+
return (
59+
<html>
60+
<body>
61+
<main>hello</main>
62+
<Preload />
63+
</body>
64+
</html>
65+
);
66+
}
67+
const stream = await ReactDOMFizzServer.renderToReadableStream(<App />);
68+
const result = await readResult(stream);
69+
// need to wait a macrotask to let the scheduled work from the preconnect to execute
70+
await new Promise(resolve => {
71+
setTimeout(resolve, 1);
72+
});
73+
74+
expect(result).toMatchInlineSnapshot(
75+
`"<!DOCTYPE html><html><head></head><body><main>hello</main></body></html>"`,
76+
);
77+
});
78+
});

0 commit comments

Comments
 (0)