7178
);
7179
});
7180
7181
+ // @gate enablePostpone
7182
+ it('can client render a boundary after having already postponed', async () => {
7183
+ let prerendering = true;
7184
+ let ssr = true;
7185
+
7186
+ function Postpone() {
7187
+ if (prerendering) {
7188
+ React.unstable_postpone();
7189
+ }
7190
+ return 'Hello';
7191
+ }
7192
+
7193
+ function ServerError() {
7194
+ if (ssr) {
7195
+ throw new Error('server error');
7196
+ }
7197
+ return 'World';
7198
+ }
7199
+
7200
+ function App() {
7201
+ return (
7202
+ <div>
7203
+ <Suspense fallback="Loading1">
7204
+ <Postpone />
7205
+ <ServerError />
7206
+ </Suspense>
7207
+ <Suspense fallback="Loading2">
7208
+ <Postpone />
7209
+ </Suspense>
7210
+ </div>
7211
+ );
7212
+ }
7213
+
7214
+ const prerenderErrors = [];
7215
+ const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
7216
+ <App />,
7217
+ {
7218
+ onError(x) {
7219
+ prerenderErrors.push(x.message);
7220
+ },
7221
+ },
7222
+ );
7223
+ expect(prerendered.postponed).not.toBe(null);
7224
+
7225
+ prerendering = false;
7226
+
7227
+ const ssrErrors = [];
7228
+
7229
+ const resumed = ReactDOMFizzServer.resumeToPipeableStream(
7230
+ <App />,
7231
+ JSON.parse(JSON.stringify(prerendered.postponed)),
7232
+ {
7233
+ onError(x) {
7234
+ ssrErrors.push(x.message);
7235
+ },
7236
+ },
7237
+ );
7238
+
7239
+ const windowErrors = [];
7240
+ function globalError(e) {
7241
+ windowErrors.push(e.message);
7242
+ }
7243
+ window.addEventListener('error', globalError);
7244
+
7245
+ // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7246
+ const preludeWritable = new Stream.PassThrough();
7247
+ preludeWritable.setEncoding('utf8');
7248
+ preludeWritable.on('data', chunk => {
7249
+ writable.write(chunk);
7250
+ });
7251
+
7252
+ await act(() => {
7253
+ prerendered.prelude.pipe(preludeWritable);
7254
+ });
7255
+
7256
+ expect(windowErrors).toEqual([]);
7257
+
7258
+ expect(getVisibleChildren(container)).toEqual(
7259
+ <div>
7260
+ {'Loading1'}
7261
+ {'Loading2'}
7262
+ </div>,
7263
+ );
7264
+
7265
+ await act(() => {
7266
+ resumed.pipe(writable);
7267
+ });
7268
+
7269
+ expect(prerenderErrors).toEqual(['server error']);
7270
+
7271
+ // Since this errored, we shouldn't have to replay it.
7272
+ expect(ssrErrors).toEqual([]);
7273
+
7274
+ expect(windowErrors).toEqual([]);
7275
+
7276
+ // Still loading...
7277
+ expect(getVisibleChildren(container)).toEqual(
7278
+ <div>
7279
+ {'Loading1'}
7280
+ {'Hello'}
7281
+ </div>,
7282
+ );
7283
+
7284
+ const recoverableErrors = [];
7285
+
7286
+ ssr = false;
7287
+
7288
+ await clientAct(() => {
7289
+ ReactDOMClient.hydrateRoot(container, <App />, {
7290
+ onRecoverableError(x) {
7291
+ recoverableErrors.push(x.message);
7292
+ },
7293
+ });
7294
+ });
7295
+
7296
+ expect(recoverableErrors).toEqual(
7297
+ __DEV__
7298
+ ? ['server error']
7299
+ : [
7300
+ 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7301
+ ],
7302
+ );
7303
+ expect(getVisibleChildren(container)).toEqual(
7304
+ <div>
7305
+ {'Hello'}
7306
+ {'World'}
7307
+ {'Hello'}
7308
+ </div>,
7309
+ );
7310
+
7311
+ expect(windowErrors).toEqual([]);
7312
+
7313
+ window.removeEventListener('error', globalError);
7314
+ });
7315
+
7316
// @gate enablePostpone
7317
it('can postpone in fallback', async () => {
7318
let prerendering = true;