14
global.TextEncoder = require('util').TextEncoder;
15
16
let React;
17
+let ReactDOMClient;
18
let ReactDOMFizzServer;
19
let Suspense;
20
+let Scheduler;
21
+let JSDOM;
22
+let document;
23
+let container;
24
25
describe('ReactDOMFizzServer', () => {
26
beforeEach(() => {
27
jest.resetModules();
28
React = require('react');
29
+ ReactDOMClient = require('react-dom/client');
30
+ Scheduler = require('scheduler');
31
if (__EXPERIMENTAL__) {
32
ReactDOMFizzServer = require('react-dom/server.browser');
33
}
55
}
56
}
57
51
- // @gate experimental
52
- it('should call renderToReadableStream', async () => {
53
- const stream = await ReactDOMFizzServer.renderToReadableStream(
54
- <div>hello world</div>,
55
- );
56
- const result = await readResult(stream);
57
- expect(result).toMatchInlineSnapshot(`"<div>hello world</div>"`);
58
- });
58
+ describe('renderToReadableStream', () => {
59
+ // @gate experimental
60
+ it('should call renderToReadableStream', async () => {
61
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
62
+ <div>hello world</div>,
63
+ );
64
+ const result = await readResult(stream);
65
+ expect(result).toMatchInlineSnapshot(`"<div>hello world</div>"`);
66
+ });
67
+
68
+ // @gate experimental
69
+ it('should emit DOCTYPE at the root of the document', async () => {
70
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
71
+ <html>
72
+ <body>hello world</body>
73
+ </html>,
74
+ );
75
+ const result = await readResult(stream);
76
+ expect(result).toMatchInlineSnapshot(
77
+ `"<!DOCTYPE html><html><body>hello world</body></html>"`,
78
+ );
79
+ });
80
60
- // @gate experimental
61
- it('should emit DOCTYPE at the root of the document', async () => {
62
- const stream = await ReactDOMFizzServer.renderToReadableStream(
63
- <html>
64
- <body>hello world</body>
65
- </html>,
66
- );
67
- const result = await readResult(stream);
68
- expect(result).toMatchInlineSnapshot(
69
- `"<!DOCTYPE html><html><body>hello world</body></html>"`,
70
- );
71
- });
81
+ // @gate experimental
82
+ it('should emit bootstrap script src at the end', async () => {
83
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
84
+ <div>hello world</div>,
85
+ {
86
+ bootstrapScriptContent: 'INIT();',
87
+ bootstrapScripts: ['init.js'],
88
+ bootstrapModules: ['init.mjs'],
89
+ },
90
+ );
91
+ const result = await readResult(stream);
92
+ expect(result).toMatchInlineSnapshot(
93
+ `"<div>hello world</div><script>INIT();</script><script src=\\"init.js\\" async=\\"\\"></script><script type=\\"module\\" src=\\"init.mjs\\" async=\\"\\"></script>"`,
94
+ );
95
+ });
96
+
97
+ // @gate experimental
98
+ it('emits all HTML as one unit if we wait until the end to start', async () => {
99
+ let hasLoaded = false;
100
+ let resolve;
101
+ const promise = new Promise(r => (resolve = r));
102
+ function Wait() {
103
+ if (!hasLoaded) {
104
+ throw promise;
105
+ }
106
+ return 'Done';
107
+ }
108
+ let isComplete = false;
109
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
110
+ <div>
111
+ <Suspense fallback="Loading">
112
+ <Wait />
113
+ </Suspense>
114
+ </div>,
115
+ );
116
73
- // @gate experimental
74
- it('should emit bootstrap script src at the end', async () => {
75
- const stream = await ReactDOMFizzServer.renderToReadableStream(
76
- <div>hello world</div>,
77
- {
78
- bootstrapScriptContent: 'INIT();',
79
- bootstrapScripts: ['init.js'],
80
- bootstrapModules: ['init.mjs'],
81
- },
82
- );
83
- const result = await readResult(stream);
84
- expect(result).toMatchInlineSnapshot(
85
- `"<div>hello world</div><script>INIT();</script><script src=\\"init.js\\" async=\\"\\"></script><script type=\\"module\\" src=\\"init.mjs\\" async=\\"\\"></script>"`,
86
- );
87
- });
117
+ stream.allReady.then(() => (isComplete = true));
118
89
- // @gate experimental
90
- it('emits all HTML as one unit if we wait until the end to start', async () => {
91
- let hasLoaded = false;
92
- let resolve;
93
- const promise = new Promise(r => (resolve = r));
94
- function Wait() {
95
- if (!hasLoaded) {
96
- throw promise;
97
- }
98
- return 'Done';
99
- }
100
- let isComplete = false;
101
- const stream = await ReactDOMFizzServer.renderToReadableStream(
102
- <div>
103
- <Suspense fallback="Loading">
104
- <Wait />
105
- </Suspense>
106
- </div>,
107
- );
108
-
109
- stream.allReady.then(() => (isComplete = true));
110
-
111
- await jest.runAllTimers();
112
- expect(isComplete).toBe(false);
113
- // Resolve the loading.
114
- hasLoaded = true;
115
- await resolve();
116
-
117
- await jest.runAllTimers();
118
-
119
- expect(isComplete).toBe(true);
120
-
121
- const result = await readResult(stream);
122
- expect(result).toMatchInlineSnapshot(
123
- `"<div><!--$-->Done<!-- --><!--/$--></div>"`,
124
- );
125
- });
119
+ await jest.runAllTimers();
120
+ expect(isComplete).toBe(false);
121
+ // Resolve the loading.
122
+ hasLoaded = true;
123
+ await resolve();
124
127
- // @gate experimental
128
- it('should reject the promise when an error is thrown at the root', async () => {
129
- const reportedErrors = [];
130
- let caughtError = null;
131
- try {
132
- await ReactDOMFizzServer.renderToReadableStream(
125
+ await jest.runAllTimers();
126
+
127
+ expect(isComplete).toBe(true);
128
+
129
+ const result = await readResult(stream);
130
+ expect(result).toMatchInlineSnapshot(
131
+ `"<div><!--$-->Done<!-- --><!--/$--></div>"`,
132
+ );
133
+ });
134
+
135
+ // @gate experimental
136
+ it('should reject the promise when an error is thrown at the root', async () => {
137
+ const reportedErrors = [];
138
+ let caughtError = null;
139
+ try {
140
+ await ReactDOMFizzServer.renderToReadableStream(
141
+ <div>
142
+ <Throw />
143
+ </div>,
144
+ {
145
+ onError(x) {
146
+ reportedErrors.push(x);
147
+ },
148
+ },
149
+ );
150
+ } catch (error) {
151
+ caughtError = error;
152
+ }
153
+ expect(caughtError).toBe(theError);
154
+ expect(reportedErrors).toEqual([theError]);
155
+ });
156
+
157
+ // @gate experimental
158
+ it('should reject the promise when an error is thrown inside a fallback', async () => {
159
+ const reportedErrors = [];
160
+ let caughtError = null;
161
+ try {
162
+ await ReactDOMFizzServer.renderToReadableStream(
163
+ <div>
164
+ <Suspense fallback={<Throw />}>
165
+ <InfiniteSuspend />
166
+ </Suspense>
167
+ </div>,
168
+ {
169
+ onError(x) {
170
+ reportedErrors.push(x);
171
+ },
172
+ },
173
+ );
174
+ } catch (error) {
175
+ caughtError = error;
176
+ }
177
+ expect(caughtError).toBe(theError);
178
+ expect(reportedErrors).toEqual([theError]);
179
+ });
180
+
181
+ // @gate experimental
182
+ it('should not error the stream when an error is thrown inside suspense boundary', async () => {
183
+ const reportedErrors = [];
184
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
185
<div>
134
- <Throw />
186
+ <Suspense fallback={<div>Loading</div>}>
187
+ <Throw />
188
+ </Suspense>
189
</div>,
190
{
191
onError(x) {
193
},
194
},
195
);
142
- } catch (error) {
143
- caughtError = error;
144
- }
145
- expect(caughtError).toBe(theError);
146
- expect(reportedErrors).toEqual([theError]);
147
- });
196
149
- // @gate experimental
150
- it('should reject the promise when an error is thrown inside a fallback', async () => {
151
- const reportedErrors = [];
152
- let caughtError = null;
153
- try {
154
- await ReactDOMFizzServer.renderToReadableStream(
197
+ const result = await readResult(stream);
198
+ expect(result).toContain('Loading');
199
+ expect(reportedErrors).toEqual([theError]);
200
+ });
201
+
202
+ // @gate experimental
203
+ it('should be able to complete by aborting even if the promise never resolves', async () => {
204
+ const errors = [];
205
+ const controller = new AbortController();
206
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
207
<div>
156
- <Suspense fallback={<Throw />}>
208
+ <Suspense fallback={<div>Loading</div>}>
209
<InfiniteSuspend />
210
</Suspense>
211
</div>,
212
{
213
+ signal: controller.signal,
214
onError(x) {
162
- reportedErrors.push(x);
215
+ errors.push(x.message);
216
},
217
},
218
);
166
- } catch (error) {
167
- caughtError = error;
168
- }
169
- expect(caughtError).toBe(theError);
170
- expect(reportedErrors).toEqual([theError]);
171
- });
219
173
- // @gate experimental
174
- it('should not error the stream when an error is thrown inside suspense boundary', async () => {
175
- const reportedErrors = [];
176
- const stream = await ReactDOMFizzServer.renderToReadableStream(
177
- <div>
178
- <Suspense fallback={<div>Loading</div>}>
179
- <Throw />
180
- </Suspense>
181
- </div>,
182
- {
183
- onError(x) {
184
- reportedErrors.push(x);
220
+ controller.abort();
221
+
222
+ const result = await readResult(stream);
223
+ expect(result).toContain('Loading');
224
+
225
+ expect(errors).toEqual([
226
+ 'The render was aborted by the server without a reason.',
227
+ ]);
228
+ });
229
+
230
+ // @gate experimental
231
+ it('should not continue rendering after the reader cancels', async () => {
232
+ let hasLoaded = false;
233
+ let resolve;
234
+ let isComplete = false;
235
+ let rendered = false;
236
+ const promise = new Promise(r => (resolve = r));
237
+ function Wait() {
238
+ if (!hasLoaded) {
239
+ throw promise;
240
+ }
241
+ rendered = true;
242
+ return 'Done';
243
+ }
244
+ const errors = [];
245
+ const stream = await ReactDOMFizzServer.renderToReadableStream(
246
+ <div>
247
+ <Suspense fallback={<div>Loading</div>}>
248
+ <Wait /> />
249
+ </Suspense>
250
+ </div>,
251
+ {
252
+ onError(x) {
253
+ errors.push(x.message);
254
+ },
255
},
186
- },
187
- );
256
+ );
257
189
- const result = await readResult(stream);
190
- expect(result).toContain('Loading');
191
- expect(reportedErrors).toEqual([theError]);
192
- });
258
+ stream.allReady.then(() => (isComplete = true));
259
+
260
+ expect(rendered).toBe(false);
261
+ expect(isComplete).toBe(false);
262
+
263
+ const reader = stream.getReader();
264
+ reader.cancel();
265
+
266
+ expect(errors).toEqual([
267
+ 'The render was aborted by the server without a reason.',
268
+ ]);
269
+
270
+ hasLoaded = true;
271
+ resolve();
272
+
273
+ await jest.runAllTimers();
274
+
275
+ expect(rendered).toBe(false);
276
+ expect(isComplete).toBe(true);
277
+ });
278
+
279
+ // @gate experimental
280
+ it('should stream large contents that might overlow individual buffers', async () => {
281
+ const str492 = `(492) This string is intentionally 492 bytes long because we want to make sure we process chunks that will overflow buffer boundaries. It will repeat to fill out the bytes required (inclusive of this prompt):: foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux q :: total count (492)`;
282
+ const str2049 = `(2049) This string is intentionally 2049 bytes long because we want to make sure we process chunks that will overflow buffer boundaries. It will repeat to fill out the bytes required (inclusive of this prompt):: foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy :: total count (2049)`;
283
+
284
+ // this specific layout is somewhat contrived to exercise the landing on
285
+ // an exact view boundary. it's not critical to test this edge case but
286
+ // since we are setting up a test in general for larger chunks I contrived it
287
+ // as such for now. I don't think it needs to be maintained if in the future
288
+ // the view sizes change or become dynamic becasue of the use of byobRequest
289
+ let stream;
290
+ stream = await ReactDOMFizzServer.renderToReadableStream(
291
+ <>
292
+ <div>
293
+ <span>{''}</span>
294
+ </div>
295
+ <div>{str492}</div>
296
+ <div>{str492}</div>
297
+ </>,
298
+ );
299
194
- // @gate experimental
195
- it('should be able to complete by aborting even if the promise never resolves', async () => {
196
- const errors = [];
197
- const controller = new AbortController();
198
- const stream = await ReactDOMFizzServer.renderToReadableStream(
199
- <div>
200
- <Suspense fallback={<div>Loading</div>}>
201
- <InfiniteSuspend />
202
- </Suspense>
203
- </div>,
204
- {
300
+ let result;
301
+ result = await readResult(stream);
302
+ expect(result).toMatchInlineSnapshot(
303
+ `"<div><span></span></div><div>${str492}</div><div>${str492}</div>"`,
304
+ );
305
+
306
+ // this size 2049 was chosen to be a couple base 2 orders larger than the current view
307
+ // size. if the size changes in the future hopefully this will still exercise
308
+ // a chunk that is too large for the view size.
309
+ stream = await ReactDOMFizzServer.renderToReadableStream(
310
+ <>
311
+ <div>{str2049}</div>
312
+ </>,
313
+ );
314
+
315
+ result = await readResult(stream);
316
+ expect(result).toMatchInlineSnapshot(`"<div>${str2049}</div>"`);
317
+ });
318
+
319
+ // @gate experimental
320
+ it('Supports custom abort reasons with a string', async () => {
321
+ const promise = new Promise(r => {});
322
+ function Wait() {
323
+ throw promise;
324
+ }
325
+ function App() {
326
+ return (
327
+ <div>
328
+ <p>
329
+ <Suspense fallback={'p'}>
330
+ <Wait />
331
+ </Suspense>
332
+ </p>
333
+ <span>
334
+ <Suspense fallback={'span'}>
335
+ <Wait />
336
+ </Suspense>
337
+ </span>
338
+ </div>
339
+ );
340
+ }
341
+
342
+ const errors = [];
343
+ const controller = new AbortController();
344
+ await ReactDOMFizzServer.renderToReadableStream(<App />, {
345
signal: controller.signal,
346
onError(x) {
207
- errors.push(x.message);
347
+ errors.push(x);
348
+ return 'a digest';
349
},
209
- },
210
- );
350
+ });
351
212
- controller.abort();
352
+ // @TODO this is a hack to work around lack of support for abortSignal.reason in node
353
+ // The abort call itself should set this property but since we are testing in node we
354
+ // set it here manually
355
+ controller.signal.reason = 'foobar';
356
+ controller.abort('foobar');
357
214
- const result = await readResult(stream);
215
- expect(result).toContain('Loading');
216
-
217
- expect(errors).toEqual([
218
- 'This Suspense boundary was aborted by the server.',
219
- ]);
220
- });
358
+ expect(errors).toEqual(['foobar', 'foobar']);
359
+ });
360
222
- // @gate experimental
223
- it('should not continue rendering after the reader cancels', async () => {
224
- let hasLoaded = false;
225
- let resolve;
226
- let isComplete = false;
227
- let rendered = false;
228
- const promise = new Promise(r => (resolve = r));
229
- function Wait() {
230
- if (!hasLoaded) {
361
+ // @gate experimental
362
+ it('Supports custom abort reasons with an Error', async () => {
363
+ const promise = new Promise(r => {});
364
+ function Wait() {
365
throw promise;
366
}
233
- rendered = true;
234
- return 'Done';
235
- }
236
- const errors = [];
237
- const stream = await ReactDOMFizzServer.renderToReadableStream(
238
- <div>
239
- <Suspense fallback={<div>Loading</div>}>
240
- <Wait /> />
241
- </Suspense>
242
- </div>,
243
- {
367
+ function App() {
368
+ return (
369
+ <div>
370
+ <p>
371
+ <Suspense fallback={'p'}>
372
+ <Wait />
373
+ </Suspense>
374
+ </p>
375
+ <span>
376
+ <Suspense fallback={'span'}>
377
+ <Wait />
378
+ </Suspense>
379
+ </span>
380
+ </div>
381
+ );
382
+ }
383
+
384
+ const errors = [];
385
+ const controller = new AbortController();
386
+ await ReactDOMFizzServer.renderToReadableStream(<App />, {
387
+ signal: controller.signal,
388
onError(x) {
389
errors.push(x.message);
390
+ return 'a digest';
391
},
247
- },
248
- );
249
-
250
- stream.allReady.then(() => (isComplete = true));
392
+ });
393
252
- expect(rendered).toBe(false);
253
- expect(isComplete).toBe(false);
394
+ // @TODO this is a hack to work around lack of support for abortSignal.reason in node
395
+ // The abort call itself should set this property but since we are testing in node we
396
+ // set it here manually
397
+ controller.signal.reason = new Error('uh oh');
398
+ controller.abort(new Error('uh oh'));
399
255
- const reader = stream.getReader();
256
- reader.cancel();
257
-
258
- expect(errors).toEqual([
259
- 'This Suspense boundary was aborted by the server.',
260
- ]);
261
-
262
- hasLoaded = true;
263
- resolve();
400
+ expect(errors).toEqual(['uh oh', 'uh oh']);
401
+ });
402
+ });
403
265
- await jest.runAllTimers();
404
+ describe('renderToString', () => {
405
+ beforeEach(() => {
406
+ JSDOM = require('jsdom').JSDOM;
407
267
- expect(rendered).toBe(false);
268
- expect(isComplete).toBe(true);
269
- });
408
+ // Test Environment
409
+ const jsdom = new JSDOM(
410
+ '<!DOCTYPE html><html><head></head><body><div id="container">',
411
+ {
412
+ runScripts: 'dangerously',
413
+ },
414
+ );
415
+ document = jsdom.window.document;
416
+ container = document.getElementById('container');
417
+ });
418
+
419
+ // @gate experimental
420
+ it('refers users to apis that support Suspense when somethign suspends', () => {
421
+ function App({isClient}) {
422
+ return (
423
+ <div>
424
+ <Suspense fallback={'fallback'}>
425
+ {isClient ? 'resolved' : <InfiniteSuspend />}
426
+ </Suspense>
427
+ </div>
428
+ );
429
+ }
430
+ container.innerHTML = ReactDOMFizzServer.renderToString(
431
+ <App isClient={false} />,
432
+ );
433
271
- // @gate experimental
272
- it('should stream large contents that might overlow individual buffers', async () => {
273
- const str492 = `(492) This string is intentionally 492 bytes long because we want to make sure we process chunks that will overflow buffer boundaries. It will repeat to fill out the bytes required (inclusive of this prompt):: foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux q :: total count (492)`;
274
- const str2049 = `(2049) This string is intentionally 2049 bytes long because we want to make sure we process chunks that will overflow buffer boundaries. It will repeat to fill out the bytes required (inclusive of this prompt):: foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy thud foo bar qux quux corge grault garply waldo fred plugh xyzzy :: total count (2049)`;
275
-
276
- // this specific layout is somewhat contrived to exercise the landing on
277
- // an exact view boundary. it's not critical to test this edge case but
278
- // since we are setting up a test in general for larger chunks I contrived it
279
- // as such for now. I don't think it needs to be maintained if in the future
280
- // the view sizes change or become dynamic becasue of the use of byobRequest
281
- let stream;
282
- stream = await ReactDOMFizzServer.renderToReadableStream(
283
- <>
284
- <div>
285
- <span>{''}</span>
286
- </div>
287
- <div>{str492}</div>
288
- <div>{str492}</div>
289
- </>,
290
- );
291
-
292
- let result;
293
- result = await readResult(stream);
294
- expect(result).toMatchInlineSnapshot(
295
- `"<div><span></span></div><div>${str492}</div><div>${str492}</div>"`,
296
- );
297
-
298
- // this size 2049 was chosen to be a couple base 2 orders larger than the current view
299
- // size. if the size changes in the future hopefully this will still exercise
300
- // a chunk that is too large for the view size.
301
- stream = await ReactDOMFizzServer.renderToReadableStream(
302
- <>
303
- <div>{str2049}</div>
304
- </>,
305
- );
306
-
307
- result = await readResult(stream);
308
- expect(result).toMatchInlineSnapshot(`"<div>${str2049}</div>"`);
434
+ const errors = [];
435
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
436
+ onRecoverableError(error, errorInfo) {
437
+ errors.push(error.message);
438
+ },
439
+ });
440
+
441
+ expect(Scheduler).toFlushAndYield([]);
442
+ expect(errors.length).toBe(1);
443
+ if (__DEV__) {
444
+ expect(errors[0]).toBe(
445
+ 'The server did not finish this Suspense boundary: The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server',
446
+ );
447
+ } else {
448
+ expect(errors[0]).toBe(
449
+ 'The server could not finish this Suspense boundary, likely due to ' +
450
+ 'an error during server rendering. Switched to client rendering.',
451
+ );
452
+ }
453
+ });
454
});
455
});