@samitouri / QOS-React / commits / 25a8b9735c

Codemod tests to waitFor pattern (1/?) (#26288)

This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context.

Andrew Clark committed Mar 2, 2023 at 22:34 UTC 25a8b9735ce7c84210707d5eced7fe2c9abbd0e1
15 files changed +698 -905
packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js
+5 -4
@@ -12,7 +12,7 @@
12 describe('DebugTracing', () => {
13 let React;
14 let ReactTestRenderer;
15 - let Scheduler;
15 + let waitForPaint;
16
17 let logs;
18
@@ -27,7 +27,8 @@ describe('DebugTracing', () => {
27
28 React = require('react');
29 ReactTestRenderer = require('react-test-renderer');
30 - Scheduler = require('scheduler');
30 + const InternalTestUtils = require('internal-test-utils');
31 + waitForPaint = InternalTestUtils.waitForPaint;
32
33 logs = [];
34
@@ -100,7 +101,7 @@ describe('DebugTracing', () => {
101 });
102
103 // @gate experimental && build === 'development' && enableDebugTracing && enableCPUSuspense
103 - it('should log sync render with CPU suspense', () => {
104 + it('should log sync render with CPU suspense', async () => {
105 function Example() {
106 console.log('<Example/>');
107 return null;
@@ -129,7 +130,7 @@ describe('DebugTracing', () => {
130
131 logs.splice(0);
132
132 - expect(Scheduler).toFlushUntilNextPaint([]);
133 + await waitForPaint([]);
134
135 expect(logs).toEqual([
136 `group: ⚛️ render (${RETRY_LANE_STRING})`,
packages/react-reconciler/src/__tests__/ReactActWarnings-test.js
+37 -31
@@ -9,6 +9,8 @@
9
10 let React;
11 let Scheduler;
12 +let waitForAll;
13 +let assertLog;
14 let ReactNoop;
15 let useState;
16 let act;
@@ -32,6 +34,10 @@ describe('act warnings', () => {
34 startTransition = React.startTransition;
35 getCacheForType = React.unstable_getCacheForType;
36 caches = [];
37 +
38 + const InternalTestUtils = require('internal-test-utils');
39 + waitForAll = InternalTestUtils.waitForAll;
40 + assertLog = InternalTestUtils.assertLog;
41 });
42
43 function createTextCache() {
@@ -134,17 +140,17 @@ describe('act warnings', () => {
140 }
141 }
142
137 - function withActEnvironment(value, scope) {
143 + async function withActEnvironment(value, scope) {
144 const prevValue = global.IS_REACT_ACT_ENVIRONMENT;
145 global.IS_REACT_ACT_ENVIRONMENT = value;
146 try {
141 - return scope();
147 + return await scope();
148 } finally {
149 global.IS_REACT_ACT_ENVIRONMENT = prevValue;
150 }
151 }
152
147 - test('warns about unwrapped updates only if environment flag is enabled', () => {
153 + test('warns about unwrapped updates only if environment flag is enabled', async () => {
154 let setState;
155 function App() {
156 const [state, _setState] = useState(0);
@@ -154,34 +160,34 @@ describe('act warnings', () => {
160
161 const root = ReactNoop.createRoot();
162 root.render(<App />);
157 - expect(Scheduler).toFlushAndYield([0]);
163 + await waitForAll([0]);
164 expect(root).toMatchRenderedOutput('0');
165
166 // Default behavior. Flag is undefined. No warning.
167 expect(global.IS_REACT_ACT_ENVIRONMENT).toBe(undefined);
168 setState(1);
163 - expect(Scheduler).toFlushAndYield([1]);
169 + await waitForAll([1]);
170 expect(root).toMatchRenderedOutput('1');
171
172 // Flag is true. Warn.
167 - withActEnvironment(true, () => {
173 + await withActEnvironment(true, async () => {
174 expect(() => setState(2)).toErrorDev(
175 'An update to App inside a test was not wrapped in act',
176 );
171 - expect(Scheduler).toFlushAndYield([2]);
177 + await waitForAll([2]);
178 expect(root).toMatchRenderedOutput('2');
179 });
180
181 // Flag is false. No warning.
176 - withActEnvironment(false, () => {
182 + await withActEnvironment(false, async () => {
183 setState(3);
178 - expect(Scheduler).toFlushAndYield([3]);
184 + await waitForAll([3]);
185 expect(root).toMatchRenderedOutput('3');
186 });
187 });
188
189 // @gate __DEV__
184 - test('act warns if the environment flag is not enabled', () => {
190 + test('act warns if the environment flag is not enabled', async () => {
191 let setState;
192 function App() {
193 const [state, _setState] = useState(0);
@@ -191,7 +197,7 @@ describe('act warnings', () => {
197
198 const root = ReactNoop.createRoot();
199 root.render(<App />);
194 - expect(Scheduler).toFlushAndYield([0]);
200 + await waitForAll([0]);
201 expect(root).toMatchRenderedOutput('0');
202
203 // Default behavior. Flag is undefined. Warn.
@@ -204,20 +210,20 @@ describe('act warnings', () => {
210 'The current testing environment is not configured to support act(...)',
211 {withoutStack: true},
212 );
207 - expect(Scheduler).toHaveYielded([1]);
213 + assertLog([1]);
214 expect(root).toMatchRenderedOutput('1');
215
216 // Flag is true. Don't warn.
211 - withActEnvironment(true, () => {
217 + await withActEnvironment(true, () => {
218 act(() => {
219 setState(2);
220 });
215 - expect(Scheduler).toHaveYielded([2]);
221 + assertLog([2]);
222 expect(root).toMatchRenderedOutput('2');
223 });
224
225 // Flag is false. Warn.
220 - withActEnvironment(false, () => {
226 + await withActEnvironment(false, () => {
227 expect(() => {
228 act(() => {
229 setState(1);
@@ -226,13 +232,13 @@ describe('act warnings', () => {
232 'The current testing environment is not configured to support act(...)',
233 {withoutStack: true},
234 );
229 - expect(Scheduler).toHaveYielded([1]);
235 + assertLog([1]);
236 expect(root).toMatchRenderedOutput('1');
237 });
238 });
239
234 - test('warns if root update is not wrapped', () => {
235 - withActEnvironment(true, () => {
240 + test('warns if root update is not wrapped', async () => {
241 + await withActEnvironment(true, () => {
242 const root = ReactNoop.createRoot();
243 expect(() => root.render('Hi')).toErrorDev(
244 // TODO: Better error message that doesn't make it look like "Root" is
@@ -244,7 +250,7 @@ describe('act warnings', () => {
250 });
251
252 // @gate __DEV__
247 - test('warns if class update is not wrapped', () => {
253 + test('warns if class update is not wrapped', async () => {
254 let app;
255 class App extends React.Component {
256 state = {count: 0};
@@ -254,7 +260,7 @@ describe('act warnings', () => {
260 }
261 }
262
257 - withActEnvironment(true, () => {
263 + await withActEnvironment(true, () => {
264 const root = ReactNoop.createRoot();
265 act(() => {
266 root.render(<App />);
@@ -266,7 +272,7 @@ describe('act warnings', () => {
272 });
273
274 // @gate __DEV__
269 - test('warns even if update is synchronous', () => {
275 + test('warns even if update is synchronous', async () => {
276 let setState;
277 function App() {
278 const [state, _setState] = useState(0);
@@ -274,10 +280,10 @@ describe('act warnings', () => {
280 return <Text text={state} />;
281 }
282
277 - withActEnvironment(true, () => {
283 + await withActEnvironment(true, () => {
284 const root = ReactNoop.createRoot();
285 act(() => root.render(<App />));
280 - expect(Scheduler).toHaveYielded([0]);
286 + assertLog([0]);
287 expect(root).toMatchRenderedOutput('0');
288
289 // Even though this update is synchronous, we should still fire a warning,
@@ -286,14 +292,14 @@ describe('act warnings', () => {
292 'An update to App inside a test was not wrapped in act(...)',
293 );
294
289 - expect(Scheduler).toHaveYielded([1]);
295 + assertLog([1]);
296 expect(root).toMatchRenderedOutput('1');
297 });
298 });
299
300 // @gate __DEV__
301 // @gate enableLegacyCache
296 - test('warns if Suspense retry is not wrapped', () => {
302 + test('warns if Suspense retry is not wrapped', async () => {
303 function App() {
304 return (
305 <Suspense fallback={<Text text="Loading..." />}>
@@ -302,12 +308,12 @@ describe('act warnings', () => {
308 );
309 }
310
305 - withActEnvironment(true, () => {
311 + await withActEnvironment(true, () => {
312 const root = ReactNoop.createRoot();
313 act(() => {
314 root.render(<App />);
315 });
310 - expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
316 + assertLog(['Suspend! [Async]', 'Loading...']);
317 expect(root).toMatchRenderedOutput('Loading...');
318
319 // This is a retry, not a ping, because we already showed a fallback.
@@ -321,7 +327,7 @@ describe('act warnings', () => {
327
328 // @gate __DEV__
329 // @gate enableLegacyCache
324 - test('warns if Suspense ping is not wrapped', () => {
330 + test('warns if Suspense ping is not wrapped', async () => {
331 function App({showMore}) {
332 return (
333 <Suspense fallback={<Text text="Loading..." />}>
@@ -330,12 +336,12 @@ describe('act warnings', () => {
336 );
337 }
338
333 - withActEnvironment(true, () => {
339 + await withActEnvironment(true, () => {
340 const root = ReactNoop.createRoot();
341 act(() => {
342 root.render(<App showMore={false} />);
343 });
338 - expect(Scheduler).toHaveYielded(['(empty)']);
344 + assertLog(['(empty)']);
345 expect(root).toMatchRenderedOutput('(empty)');
346
347 act(() => {
@@ -343,7 +349,7 @@ describe('act warnings', () => {
349 root.render(<App showMore={true} />);
350 });
351 });
346 - expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
352 + assertLog(['Suspend! [Async]', 'Loading...']);
353 expect(root).toMatchRenderedOutput('(empty)');
354
355 // This is a ping, not a retry, because no fallback is showing.
packages/react-reconciler/src/__tests__/ReactBatching-test.internal.js
+19 -17
@@ -2,6 +2,8 @@ let React;
2 let ReactFeatureFlags;
3 let ReactNoop;
4 let Scheduler;
5 +let waitForAll;
6 +let assertLog;
7 let ReactCache;
8 let Suspense;
9 let TextResource;
@@ -18,6 +20,10 @@ describe('ReactBlockingMode', () => {
20 ReactCache = require('react-cache');
21 Suspense = React.Suspense;
22
23 + const InternalTestUtils = require('internal-test-utils');
24 + waitForAll = InternalTestUtils.waitForAll;
25 + assertLog = InternalTestUtils.assertLog;
26 +
27 TextResource = ReactCache.unstable_createResource(
28 ([text, ms = 0]) => {
29 return new Promise((resolve, reject) =>
@@ -52,7 +58,7 @@ describe('ReactBlockingMode', () => {
58 }
59 }
60
55 - it('updates flush without yielding in the next event', () => {
61 + it('updates flush without yielding in the next event', async () => {
62 const root = ReactNoop.createRoot();
63
64 root.render(
@@ -66,12 +72,11 @@ describe('ReactBlockingMode', () => {
72 // Nothing should have rendered yet
73 expect(root).toMatchRenderedOutput(null);
74
69 - // Everything should render immediately in the next event
70 - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']);
75 + await waitForAll(['A', 'B', 'C']);
76 expect(root).toMatchRenderedOutput('ABC');
77 });
78
74 - it('layout updates flush synchronously in same event', () => {
79 + it('layout updates flush synchronously in same event', async () => {
80 const {useLayoutEffect} = React;
81
82 function App() {
@@ -84,9 +89,9 @@ describe('ReactBlockingMode', () => {
89 const root = ReactNoop.createRoot();
90 root.render(<App />);
91 expect(root).toMatchRenderedOutput(null);
87 - expect(Scheduler).toHaveYielded([]);
92 + assertLog([]);
93
89 - expect(Scheduler).toFlushAndYield(['Hi', 'Layout effect']);
94 + await waitForAll(['Hi', 'Layout effect']);
95 expect(root).toMatchRenderedOutput('Hi');
96 });
97
@@ -106,15 +111,15 @@ describe('ReactBlockingMode', () => {
111 </Suspense>,
112 );
113
109 - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'C', 'Loading...']);
114 + await waitForAll(['A', 'Suspend! [B]', 'C', 'Loading...']);
115 // In Legacy Mode, A and B would mount in a hidden primary tree. In
116 // Concurrent Mode, nothing in the primary tree should mount. But the
117 // fallback should mount immediately.
118 expect(root).toMatchRenderedOutput('Loading...');
119
120 await jest.advanceTimersByTime(1000);
116 - expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
117 - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']);
121 + assertLog(['Promise resolved [B]']);
122 + await waitForAll(['A', 'B', 'C']);
123 expect(root).toMatchRenderedOutput(
124 <>
125 <span>A</span>
@@ -124,7 +129,7 @@ describe('ReactBlockingMode', () => {
129 );
130 });
131
127 - it('flushSync does not flush batched work', () => {
132 + it('flushSync does not flush batched work', async () => {
133 const {useState, forwardRef, useImperativeHandle} = React;
134 const root = ReactNoop.createRoot();
135
@@ -143,8 +148,7 @@ describe('ReactBlockingMode', () => {
148 </>,
149 );
150
146 - // Mount
147 - expect(Scheduler).toFlushAndYield(['A0', 'B0']);
151 + await waitForAll(['A0', 'B0']);
152 expect(root).toMatchRenderedOutput('A0B0');
153
154 // Schedule a batched update to the first sibling
@@ -159,15 +163,13 @@ describe('ReactBlockingMode', () => {
163
164 // Now flush the first update
165 if (gate(flags => flags.enableUnifiedSyncLane)) {
162 - expect(Scheduler).toHaveYielded(['A1', 'B1']);
166 + assertLog(['A1', 'B1']);
167 expect(root).toMatchRenderedOutput('A1B1');
168 } else {
165 - // Only the second update should have flushed synchronously
166 - expect(Scheduler).toHaveYielded(['B1']);
169 + assertLog(['B1']);
170 expect(root).toMatchRenderedOutput('A0B1');
171
169 - // Now flush the first update
170 - expect(Scheduler).toFlushAndYield(['A1']);
172 + await waitForAll(['A1']);
173 expect(root).toMatchRenderedOutput('A1B1');
174 }
175 });
packages/react-reconciler/src/__tests__/ReactCPUSuspense-test.js
+15 -19
@@ -10,6 +10,9 @@ let readText;
10 let resolveText;
11 // let rejectText;
12
13 +let assertLog;
14 +let waitForPaint;
15 +
16 describe('ReactSuspenseWithNoopRenderer', () => {
17 beforeEach(() => {
18 jest.resetModules();
@@ -21,6 +24,10 @@ describe('ReactSuspenseWithNoopRenderer', () => {
24 Suspense = React.Suspense;
25 useState = React.useState;
26
27 + const InternalTestUtils = require('internal-test-utils');
28 + assertLog = InternalTestUtils.assertLog;
29 + waitForPaint = InternalTestUtils.waitForPaint;
30 +
31 textCache = new Map();
32
33 readText = text => {
@@ -128,7 +135,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
135 const root = ReactNoop.createRoot();
136 await act(async () => {
137 root.render(<App />);
131 - expect(Scheduler).toFlushUntilNextPaint(['Outer', 'Loading...']);
138 + await waitForPaint(['Outer', 'Loading...']);
139 expect(root).toMatchRenderedOutput(
140 <>
141 Outer
@@ -136,8 +143,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
143 </>,
144 );
145 });
139 - // Inner contents finish in separate commit from outer
140 - expect(Scheduler).toHaveYielded(['Inner']);
146 + assertLog(['Inner']);
147 expect(root).toMatchRenderedOutput(
148 <>
149 Outer
@@ -172,8 +178,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
178 await act(async () => {
179 root.render(<App />);
180 });
175 - // Inner contents finish in separate commit from outer
176 - expect(Scheduler).toHaveYielded(['Outer', 'Loading...', 'Inner [0]']);
181 + assertLog(['Outer', 'Loading...', 'Inner [0]']);
182 expect(root).toMatchRenderedOutput(
183 <>
184 Outer
@@ -185,8 +190,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
190 await act(async () => {
191 setCount(1);
192 });
188 - // Entire update finishes in a single commit
189 - expect(Scheduler).toHaveYielded(['Outer', 'Inner [1]']);
193 + assertLog(['Outer', 'Inner [1]']);
194 expect(root).toMatchRenderedOutput(
195 <>
196 Outer
@@ -215,7 +219,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
219 const root = ReactNoop.createRoot();
220 await act(async () => {
221 root.render(<App />);
218 - expect(Scheduler).toFlushUntilNextPaint(['Outer', 'Loading...']);
222 + await waitForPaint(['Outer', 'Loading...']);
223 expect(root).toMatchRenderedOutput(
224 <>
225 Outer
@@ -223,8 +227,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
227 </>,
228 );
229 });
226 - // Inner contents suspended, so we continue showing a fallback.
227 - expect(Scheduler).toHaveYielded(['Suspend! [Inner]']);
230 + assertLog(['Suspend! [Inner]']);
231 expect(root).toMatchRenderedOutput(
232 <>
233 Outer
@@ -236,7 +239,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
239 await act(async () => {
240 await resolveText('Inner');
241 });
239 - expect(Scheduler).toHaveYielded(['Inner']);
242 + assertLog(['Inner']);
243 expect(root).toMatchRenderedOutput(
244 <>
245 Outer
@@ -273,14 +276,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
276 await act(async () => {
277 root.render(<App />);
278 });
276 - // Each level commits separately
277 - expect(Scheduler).toHaveYielded([
278 - 'A',
279 - 'Loading B...',
280 - 'B',
281 - 'Loading C...',
282 - 'C',
283 - ]);
279 + assertLog(['A', 'Loading B...', 'B', 'Loading C...', 'C']);
280 expect(root).toMatchRenderedOutput(
281 <>
282 A
packages/react-reconciler/src/__tests__/ReactCache-test.js
+108 -170
@@ -3,6 +3,7 @@ let ReactNoop;
3 let Cache;
4 let getCacheSignal;
5 let Scheduler;
6 +let assertLog;
7 let act;
8 let Suspense;
9 let Offscreen;
@@ -32,6 +33,9 @@ describe('ReactCache', () => {
33 startTransition = React.startTransition;
34 useState = React.useState;
35
36 + const InternalTestUtils = require('internal-test-utils');
37 + assertLog = InternalTestUtils.assertLog;
38 +
39 textCaches = [];
40 seededCache = null;
41
@@ -203,20 +207,19 @@ describe('ReactCache', () => {
207 </Cache>,
208 );
209 });
206 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
210 + assertLog(['Cache miss! [A]', 'Loading...']);
211 expect(root).toMatchRenderedOutput('Loading...');
212
213 await act(async () => {
214 resolveMostRecentTextCache('A');
215 });
212 - expect(Scheduler).toHaveYielded(['A']);
216 + assertLog(['A']);
217 expect(root).toMatchRenderedOutput('A');
218
219 await act(async () => {
220 root.render('Bye');
221 });
218 - // no cleanup: cache is still retained at the root
219 - expect(Scheduler).toHaveYielded([]);
222 + assertLog([]);
223 expect(root).toMatchRenderedOutput('Bye');
224 });
225
@@ -230,20 +233,19 @@ describe('ReactCache', () => {
233 </Suspense>,
234 );
235 });
233 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
236 + assertLog(['Cache miss! [A]', 'Loading...']);
237 expect(root).toMatchRenderedOutput('Loading...');
238
239 await act(async () => {
240 resolveMostRecentTextCache('A');
241 });
239 - expect(Scheduler).toHaveYielded(['A']);
242 + assertLog(['A']);
243 expect(root).toMatchRenderedOutput('A');
244
245 await act(async () => {
246 root.render('Bye');
247 });
245 - // no cleanup: cache is still retained at the root
246 - expect(Scheduler).toHaveYielded([]);
248 + assertLog([]);
249 expect(root).toMatchRenderedOutput('Bye');
250 });
251
@@ -271,26 +273,19 @@ describe('ReactCache', () => {
273 root.render(<App showMore={false} />);
274 });
275
274 - // Even though there are two new <Cache /> trees, they should share the same
275 - // data cache. So there should be only a single cache miss for A.
276 - expect(Scheduler).toHaveYielded([
277 - 'Cache miss! [A]',
278 - 'Loading...',
279 - 'Loading...',
280 - ]);
276 + assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
277 expect(root).toMatchRenderedOutput('Loading...Loading...');
278
279 await act(async () => {
280 resolveMostRecentTextCache('A');
281 });
286 - expect(Scheduler).toHaveYielded(['A', 'A']);
282 + assertLog(['A', 'A']);
283 expect(root).toMatchRenderedOutput('AA');
284
285 await act(async () => {
286 root.render('Bye');
287 });
292 - // no cleanup: cache is still retained at the root
293 - expect(Scheduler).toHaveYielded([]);
288 + assertLog([]);
289 expect(root).toMatchRenderedOutput('Bye');
290 });
291
@@ -319,34 +314,25 @@ describe('ReactCache', () => {
314 await act(async () => {
315 root.render(<App showMore={false} />);
316 });
322 - expect(Scheduler).toHaveYielded([]);
317 + assertLog([]);
318 expect(root).toMatchRenderedOutput('(empty)');
319
320 await act(async () => {
321 root.render(<App showMore={true} />);
322 });
328 - // Even though there are two new <Cache /> trees, they should share the same
329 - // data cache. So there should be only a single cache miss for A.
330 - expect(Scheduler).toHaveYielded([
331 - 'Cache miss! [A]',
332 - 'Loading...',
333 - 'Loading...',
334 - ]);
323 + assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
324 expect(root).toMatchRenderedOutput('Loading...Loading...');
325
326 await act(async () => {
327 resolveMostRecentTextCache('A');
328 });
340 - expect(Scheduler).toHaveYielded(['A', 'A']);
329 + assertLog(['A', 'A']);
330 expect(root).toMatchRenderedOutput('AA');
331
332 await act(async () => {
333 root.render('Bye');
334 });
346 - // cleanup occurs for the cache shared by the inner cache boundaries (which
347 - // are not shared w the root because they were added in an update)
348 - // note that no cache is created for the root since the cache is never accessed
349 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v1]']);
335 + assertLog(['Cache cleanup: A [v1]']);
336 expect(root).toMatchRenderedOutput('Bye');
337 });
338
@@ -370,22 +356,19 @@ describe('ReactCache', () => {
356 await act(async () => {
357 root.render(<App />);
358 });
373 - // Even though there is a nested <Cache /> boundary, it should share the same
374 - // data cache as the root. So there should be only a single cache miss for A.
375 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
359 + assertLog(['Cache miss! [A]', 'Loading...']);
360 expect(root).toMatchRenderedOutput('Loading...');
361
362 await act(async () => {
363 resolveMostRecentTextCache('A');
364 });
381 - expect(Scheduler).toHaveYielded(['A', 'A']);
365 + assertLog(['A', 'A']);
366 expect(root).toMatchRenderedOutput('AA');
367
368 await act(async () => {
369 root.render('Bye');
370 });
387 - // no cleanup: cache is still retained at the root
388 - expect(Scheduler).toHaveYielded([]);
371 + assertLog([]);
372 expect(root).toMatchRenderedOutput('Bye');
373 },
374 );
@@ -412,14 +395,14 @@ describe('ReactCache', () => {
395 seedNextTextCache('A');
396 root.render(<App showMore={false} />);
397 });
415 - expect(Scheduler).toHaveYielded(['A [v1]']);
398 + assertLog(['A [v1]']);
399 expect(root).toMatchRenderedOutput('A [v1]');
400
401 // Add a new cache boundary
402 await act(async () => {
403 root.render(<App showMore={true} />);
404 });
422 - expect(Scheduler).toHaveYielded([
405 + assertLog([
406 'A [v1]',
407 // New tree should use already cached data
408 'A [v1]',
@@ -429,8 +412,7 @@ describe('ReactCache', () => {
412 await act(async () => {
413 root.render('Bye');
414 });
432 - // no cleanup: cache is still retained at the root
433 - expect(Scheduler).toHaveYielded([]);
415 + assertLog([]);
416 expect(root).toMatchRenderedOutput('Bye');
417 });
418
@@ -460,14 +442,14 @@ describe('ReactCache', () => {
442 seedNextTextCache('A');
443 root.render(<App showMore={false} />);
444 });
463 - expect(Scheduler).toHaveYielded(['A [v1]']);
445 + assertLog(['A [v1]']);
446 expect(root).toMatchRenderedOutput('A [v1]');
447
448 // Add a new cache boundary
449 await act(async () => {
450 root.render(<App showMore={true} />);
451 });
470 - expect(Scheduler).toHaveYielded([
452 + assertLog([
453 'A [v1]',
454 // New tree should load fresh data.
455 'Cache miss! [A]',
@@ -477,7 +459,7 @@ describe('ReactCache', () => {
459 await act(async () => {
460 resolveMostRecentTextCache('A');
461 });
480 - expect(Scheduler).toHaveYielded(['A [v2]']);
462 + assertLog(['A [v2]']);
463 expect(root).toMatchRenderedOutput('A [v1]A [v2]');
464
465 // Replace all the children: this should retain the root Cache instance,
@@ -486,9 +468,7 @@ describe('ReactCache', () => {
468 await act(async () => {
469 root.render('Bye!');
470 });
489 - // Cleanup occurs for the *second* cache instance: the first is still
490 - // referenced by the root
491 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v2]']);
471 + assertLog(['Cache cleanup: A [v2]']);
472 expect(root).toMatchRenderedOutput('Bye!');
473 });
474
@@ -535,13 +515,13 @@ describe('ReactCache', () => {
515 await act(async () => {
516 root.render(<App />);
517 });
538 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading shell...']);
518 + assertLog(['Cache miss! [A]', 'Loading shell...']);
519 expect(root).toMatchRenderedOutput('Loading shell...');
520
521 await act(async () => {
522 resolveMostRecentTextCache('A');
523 });
544 - expect(Scheduler).toHaveYielded([
524 + assertLog([
525 'Shell',
526 // There's a cache miss for B, because it hasn't been read yet. But not
527 // A, because it was cached when we rendered the shell.
@@ -558,7 +538,7 @@ describe('ReactCache', () => {
538 await act(async () => {
539 resolveMostRecentTextCache('B');
540 });
561 - expect(Scheduler).toHaveYielded(['Content']);
541 + assertLog(['Content']);
542 expect(root).toMatchRenderedOutput(
543 <>
544 <div>Shell</div>
@@ -569,8 +549,7 @@ describe('ReactCache', () => {
549 await act(async () => {
550 root.render('Bye');
551 });
572 - // no cleanup: cache is still retained at the root
573 - expect(Scheduler).toHaveYielded([]);
552 + assertLog([]);
553 expect(root).toMatchRenderedOutput('Bye');
554 });
555
@@ -619,19 +598,19 @@ describe('ReactCache', () => {
598 await act(async () => {
599 root.render(<App showMore={false} />);
600 });
622 - expect(Scheduler).toHaveYielded([]);
601 + assertLog([]);
602 expect(root).toMatchRenderedOutput('(empty)');
603
604 await act(async () => {
605 root.render(<App showMore={true} />);
606 });
628 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading shell...']);
607 + assertLog(['Cache miss! [A]', 'Loading shell...']);
608 expect(root).toMatchRenderedOutput('Loading shell...');
609
610 await act(async () => {
611 resolveMostRecentTextCache('A');
612 });
634 - expect(Scheduler).toHaveYielded([
613 + assertLog([
614 'Shell',
615 // There's a cache miss for B, because it hasn't been read yet. But not
616 // A, because it was cached when we rendered the shell.
@@ -648,7 +627,7 @@ describe('ReactCache', () => {
627 await act(async () => {
628 resolveMostRecentTextCache('B');
629 });
651 - expect(Scheduler).toHaveYielded(['Content']);
630 + assertLog(['Content']);
631 expect(root).toMatchRenderedOutput(
632 <>
633 <div>Shell</div>
@@ -659,10 +638,7 @@ describe('ReactCache', () => {
638 await act(async () => {
639 root.render('Bye');
640 });
662 - expect(Scheduler).toHaveYielded([
663 - 'Cache cleanup: A [v1]',
664 - 'Cache cleanup: B [v1]',
665 - ]);
641 + assertLog(['Cache cleanup: A [v1]', 'Cache cleanup: B [v1]']);
642 expect(root).toMatchRenderedOutput('Bye');
643 });
644
@@ -683,20 +659,20 @@ describe('ReactCache', () => {
659 </Suspense>,
660 );
661 });
686 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
662 + assertLog(['Cache miss! [A]', 'Loading...']);
663 expect(root).toMatchRenderedOutput('Loading...');
664
665 await act(async () => {
666 resolveMostRecentTextCache('A');
667 });
692 - expect(Scheduler).toHaveYielded(['A [v1]']);
668 + assertLog(['A [v1]']);
669 expect(root).toMatchRenderedOutput('A [v1]');
670
671 // Refresh for new data.
672 await act(async () => {
673 startTransition(() => refresh());
674 });
699 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
675 + assertLog(['Cache miss! [A]', 'Loading...']);
676 expect(root).toMatchRenderedOutput('A [v1]');
677
678 await act(async () => {
@@ -704,9 +680,9 @@ describe('ReactCache', () => {
680 });
681 // Note that the version has updated
682 if (getCacheSignal) {
707 - expect(Scheduler).toHaveYielded(['A [v2]', 'Cache cleanup: A [v1]']);
683 + assertLog(['A [v2]', 'Cache cleanup: A [v1]']);
684 } else {
709 - expect(Scheduler).toHaveYielded(['A [v2]']);
685 + assertLog(['A [v2]']);
686 }
687 expect(root).toMatchRenderedOutput('A [v2]');
688
@@ -733,34 +709,32 @@ describe('ReactCache', () => {
709 </Suspense>,
710 );
711 });
736 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
712 + assertLog(['Cache miss! [A]', 'Loading...']);
713 expect(root).toMatchRenderedOutput('Loading...');
714
715 await act(async () => {
716 resolveMostRecentTextCache('A');
717 });
742 - expect(Scheduler).toHaveYielded(['A [v1]']);
718 + assertLog(['A [v1]']);
719 expect(root).toMatchRenderedOutput('A [v1]');
720
721 // Refresh for new data.
722 await act(async () => {
723 startTransition(() => refresh());
724 });
749 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
725 + assertLog(['Cache miss! [A]', 'Loading...']);
726 expect(root).toMatchRenderedOutput('A [v1]');
727
728 await act(async () => {
729 resolveMostRecentTextCache('A');
730 });
755 - // Note that the version has updated, and the previous cache is cleared
756 - expect(Scheduler).toHaveYielded(['A [v2]', 'Cache cleanup: A [v1]']);
731 + assertLog(['A [v2]', 'Cache cleanup: A [v1]']);
732 expect(root).toMatchRenderedOutput('A [v2]');
733
734 await act(async () => {
735 root.render('Bye');
736 });
762 - // the original root cache already cleaned up when the refresh completed
763 - expect(Scheduler).toHaveYielded([]);
737 + assertLog([]);
738 expect(root).toMatchRenderedOutput('Bye');
739 });
740
@@ -781,20 +755,20 @@ describe('ReactCache', () => {
755 </Suspense>,
756 );
757 });
784 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
758 + assertLog(['Cache miss! [A]', 'Loading...']);
759 expect(root).toMatchRenderedOutput('Loading...');
760
761 await act(async () => {
762 resolveMostRecentTextCache('A');
763 });
790 - expect(Scheduler).toHaveYielded(['A [v1]']);
764 + assertLog(['A [v1]']);
765 expect(root).toMatchRenderedOutput('A [v1]');
766
767 // Refresh for new data.
768 await act(async () => {
769 refresh();
770 });
797 - expect(Scheduler).toHaveYielded([
771 + assertLog([
772 'Cache miss! [A]',
773 'Loading...',
774 // The v1 cache can be cleaned up since everything that references it has
@@ -807,15 +781,13 @@ describe('ReactCache', () => {
781 await act(async () => {
782 resolveMostRecentTextCache('A');
783 });
810 - // Note that the version has updated, and the previous cache is cleared
811 - expect(Scheduler).toHaveYielded(['A [v2]']);
784 + assertLog(['A [v2]']);
785 expect(root).toMatchRenderedOutput('A [v2]');
786
787 await act(async () => {
788 root.render('Bye');
789 });
817 - // the original root cache already cleaned up when the refresh completed
818 - expect(Scheduler).toHaveYielded([]);
790 + assertLog([]);
791 expect(root).toMatchRenderedOutput('Bye');
792 });
793
@@ -847,13 +819,13 @@ describe('ReactCache', () => {
819 </Cache>,
820 );
821 });
850 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
822 + assertLog(['Cache miss! [A]', 'Loading...']);
823 expect(root).toMatchRenderedOutput('Loading...');
824
825 await act(async () => {
826 resolveMostRecentTextCache('A');
827 });
856 - expect(Scheduler).toHaveYielded(['A [v1]']);
828 + assertLog(['A [v1]']);
829 expect(root).toMatchRenderedOutput('A [v1]');
830
831 // Refresh for new data.
@@ -869,16 +841,13 @@ describe('ReactCache', () => {
841 }),
842 );
843 });
872 - // The root should re-render without a cache miss.
873 - // The cache is not cleared up yet, since it's still reference by the root
874 - expect(Scheduler).toHaveYielded(['A [v2]']);
844 + assertLog(['A [v2]']);
845 expect(root).toMatchRenderedOutput('A [v2]');
846
847 await act(async () => {
848 root.render('Bye');
849 });
880 - // the refreshed cache boundary is unmounted and cleans up
881 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v2]']);
850 + assertLog(['Cache cleanup: A [v2]']);
851 expect(root).toMatchRenderedOutput('Bye');
852 });
853
@@ -913,7 +882,7 @@ describe('ReactCache', () => {
882 seedNextTextCache('A');
883 root.render(<App showMore={false} />);
884 });
916 - expect(Scheduler).toHaveYielded(['A [v1]']);
885 + assertLog(['A [v1]']);
886 expect(root).toMatchRenderedOutput('A [v1]');
887
888 // Add a new cache boundary
@@ -921,7 +890,7 @@ describe('ReactCache', () => {
890 seedNextTextCache('A');
891 root.render(<App showMore={true} />);
892 });
924 - expect(Scheduler).toHaveYielded([
893 + assertLog([
894 'A [v1]',
895 // New tree should load fresh data.
896 'A [v2]',
@@ -933,17 +902,13 @@ describe('ReactCache', () => {
902 await act(async () => {
903 startTransition(() => refreshShell());
904 });
936 - expect(Scheduler).toHaveYielded([
937 - 'Cache miss! [A]',
938 - 'Loading...',
939 - 'Loading...',
940 - ]);
905 + assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
906 expect(root).toMatchRenderedOutput('A [v1]A [v2]');
907
908 await act(async () => {
909 resolveMostRecentTextCache('A');
910 });
946 - expect(Scheduler).toHaveYielded([
911 + assertLog([
912 'A [v3]',
913 'A [v3]',
914 // once the refresh completes the inner showMore boundary frees its previous
@@ -955,9 +920,7 @@ describe('ReactCache', () => {
920 await act(async () => {
921 root.render('Bye!');
922 });
958 - // Unmounting children releases the refreshed cache instance only; the root
959 - // still retains the original cache instance used for the first render
960 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v3]']);
923 + assertLog(['Cache cleanup: A [v3]']);
924 expect(root).toMatchRenderedOutput('Bye!');
925 });
926
@@ -1004,19 +967,13 @@ describe('ReactCache', () => {
967 root.render(<App showMore={true} />);
968 });
969
1007 - // Even though there are two new <Cache /> trees, they should share the same
1008 - // data cache. So there should be only a single cache miss for A.
1009 - expect(Scheduler).toHaveYielded([
1010 - 'Cache miss! [A]',
1011 - 'Loading...',
1012 - 'Loading...',
1013 - ]);
970 + assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
971 expect(root).toMatchRenderedOutput('Loading...Loading...');
972
973 await act(async () => {
974 resolveMostRecentTextCache('A');
975 });
1019 - expect(Scheduler).toHaveYielded(['A [v1]', 'A [v1]']);
976 + assertLog(['A [v1]', 'A [v1]']);
977 expect(root).toMatchRenderedOutput('A [v1]A [v1]');
978
979 // Refresh the first boundary. It should not refresh the second boundary,
@@ -1024,12 +981,12 @@ describe('ReactCache', () => {
981 await act(async () => {
982 await refreshFirstBoundary();
983 });
1027 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
984 + assertLog(['Cache miss! [A]', 'Loading...']);
985
986 await act(async () => {
987 resolveMostRecentTextCache('A');
988 });
1032 - expect(Scheduler).toHaveYielded(['A [v2]']);
989 + assertLog(['A [v2]']);
990 expect(root).toMatchRenderedOutput('A [v2]A [v1]');
991
992 // Unmount children: this should clear *both* cache instances:
@@ -1041,10 +998,7 @@ describe('ReactCache', () => {
998 await act(async () => {
999 root.render('Bye!');
1000 });
1044 - expect(Scheduler).toHaveYielded([
1045 - 'Cache cleanup: A [v2]',
1046 - 'Cache cleanup: A [v1]',
1047 - ]);
1001 + assertLog(['Cache cleanup: A [v2]', 'Cache cleanup: A [v1]']);
1002 expect(root).toMatchRenderedOutput('Bye!');
1003 },
1004 );
@@ -1079,11 +1033,7 @@ describe('ReactCache', () => {
1033 await act(async () => {
1034 root.render(<App showMore={false} />);
1035 });
1082 - expect(Scheduler).toHaveYielded([
1083 - 'Cache miss! [A]',
1084 - 'Cache miss! [B]',
1085 - 'Loading...',
1086 - ]);
1036 + assertLog(['Cache miss! [A]', 'Cache miss! [B]', 'Loading...']);
1037 expect(root).toMatchRenderedOutput('Loading...');
1038
1039 await act(async () => {
@@ -1093,7 +1043,7 @@ describe('ReactCache', () => {
1043 // And mount the second tree, which includes new content
1044 root.render(<App showMore={true} />);
1045 });
1096 - expect(Scheduler).toHaveYielded([
1046 + assertLog([
1047 // The new tree should use a fresh cache
1048 'Cache miss! [A]',
1049 'Loading...',
@@ -1108,16 +1058,13 @@ describe('ReactCache', () => {
1058 await act(async () => {
1059 resolveMostRecentTextCache('A');
1060 });
1111 - expect(Scheduler).toHaveYielded(['A [v2]']);
1061 + assertLog(['A [v2]']);
1062 expect(root).toMatchRenderedOutput('A [v2] A [v1] B [v1]');
1063
1064 await act(async () => {
1065 root.render('Bye!');
1066 });
1117 - // Unmounting children releases both cache boundaries, but the original
1118 - // cache instance (used by second boundary) is still referenced by the root.
1119 - // only the second cache instance is freed.
1120 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v2]']);
1067 + assertLog(['Cache cleanup: A [v2]']);
1068 expect(root).toMatchRenderedOutput('Bye!');
1069 },
1070 );
@@ -1138,7 +1085,7 @@ describe('ReactCache', () => {
1085 <Suspense fallback={<Text text="Loading..." />}>(empty)</Suspense>,
1086 );
1087 });
1141 - expect(Scheduler).toHaveYielded([]);
1088 + assertLog([]);
1089 expect(root).toMatchRenderedOutput('(empty)');
1090
1091 await act(async () => {
@@ -1150,7 +1097,7 @@ describe('ReactCache', () => {
1097 );
1098 });
1099 });
1153 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
1100 + assertLog(['Cache miss! [A]', 'Loading...']);
1101 expect(root).toMatchRenderedOutput('(empty)');
1102
1103 await act(async () => {
@@ -1163,7 +1110,7 @@ describe('ReactCache', () => {
1110 );
1111 });
1112 });
1166 - expect(Scheduler).toHaveYielded([
1113 + assertLog([
1114 // No cache miss, because it uses the pooled cache
1115 'Loading...',
1116 ]);
@@ -1173,7 +1120,7 @@ describe('ReactCache', () => {
1120 await act(async () => {
1121 resolveMostRecentTextCache('A');
1122 });
1176 - expect(Scheduler).toHaveYielded(['A [v1]', 'A [v1]']);
1123 + assertLog(['A [v1]', 'A [v1]']);
1124 expect(root).toMatchRenderedOutput('A [v1]A [v1]');
1125
1126 // Now do another transition
@@ -1188,7 +1135,7 @@ describe('ReactCache', () => {
1135 );
1136 });
1137 });
1191 - expect(Scheduler).toHaveYielded([
1138 + assertLog([
1139 // First two children use the old cache because they already finished
1140 'A [v1]',
1141 'A [v1]',
@@ -1201,7 +1148,7 @@ describe('ReactCache', () => {
1148 await act(async () => {
1149 resolveMostRecentTextCache('A');
1150 });
1204 - expect(Scheduler).toHaveYielded(['A [v1]', 'A [v1]', 'A [v2]']);
1151 + assertLog(['A [v1]', 'A [v1]', 'A [v2]']);
1152 expect(root).toMatchRenderedOutput('A [v1]A [v1]A [v2]');
1153
1154 // Unmount children: the first text cache instance is created only after the root
@@ -1211,10 +1158,7 @@ describe('ReactCache', () => {
1158 await act(async () => {
1159 root.render('Bye!');
1160 });
1214 - expect(Scheduler).toHaveYielded([
1215 - 'Cache cleanup: A [v1]',
1216 - 'Cache cleanup: A [v2]',
1217 - ]);
1161 + assertLog(['Cache cleanup: A [v1]', 'Cache cleanup: A [v2]']);
1162 expect(root).toMatchRenderedOutput('Bye!');
1163 });
1164
@@ -1257,7 +1201,7 @@ describe('ReactCache', () => {
1201 await act(async () => {
1202 root.render(<App />);
1203 });
1260 - expect(Scheduler).toHaveYielded(['0']);
1204 + assertLog(['0']);
1205 expect(root).toMatchRenderedOutput('0');
1206
1207 await act(async () => {
@@ -1265,13 +1209,13 @@ describe('ReactCache', () => {
1209 showMore();
1210 });
1211 });
1268 - expect(Scheduler).toHaveYielded(['Cache miss! [A]', 'Loading...']);
1212 + assertLog(['Cache miss! [A]', 'Loading...']);
1213 expect(root).toMatchRenderedOutput('0');
1214
1215 await act(async () => {
1216 updateUnrelated(1);
1217 });
1274 - expect(Scheduler).toHaveYielded([
1218 + assertLog([
1219 '1',
1220
1221 // Happens to re-render the fallback. Doesn't need to, but not relevant
@@ -1283,7 +1227,7 @@ describe('ReactCache', () => {
1227 await act(async () => {
1228 resolveMostRecentTextCache('A');
1229 });
1286 - expect(Scheduler).toHaveYielded(['A [v1]']);
1230 + assertLog(['A [v1]']);
1231 expect(root).toMatchRenderedOutput('A [v1]1');
1232
1233 // Unmount children: the first text cache instance is created only after initial
@@ -1293,7 +1237,7 @@ describe('ReactCache', () => {
1237 await act(async () => {
1238 root.render('Bye!');
1239 });
1296 - expect(Scheduler).toHaveYielded(['Cache cleanup: A [v1]']);
1240 + assertLog(['Cache cleanup: A [v1]']);
1241 expect(root).toMatchRenderedOutput('Bye!');
1242 });
1243
@@ -1310,7 +1254,7 @@ describe('ReactCache', () => {
1254 </Suspense>,
1255 );
1256 });
1313 - expect(Scheduler).toHaveYielded(['A [v1]']);
1257 + assertLog(['A [v1]']);
1258 expect(root).toMatchRenderedOutput('A [v1]');
1259
1260 seedNextTextCache('B');
@@ -1323,7 +1267,7 @@ describe('ReactCache', () => {
1267 </Suspense>,
1268 );
1269 });
1326 - expect(Scheduler).toHaveYielded(['B [v2]']);
1270 + assertLog(['B [v2]']);
1271 expect(root).toMatchRenderedOutput('B [v2]');
1272
1273 // Unmount children: the fresh cache instance for B cleans up since the cache boundary
@@ -1332,7 +1276,7 @@ describe('ReactCache', () => {
1276 await act(async () => {
1277 root.render('Bye!');
1278 });
1335 - expect(Scheduler).toHaveYielded(['Cache cleanup: B [v2]']);
1279 + assertLog(['Cache cleanup: B [v2]']);
1280 expect(root).toMatchRenderedOutput('Bye!');
1281 });
1282
@@ -1348,13 +1292,13 @@ describe('ReactCache', () => {
1292 </Suspense>,
1293 );
1294 });
1351 - expect(Scheduler).toHaveYielded(['Cache miss! [A]']);
1295 + assertLog(['Cache miss! [A]']);
1296 expect(root).toMatchRenderedOutput('Loading...');
1297
1298 await act(async () => {
1299 resolveMostRecentTextCache('A');
1300 });
1357 - expect(Scheduler).toHaveYielded(['A [v1]']);
1301 + assertLog(['A [v1]']);
1302 expect(root).toMatchRenderedOutput('A [v1]');
1303
1304 // After a mount, subsequent transitions use a fresh cache
@@ -1369,7 +1313,7 @@ describe('ReactCache', () => {
1313 );
1314 });
1315 });
1372 - expect(Scheduler).toHaveYielded(['Cache miss! [B]']);
1316 + assertLog(['Cache miss! [B]']);
1317 expect(root).toMatchRenderedOutput('A [v1]');
1318
1319 // Update to a different text and with a different key for the cache
@@ -1386,13 +1330,13 @@ describe('ReactCache', () => {
1330 );
1331 });
1332 });
1389 - expect(Scheduler).toHaveYielded(['Cache miss! [C]']);
1333 + assertLog(['Cache miss! [C]']);
1334 expect(root).toMatchRenderedOutput('A [v1]');
1335
1336 await act(async () => {
1337 resolveMostRecentTextCache('C');
1338 });
1395 - expect(Scheduler).toHaveYielded(['C [v2]']);
1339 + assertLog(['C [v2]']);
1340 expect(root).toMatchRenderedOutput('C [v2]');
1341
1342 // Unmount children: the fresh cache used for the updates is freed, while the
@@ -1400,10 +1344,7 @@ describe('ReactCache', () => {
1344 await act(async () => {
1345 root.render('Bye!');
1346 });
1403 - expect(Scheduler).toHaveYielded([
1404 - 'Cache cleanup: B [v2]',
1405 - 'Cache cleanup: C [v2]',
1406 - ]);
1347 + assertLog(['Cache cleanup: B [v2]', 'Cache cleanup: C [v2]']);
1348 expect(root).toMatchRenderedOutput('Bye!');
1349 });
1350
@@ -1419,13 +1360,13 @@ describe('ReactCache', () => {
1360 </Suspense>,
1361 );
1362 });
1422 - expect(Scheduler).toHaveYielded(['Cache miss! [A]']);
1363 + assertLog(['Cache miss! [A]']);
1364 expect(root).toMatchRenderedOutput('Loading...');
1365
1366 await act(async () => {
1367 resolveMostRecentTextCache('A');
1368 });
1428 - expect(Scheduler).toHaveYielded(['A [v1]']);
1369 + assertLog(['A [v1]']);
1370 expect(root).toMatchRenderedOutput('A [v1]');
1371
1372 // After a mount, subsequent updates use a fresh cache
@@ -1438,7 +1379,7 @@ describe('ReactCache', () => {
1379 </Suspense>,
1380 );
1381 });
1441 - expect(Scheduler).toHaveYielded(['Cache miss! [B]']);
1382 + assertLog(['Cache miss! [B]']);
1383 expect(root).toMatchRenderedOutput('Loading...');
1384
1385 // A second update uses the same fresh cache: even though this is a new
@@ -1452,13 +1393,13 @@ describe('ReactCache', () => {
1393 </Suspense>,
1394 );
1395 });
1455 - expect(Scheduler).toHaveYielded(['Cache miss! [C]']);
1396 + assertLog(['Cache miss! [C]']);
1397 expect(root).toMatchRenderedOutput('Loading...');
1398
1399 await act(async () => {
1400 resolveMostRecentTextCache('C');
1401 });
1461 - expect(Scheduler).toHaveYielded(['C [v2]']);
1402 + assertLog(['C [v2]']);
1403 expect(root).toMatchRenderedOutput('C [v2]');
1404
1405 // Unmount children: the fresh cache used for the updates is freed, while the
@@ -1466,10 +1407,7 @@ describe('ReactCache', () => {
1407 await act(async () => {
1408 root.render('Bye!');
1409 });
1469 - expect(Scheduler).toHaveYielded([
1470 - 'Cache cleanup: B [v2]',
1471 - 'Cache cleanup: C [v2]',
1472 - ]);
1410 + assertLog(['Cache cleanup: B [v2]', 'Cache cleanup: C [v2]']);
1411 expect(root).toMatchRenderedOutput('Bye!');
1412 });
1413
@@ -1486,7 +1424,7 @@ describe('ReactCache', () => {
1424 </Suspense>,
1425 );
1426 });
1489 - expect(Scheduler).toHaveYielded(['A [v1]']);
1427 + assertLog(['A [v1]']);
1428 expect(root).toMatchRenderedOutput('A [v1]');
1429
1430 // Start a transition from A -> B..., which should create a fresh cache
@@ -1502,7 +1440,7 @@ describe('ReactCache', () => {
1440 );
1441 });
1442 });
1505 - expect(Scheduler).toHaveYielded(['Cache miss! [B]']);
1443 + assertLog(['Cache miss! [B]']);
1444 expect(root).toMatchRenderedOutput('A [v1]');
1445
1446 // ...but cancel by transitioning "back" to A (which we never really left)
@@ -1517,14 +1455,14 @@ describe('ReactCache', () => {
1455 );
1456 });
1457 });
1520 - expect(Scheduler).toHaveYielded(['A [v1]', 'Cache cleanup: B [v2]']);
1458 + assertLog(['A [v1]', 'Cache cleanup: B [v2]']);
1459 expect(root).toMatchRenderedOutput('A [v1]');
1460
1461 // Unmount children: ...
1462 await act(async () => {
1463 root.render('Bye!');
1464 });
1527 - expect(Scheduler).toHaveYielded([]);
1465 + assertLog([]);
1466 expect(root).toMatchRenderedOutput('Bye!');
1467 });
1468
@@ -1544,7 +1482,7 @@ describe('ReactCache', () => {
1482 </Suspense>,
1483 );
1484 });
1547 - expect(Scheduler).toHaveYielded(['A [v1]']);
1485 + assertLog(['A [v1]']);
1486 expect(root).toMatchRenderedOutput('A [v1]');
1487
1488 await act(async () => {
@@ -1552,13 +1490,13 @@ describe('ReactCache', () => {
1490 refresh();
1491 });
1492 });
1555 - expect(Scheduler).toHaveYielded(['Cache miss! [A]']);
1493 + assertLog(['Cache miss! [A]']);
1494 expect(root).toMatchRenderedOutput('A [v1]');
1495
1496 await act(async () => {
1497 root.render('Bye!');
1498 });
1561 - expect(Scheduler).toHaveYielded([
1499 + assertLog([
1500 // TODO: the v1 cache should *not* be cleaned up, it is still retained by the root
1501 // The following line is presently yielded but should not be:
1502 // 'Cache cleanup: A [v1]',
@@ -1588,7 +1526,7 @@ describe('ReactCache', () => {
1526 </Suspense>,
1527 );
1528 });
1591 - expect(Scheduler).toHaveYielded(['A [v1]']);
1529 + assertLog(['A [v1]']);
1530 expect(root).toMatchRenderedOutput('A [v1]');
1531
1532 await act(async () => {
@@ -1596,14 +1534,14 @@ describe('ReactCache', () => {
1534 refresh();
1535 });
1536 });
1599 - expect(Scheduler).toHaveYielded(['Cache miss! [A]']);
1537 + assertLog(['Cache miss! [A]']);
1538 expect(root).toMatchRenderedOutput('A [v1]');
1539
1540 // Unmount the boundary before the refresh can complete
1541 await act(async () => {
1542 root.render('Bye!');
1543 });
1606 - expect(Scheduler).toHaveYielded([
1544 + assertLog([
1545 // TODO: the v2 cache *should* be cleaned up, it was created for the abandoned refresh
1546 // The following line is presently not yielded but should be:
1547 'Cache cleanup: A [v2]',
@@ -1632,14 +1570,14 @@ describe('ReactCache', () => {
1570 await act(async () => {
1571 root.render(<App prerenderMore={false} />);
1572 });
1635 - expect(Scheduler).toHaveYielded([]);
1573 + assertLog([]);
1574 expect(root).toMatchRenderedOutput(<div hidden={true} />);
1575
1576 seedNextTextCache('More');
1577 await act(async () => {
1578 root.render(<App prerenderMore={true} />);
1579 });
1642 - expect(Scheduler).toHaveYielded(['More']);
1580 + assertLog(['More']);
1581 expect(root).toMatchRenderedOutput(<div hidden={true}>More</div>);
1582 });
1583
packages/react-reconciler/src/__tests__/ReactClassSetStateCallback-test.js
+6 -2
@@ -2,6 +2,7 @@ let React;
2 let ReactNoop;
3 let Scheduler;
4 let act;
5 +let assertLog;
6
7 describe('ReactClassSetStateCallback', () => {
8 beforeEach(() => {
@@ -11,6 +12,9 @@ describe('ReactClassSetStateCallback', () => {
12 ReactNoop = require('react-noop-renderer');
13 Scheduler = require('scheduler');
14 act = require('jest-react').act;
15 +
16 + const InternalTestUtils = require('internal-test-utils');
17 + assertLog = InternalTestUtils.assertLog;
18 });
19
20 function Text({text}) {
@@ -32,7 +36,7 @@ describe('ReactClassSetStateCallback', () => {
36 await act(async () => {
37 root.render(<App />);
38 });
35 - expect(Scheduler).toHaveYielded([0]);
39 + assertLog([0]);
40
41 await act(async () => {
42 if (gate(flags => flags.enableUnifiedSyncLane)) {
@@ -52,6 +56,6 @@ describe('ReactClassSetStateCallback', () => {
56 );
57 });
58 });
55 - expect(Scheduler).toHaveYielded([2, 'Callback 2', 2, 'Callback 1']);
59 + assertLog([2, 'Callback 2', 2, 'Callback 1']);
60 });
61 });
packages/react-reconciler/src/__tests__/ReactConcurrentErrorRecovery-test.js
+22 -57
@@ -5,6 +5,7 @@ let act;
5 let Suspense;
6 let getCacheForType;
7 let startTransition;
8 +let assertLog;
9
10 let caches;
11 let seededCache;
@@ -20,6 +21,9 @@ describe('ReactConcurrentErrorRecovery', () => {
21 Suspense = React.Suspense;
22 startTransition = React.startTransition;
23
24 + const InternalTestUtils = require('internal-test-utils');
25 + assertLog = InternalTestUtils.assertLog;
26 +
27 getCacheForType = React.unstable_getCacheForType;
28
29 caches = [];
@@ -196,7 +200,7 @@ describe('ReactConcurrentErrorRecovery', () => {
200 await act(async () => {
201 root.render(<App step={1} />);
202 });
199 - expect(Scheduler).toHaveYielded(['A1', 'B1']);
203 + assertLog(['A1', 'B1']);
204 expect(root).toMatchRenderedOutput('A1B1');
205
206 // Start a refresh transition
@@ -205,12 +209,7 @@ describe('ReactConcurrentErrorRecovery', () => {
209 root.render(<App step={2} />);
210 });
211 });
208 - expect(Scheduler).toHaveYielded([
209 - 'Suspend! [A2]',
210 - 'Loading...',
211 - 'Suspend! [B2]',
212 - 'Loading...',
213 - ]);
212 + assertLog(['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...']);
213 // Because this is a refresh, we don't switch to a fallback
214 expect(root).toMatchRenderedOutput('A1B1');
215
@@ -222,7 +221,7 @@ describe('ReactConcurrentErrorRecovery', () => {
221 // Because we're still suspended on A, we can't show an error boundary. We
222 // should wait for A to resolve.
223 if (gate(flags => flags.replayFailedUnitOfWorkWithInvokeGuardedCallback)) {
225 - expect(Scheduler).toHaveYielded([
224 + assertLog([
225 'Suspend! [A2]',
226 'Loading...',
227
@@ -233,12 +232,7 @@ describe('ReactConcurrentErrorRecovery', () => {
232 'Oops!',
233 ]);
234 } else {
236 - expect(Scheduler).toHaveYielded([
237 - 'Suspend! [A2]',
238 - 'Loading...',
239 - 'Error! [B2]',
240 - 'Oops!',
241 - ]);
235 + assertLog(['Suspend! [A2]', 'Loading...', 'Error! [B2]', 'Oops!']);
236 }
237 // Remain on previous screen.
238 expect(root).toMatchRenderedOutput('A1B1');
@@ -248,7 +242,7 @@ describe('ReactConcurrentErrorRecovery', () => {
242 resolveText('A2');
243 });
244 if (gate(flags => flags.replayFailedUnitOfWorkWithInvokeGuardedCallback)) {
251 - expect(Scheduler).toHaveYielded([
245 + assertLog([
246 'A2',
247 'Error! [B2]',
248 // This extra log happens when we replay the error
@@ -264,15 +258,7 @@ describe('ReactConcurrentErrorRecovery', () => {
258 'Oops!',
259 ]);
260 } else {
267 - expect(Scheduler).toHaveYielded([
268 - 'A2',
269 - 'Error! [B2]',
270 - 'Oops!',
271 -
272 - 'A2',
273 - 'Error! [B2]',
274 - 'Oops!',
275 - ]);
261 + assertLog(['A2', 'Error! [B2]', 'Oops!', 'A2', 'Error! [B2]', 'Oops!']);
262 }
263 // Now we can show the error boundary that's wrapped around B.
264 expect(root).toMatchRenderedOutput('A2Oops!');
@@ -317,7 +303,7 @@ describe('ReactConcurrentErrorRecovery', () => {
303 await act(async () => {
304 root.render(<App step={1} />);
305 });
320 - expect(Scheduler).toHaveYielded(['A1', 'B1']);
306 + assertLog(['A1', 'B1']);
307 expect(root).toMatchRenderedOutput('A1B1');
308
309 // Start a refresh transition
@@ -326,12 +312,7 @@ describe('ReactConcurrentErrorRecovery', () => {
312 root.render(<App step={2} />);
313 });
314 });
329 - expect(Scheduler).toHaveYielded([
330 - 'Suspend! [A2]',
331 - 'Loading...',
332 - 'Suspend! [B2]',
333 - 'Loading...',
334 - ]);
315 + assertLog(['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...']);
316 // Because this is a refresh, we don't switch to a fallback
317 expect(root).toMatchRenderedOutput('A1B1');
318
@@ -343,7 +324,7 @@ describe('ReactConcurrentErrorRecovery', () => {
324 // Because we're still suspended on B, we can't show an error boundary. We
325 // should wait for B to resolve.
326 if (gate(flags => flags.replayFailedUnitOfWorkWithInvokeGuardedCallback)) {
346 - expect(Scheduler).toHaveYielded([
327 + assertLog([
328 'Error! [A2]',
329 // This extra log happens when we replay the error
330 // in invokeGuardedCallback
@@ -354,12 +335,7 @@ describe('ReactConcurrentErrorRecovery', () => {
335 'Loading...',
336 ]);
337 } else {
357 - expect(Scheduler).toHaveYielded([
358 - 'Error! [A2]',
359 - 'Oops!',
360 - 'Suspend! [B2]',
361 - 'Loading...',
362 - ]);
338 + assertLog(['Error! [A2]', 'Oops!', 'Suspend! [B2]', 'Loading...']);
339 }
340 // Remain on previous screen.
341 expect(root).toMatchRenderedOutput('A1B1');
@@ -369,7 +345,7 @@ describe('ReactConcurrentErrorRecovery', () => {
345 resolveText('B2');
346 });
347 if (gate(flags => flags.replayFailedUnitOfWorkWithInvokeGuardedCallback)) {
372 - expect(Scheduler).toHaveYielded([
348 + assertLog([
349 'Error! [A2]',
350 // This extra log happens when we replay the error
351 // in invokeGuardedCallback
@@ -385,15 +361,7 @@ describe('ReactConcurrentErrorRecovery', () => {
361 'B2',
362 ]);
363 } else {
388 - expect(Scheduler).toHaveYielded([
389 - 'Error! [A2]',
390 - 'Oops!',
391 - 'B2',
392 -
393 - 'Error! [A2]',
394 - 'Oops!',
395 - 'B2',
396 - ]);
364 + assertLog(['Error! [A2]', 'Oops!', 'B2', 'Error! [A2]', 'Oops!', 'B2']);
365 }
366 // Now we can show the error boundary that's wrapped around B.
367 expect(root).toMatchRenderedOutput('Oops!B2');
@@ -422,7 +390,7 @@ describe('ReactConcurrentErrorRecovery', () => {
390 root.render(<AsyncText text="Async" />);
391 });
392 });
425 - expect(Scheduler).toHaveYielded(['Suspend! [Async]']);
393 + assertLog(['Suspend! [Async]']);
394 expect(root).toMatchRenderedOutput(null);
395
396 // This also works if the suspended component is wrapped with an error
@@ -438,14 +406,14 @@ describe('ReactConcurrentErrorRecovery', () => {
406 );
407 });
408 });
441 - expect(Scheduler).toHaveYielded(['Suspend! [Async]']);
409 + assertLog(['Suspend! [Async]']);
410 expect(root).toMatchRenderedOutput(null);
411
412 // Continues rendering once data resolves
413 await act(async () => {
414 resolveText('Async');
415 });
448 - expect(Scheduler).toHaveYielded(['Async']);
416 + assertLog(['Async']);
417 expect(root).toMatchRenderedOutput('Async');
418 });
419
@@ -489,7 +457,7 @@ describe('ReactConcurrentErrorRecovery', () => {
457 );
458 });
459 });
492 - expect(Scheduler).toHaveYielded([
460 + assertLog([
461 'Suspend! [Async]',
462 // TODO: Ideally we would skip this second render pass to render the
463 // error UI, since it's not going to commit anyway. The same goes for
@@ -510,17 +478,14 @@ describe('ReactConcurrentErrorRecovery', () => {
478 );
479 });
480 });
513 - expect(Scheduler).toHaveYielded([
514 - 'Suspend! [Async]',
515 - 'Caught an error: Oops!',
516 - ]);
481 + assertLog(['Suspend! [Async]', 'Caught an error: Oops!']);
482 expect(root).toMatchRenderedOutput(null);
483
484 await act(async () => {
485 await resolveText('Async');
486 });
487
523 - expect(Scheduler).toHaveYielded([
488 + assertLog([
489 'Async',
490 'Caught an error: Oops!',
491
packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js
+36 -37
@@ -9,6 +9,7 @@ let SuspenseList;
9 let getCacheForType;
10 let caches;
11 let seededCache;
12 +let assertLog;
13
14 describe('ReactLazyContextPropagation', () => {
15 beforeEach(() => {
@@ -25,6 +26,9 @@ describe('ReactLazyContextPropagation', () => {
26 SuspenseList = React.SuspenseList;
27 }
28
29 + const InternalTestUtils = require('internal-test-utils');
30 + assertLog = InternalTestUtils.assertLog;
31 +
32 getCacheForType = React.unstable_getCacheForType;
33
34 caches = [];
@@ -202,13 +206,13 @@ describe('ReactLazyContextPropagation', () => {
206 await act(async () => {
207 root.render(<App />);
208 });
205 - expect(Scheduler).toHaveYielded([0]);
209 + assertLog([0]);
210 expect(root).toMatchRenderedOutput('0');
211
212 await act(async () => {
213 setValue(1);
214 });
211 - expect(Scheduler).toHaveYielded([1]);
215 + assertLog([1]);
216 expect(root).toMatchRenderedOutput('1');
217 },
218 );
@@ -244,13 +248,13 @@ describe('ReactLazyContextPropagation', () => {
248 await act(async () => {
249 root.render(<App />);
250 });
247 - expect(Scheduler).toHaveYielded([0]);
251 + assertLog([0]);
252 expect(root).toMatchRenderedOutput('0');
253
254 await act(async () => {
255 setValue(1);
256 });
253 - expect(Scheduler).toHaveYielded([1]);
257 + assertLog([1]);
258 expect(root).toMatchRenderedOutput('1');
259 });
260
@@ -287,13 +291,13 @@ describe('ReactLazyContextPropagation', () => {
291 await act(async () => {
292 root.render(<App />);
293 });
290 - expect(Scheduler).toHaveYielded([0]);
294 + assertLog([0]);
295 expect(root).toMatchRenderedOutput('0');
296
297 await act(async () => {
298 setValue(1);
299 });
296 - expect(Scheduler).toHaveYielded([1]);
300 + assertLog([1]);
301 expect(root).toMatchRenderedOutput('1');
302 });
303
@@ -325,7 +329,7 @@ describe('ReactLazyContextPropagation', () => {
329 await act(async () => {
330 root.render(<App />);
331 });
328 - expect(Scheduler).toHaveYielded(['Consumer', 0]);
332 + assertLog(['Consumer', 0]);
333 expect(root).toMatchRenderedOutput('0');
334
335 await act(async () => {
@@ -335,12 +339,7 @@ describe('ReactLazyContextPropagation', () => {
339 setOtherValue(1);
340 setOtherValue(0);
341 });
338 - // NOTE: If this didn't yield anything, that indicates that we never visited
339 - // the consumer during the render phase, which probably means the eager
340 - // bailout mechanism kicked in. Because we're testing the _lazy_ bailout
341 - // mechanism, update this test to foil the _eager_ bailout, somehow. Perhaps
342 - // by switching to useReducer.
343 - expect(Scheduler).toHaveYielded(['Consumer']);
342 + assertLog(['Consumer']);
343 expect(root).toMatchRenderedOutput('0');
344 });
345
@@ -387,7 +386,7 @@ describe('ReactLazyContextPropagation', () => {
386 await act(async () => {
387 root.render(<App />);
388 });
390 - expect(Scheduler).toHaveYielded(['A', 'A']);
389 + assertLog(['A', 'A']);
390 expect(root).toMatchRenderedOutput('AA');
391
392 await act(async () => {
@@ -395,13 +394,13 @@ describe('ReactLazyContextPropagation', () => {
394 // the fallback displays despite this being a refresh.
395 setContext('B');
396 });
398 - expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...', 'B']);
397 + assertLog(['Suspend! [B]', 'Loading...', 'B']);
398 expect(root).toMatchRenderedOutput('Loading...B');
399
400 await act(async () => {
401 await resolveText('B');
402 });
404 - expect(Scheduler).toHaveYielded(['B']);
403 + assertLog(['B']);
404 expect(root).toMatchRenderedOutput('BB');
405 });
406
@@ -467,7 +466,7 @@ describe('ReactLazyContextPropagation', () => {
466 await act(async () => {
467 root.render(<App />);
468 });
470 - expect(Scheduler).toHaveYielded(['A', 'A', 'A']);
469 + assertLog(['A', 'A', 'A']);
470 expect(root).toMatchRenderedOutput('AAA');
471
472 await act(async () => {
@@ -475,13 +474,13 @@ describe('ReactLazyContextPropagation', () => {
474 // the fallback displays despite this being a refresh.
475 setContext('B');
476 });
478 - expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...', 'B']);
477 + assertLog(['Suspend! [B]', 'Loading...', 'B']);
478 expect(root).toMatchRenderedOutput('Loading...B');
479
480 await act(async () => {
481 await resolveText('B');
482 });
484 - expect(Scheduler).toHaveYielded(['B', 'B']);
483 + assertLog(['B', 'B']);
484 expect(root).toMatchRenderedOutput('BBB');
485 });
486
@@ -528,7 +527,7 @@ describe('ReactLazyContextPropagation', () => {
527 await act(async () => {
528 root.render(<App />);
529 });
531 - expect(Scheduler).toHaveYielded(['A', 'A']);
530 + assertLog(['A', 'A']);
531 expect(root).toMatchRenderedOutput('AA');
532
533 await act(async () => {
@@ -536,13 +535,13 @@ describe('ReactLazyContextPropagation', () => {
535 // the fallback displays despite this being a refresh.
536 setContext('B');
537 });
539 - expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...', 'B']);
538 + assertLog(['Suspend! [B]', 'Loading...', 'B']);
539 expect(root).toMatchRenderedOutput('Loading...B');
540
541 await act(async () => {
542 await resolveText('B');
543 });
545 - expect(Scheduler).toHaveYielded(['B']);
544 + assertLog(['B']);
545 expect(root).toMatchRenderedOutput('BB');
546 });
547
@@ -582,13 +581,13 @@ describe('ReactLazyContextPropagation', () => {
581 await act(async () => {
582 root.render(<App />);
583 });
585 - expect(Scheduler).toHaveYielded(['A', 'A']);
584 + assertLog(['A', 'A']);
585 expect(root).toMatchRenderedOutput('AA');
586
587 await act(async () => {
588 setContext('B');
589 });
591 - expect(Scheduler).toHaveYielded(['B', 'B']);
590 + assertLog(['B', 'B']);
591 expect(root).toMatchRenderedOutput('BB');
592 });
593
@@ -643,13 +642,13 @@ describe('ReactLazyContextPropagation', () => {
642 await act(async () => {
643 root.render(<App />);
644 });
646 - expect(Scheduler).toHaveYielded(['A', 'A', 'A']);
645 + assertLog(['A', 'A', 'A']);
646 expect(root).toMatchRenderedOutput('AAA');
647
648 await act(async () => {
649 setContext('B');
650 });
652 - expect(Scheduler).toHaveYielded(['B', 'B', 'B']);
651 + assertLog(['B', 'B', 'B']);
652 expect(root).toMatchRenderedOutput('BBB');
653 });
654
@@ -685,13 +684,13 @@ describe('ReactLazyContextPropagation', () => {
684 await act(async () => {
685 root.render(<App />);
686 });
688 - expect(Scheduler).toHaveYielded(['A', 'A']);
687 + assertLog(['A', 'A']);
688 expect(root).toMatchRenderedOutput('AA');
689
690 await act(async () => {
691 setContext('B');
692 });
694 - expect(Scheduler).toHaveYielded(['B', 'B']);
693 + assertLog(['B', 'B']);
694 expect(root).toMatchRenderedOutput('BB');
695 });
696
@@ -739,13 +738,13 @@ describe('ReactLazyContextPropagation', () => {
738 await act(async () => {
739 root.render(<App />);
740 });
742 - expect(Scheduler).toHaveYielded(['A', 'A']);
741 + assertLog(['A', 'A']);
742 expect(root).toMatchRenderedOutput('AA');
743
744 await act(async () => {
745 setContext('B');
746 });
748 - expect(Scheduler).toHaveYielded(['B', 'B']);
747 + assertLog(['B', 'B']);
748 expect(root).toMatchRenderedOutput('BB');
749 });
750
@@ -802,19 +801,19 @@ describe('ReactLazyContextPropagation', () => {
801 await act(async () => {
802 root.render(<App />);
803 });
805 - expect(Scheduler).toHaveYielded(['A', 'A']);
804 + assertLog(['A', 'A']);
805 expect(root).toMatchRenderedOutput('AA');
806
807 await act(async () => {
808 setContext('B');
809 });
811 - expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...']);
810 + assertLog(['Suspend! [B]', 'Loading...']);
811 expect(root).toMatchRenderedOutput('Loading...');
812
813 await act(async () => {
814 await resolveText('B');
815 });
817 - expect(Scheduler).toHaveYielded(['B', 'B']);
816 + assertLog(['B', 'B']);
817 expect(root).toMatchRenderedOutput('BB');
818 });
819
@@ -863,13 +862,13 @@ describe('ReactLazyContextPropagation', () => {
862 await act(async () => {
863 root.render(<App />);
864 });
866 - expect(Scheduler).toHaveYielded(['A', 'A']);
865 + assertLog(['A', 'A']);
866 expect(root).toMatchRenderedOutput('AA');
867
868 await act(async () => {
869 setContext('B');
870 });
872 - expect(Scheduler).toHaveYielded(['B', 'B']);
871 + assertLog(['B', 'B']);
872 expect(root).toMatchRenderedOutput('BB');
873 });
874
@@ -912,13 +911,13 @@ describe('ReactLazyContextPropagation', () => {
911 await act(async () => {
912 root.render(<App />);
913 });
915 - expect(Scheduler).toHaveYielded(['A', 'A']);
914 + assertLog(['A', 'A']);
915 expect(root).toMatchRenderedOutput('AA');
916
917 await act(async () => {
918 setContext('B');
919 });
921 - expect(Scheduler).toHaveYielded(['B', 'B']);
920 + assertLog(['B', 'B']);
921 expect(root).toMatchRenderedOutput('BB');
922 });
923 });
packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js
+22 -25
@@ -15,6 +15,8 @@ let startTransition;
15 let useDeferredValue;
16 let useMemo;
17 let useState;
18 +let assertLog;
19 +let waitForPaint;
20
21 describe('ReactDeferredValue', () => {
22 beforeEach(() => {
@@ -28,6 +30,10 @@ describe('ReactDeferredValue', () => {
30 useDeferredValue = React.useDeferredValue;
31 useMemo = React.useMemo;
32 useState = React.useState;
33 +
34 + const InternalTestUtils = require('internal-test-utils');
35 + assertLog = InternalTestUtils.assertLog;
36 + waitForPaint = InternalTestUtils.waitForPaint;
37 });
38
39 function Text({text}) {
@@ -65,15 +71,14 @@ describe('ReactDeferredValue', () => {
71 await act(async () => {
72 root.render(<App value={1} />);
73 });
68 - expect(Scheduler).toHaveYielded(['Original: 1', 'Deferred: 1']);
74 + assertLog(['Original: 1', 'Deferred: 1']);
75
76 // If it's an urgent update, the value is deferred
77 await act(async () => {
78 root.render(<App value={2} />);
79
74 - expect(Scheduler).toFlushUntilNextPaint(['Original: 2']);
75 - // The deferred value updates in a separate render
76 - expect(Scheduler).toFlushUntilNextPaint(['Deferred: 2']);
80 + await waitForPaint(['Original: 2']);
81 + await waitForPaint(['Deferred: 2']);
82 });
83 expect(root).toMatchRenderedOutput(
84 <div>
@@ -87,8 +92,7 @@ describe('ReactDeferredValue', () => {
92 startTransition(() => {
93 root.render(<App value={3} />);
94 });
90 - // The deferred value updates in the same render as the original
91 - expect(Scheduler).toFlushUntilNextPaint(['Original: 3', 'Deferred: 3']);
95 + await waitForPaint(['Original: 3', 'Deferred: 3']);
96 });
97 expect(root).toMatchRenderedOutput(
98 <div>
@@ -126,15 +130,14 @@ describe('ReactDeferredValue', () => {
130 await act(async () => {
131 root.render(<App value={1} />);
132 });
129 - expect(Scheduler).toHaveYielded(['Original: 1', 'Deferred: 1']);
133 + assertLog(['Original: 1', 'Deferred: 1']);
134
135 // If it's an urgent update, the value is deferred
136 await act(async () => {
137 root.render(<App value={2} />);
138
135 - expect(Scheduler).toFlushUntilNextPaint(['Original: 2']);
136 - // The deferred value updates in a separate render
137 - expect(Scheduler).toFlushUntilNextPaint(['Deferred: 2']);
139 + await waitForPaint(['Original: 2']);
140 + await waitForPaint(['Deferred: 2']);
141 });
142 expect(root).toMatchRenderedOutput(
143 <div>
@@ -148,8 +151,7 @@ describe('ReactDeferredValue', () => {
151 startTransition(() => {
152 root.render(<App value={3} />);
153 });
151 - // The deferred value updates in the same render as the original
152 - expect(Scheduler).toFlushUntilNextPaint(['Original: 3', 'Deferred: 3']);
154 + await waitForPaint(['Original: 3', 'Deferred: 3']);
155 });
156 expect(root).toMatchRenderedOutput(
157 <div>
@@ -192,15 +194,14 @@ describe('ReactDeferredValue', () => {
194 await act(async () => {
195 root.render(<App value={1} />);
196 });
195 - expect(Scheduler).toHaveYielded(['Original: 1', 'Deferred: 1']);
197 + assertLog(['Original: 1', 'Deferred: 1']);
198
199 // If it's an urgent update, the value is deferred
200 await act(async () => {
201 root.render(<App value={2} />);
202
201 - expect(Scheduler).toFlushUntilNextPaint(['Original: 2']);
202 - // The deferred value updates in a separate render
203 - expect(Scheduler).toFlushUntilNextPaint(['Deferred: 2']);
203 + await waitForPaint(['Original: 2']);
204 + await waitForPaint(['Deferred: 2']);
205 });
206 expect(root).toMatchRenderedOutput(
207 <div>
@@ -214,8 +215,7 @@ describe('ReactDeferredValue', () => {
215 startTransition(() => {
216 root.render(<App value={3} />);
217 });
217 - // The deferred value updates in the same render as the original
218 - expect(Scheduler).toFlushUntilNextPaint(['Original: 3', 'Deferred: 3']);
218 + await waitForPaint(['Original: 3', 'Deferred: 3']);
219 });
220 expect(root).toMatchRenderedOutput(
221 <div>
@@ -257,7 +257,7 @@ describe('ReactDeferredValue', () => {
257 // Initial render
258 await act(async () => {
259 root.render(<App value={1} />);
260 - expect(Scheduler).toFlushUntilNextPaint(['Original: 1', 'Deferred: 1']);
260 + await waitForPaint(['Original: 1', 'Deferred: 1']);
261 expect(root).toMatchRenderedOutput(
262 <div>
263 <div>Original: 1</div>
@@ -270,7 +270,7 @@ describe('ReactDeferredValue', () => {
270 startTransition(() => {
271 root.render(<App value={2} />);
272 });
273 - expect(Scheduler).toFlushUntilNextPaint(['Original: 2', 'Deferred: 2']);
273 + await waitForPaint(['Original: 2', 'Deferred: 2']);
274 expect(root).toMatchRenderedOutput(
275 <div>
276 <div>Original: 2</div>
@@ -281,17 +281,14 @@ describe('ReactDeferredValue', () => {
281
282 await act(async () => {
283 root.render(<App value={3} />);
284 - // In the regression, the memoized value was not updated during non-urgent
285 - // updates, so this would flip the deferred value back to the initial
286 - // value (1) instead of reusing the current one (2).
287 - expect(Scheduler).toFlushUntilNextPaint(['Original: 3']);
284 + await waitForPaint(['Original: 3']);
285 expect(root).toMatchRenderedOutput(
286 <div>
287 <div>Original: 3</div>
288 <div>Deferred: 2</div>
289 </div>,
290 );
294 - expect(Scheduler).toFlushUntilNextPaint(['Deferred: 3']);
291 + await waitForPaint(['Deferred: 3']);
292 expect(root).toMatchRenderedOutput(
293 <div>
294 <div>Original: 3</div>
packages/react-reconciler/src/__tests__/ReactDisableSchedulerTimeoutBasedOnReactExpirationTime-test.internal.js
+7 -7
@@ -5,6 +5,7 @@ let Scheduler;
5 let Suspense;
6 let scheduleCallback;
7 let NormalPriority;
8 +let waitForAll;
9
10 describe('ReactSuspenseList', () => {
11 beforeEach(() => {
@@ -20,6 +21,9 @@ describe('ReactSuspenseList', () => {
21
22 scheduleCallback = Scheduler.unstable_scheduleCallback;
23 NormalPriority = Scheduler.unstable_NormalPriority;
24 +
25 + const InternalTestUtils = require('internal-test-utils');
26 + waitForAll = InternalTestUtils.waitForAll;
27 });
28
29 function Text(props) {
@@ -61,16 +65,12 @@ describe('ReactSuspenseList', () => {
65 const root = ReactNoop.createRoot(null);
66
67 root.render(<App show={false} />);
64 - expect(Scheduler).toFlushAndYield([]);
68 + await waitForAll([]);
69
70 React.startTransition(() => {
71 root.render(<App show={true} />);
72 });
69 - expect(Scheduler).toFlushAndYield([
70 - 'Suspend! [A]',
71 - 'Suspend! [B]',
72 - 'Loading...',
73 - ]);
73 + await waitForAll(['Suspend! [A]', 'Suspend! [B]', 'Loading...']);
74 expect(root).toMatchRenderedOutput(null);
75
76 Scheduler.unstable_advanceTime(2000);
@@ -92,7 +92,7 @@ describe('ReactSuspenseList', () => {
92 // task should not jump the queue ahead of B.
93 await expect(Scheduler).toFlushAndYieldThrough(['Resolve B']);
94
95 - expect(Scheduler).toFlushAndYield(['A', 'B']);
95 + await waitForAll(['A', 'B']);
96 expect(root).toMatchRenderedOutput('AB');
97 });
98 });
packages/react-reconciler/src/__tests__/ReactEffectOrdering-test.js
+7 -3
@@ -18,8 +18,9 @@ let Scheduler;
18 let act;
19 let useEffect;
20 let useLayoutEffect;
21 +let assertLog;
22
22 -describe('ReactHooksWithNoopRenderer', () => {
23 +describe('ReactEffectOrdering', () => {
24 beforeEach(() => {
25 jest.resetModules();
26 jest.useFakeTimers();
@@ -30,6 +31,9 @@ describe('ReactHooksWithNoopRenderer', () => {
31 act = require('jest-react').act;
32 useEffect = React.useEffect;
33 useLayoutEffect = React.useLayoutEffect;
34 +
35 + const InternalTestUtils = require('internal-test-utils');
36 + assertLog = InternalTestUtils.assertLog;
37 });
38
39 test('layout unmounts on deletion are fired in parent -> child order', async () => {
@@ -56,7 +60,7 @@ describe('ReactHooksWithNoopRenderer', () => {
60 await act(async () => {
61 root.render(null);
62 });
59 - expect(Scheduler).toHaveYielded(['Unmount parent', 'Unmount child']);
63 + assertLog(['Unmount parent', 'Unmount child']);
64 });
65
66 test('passive unmounts on deletion are fired in parent -> child order', async () => {
@@ -83,6 +87,6 @@ describe('ReactHooksWithNoopRenderer', () => {
87 await act(async () => {
88 root.render(null);
89 });
86 - expect(Scheduler).toHaveYielded(['Unmount parent', 'Unmount child']);
90 + assertLog(['Unmount parent', 'Unmount child']);
91 });
92 });
packages/react-reconciler/src/__tests__/ReactExpiration-test.js
+67 -86
@@ -18,6 +18,9 @@ let resolveText;
18 let startTransition;
19 let useState;
20 let useEffect;
21 +let assertLog;
22 +let waitFor;
23 +let waitForAll;
24
25 describe('ReactExpiration', () => {
26 beforeEach(() => {
@@ -31,6 +34,11 @@ describe('ReactExpiration', () => {
34 useState = React.useState;
35 useEffect = React.useEffect;
36
37 + const InternalTestUtils = require('internal-test-utils');
38 + assertLog = InternalTestUtils.assertLog;
39 + waitFor = InternalTestUtils.waitFor;
40 + waitForAll = InternalTestUtils.waitForAll;
41 +
42 const textCache = new Map();
43
44 readText = text => {
@@ -136,7 +144,7 @@ describe('ReactExpiration', () => {
144 expect(ReactNoop).toMatchRenderedOutput(<span prop="done" />);
145 });
146
139 - it('two updates of like priority in the same event always flush within the same batch', () => {
147 + it('two updates of like priority in the same event always flush within the same batch', async () => {
148 class TextClass extends React.Component {
149 componentDidMount() {
150 Scheduler.unstable_yieldValue(`${this.props.text} [commit]`);
@@ -163,37 +171,32 @@ describe('ReactExpiration', () => {
171 });
172 // Advance the timer.
173 Scheduler.unstable_advanceTime(2000);
166 - // Partially flush the first update, then interrupt it.
167 - expect(Scheduler).toFlushAndYieldThrough(['A [render]']);
174 + await waitFor(['A [render]']);
175 interrupt();
176
170 - // Don't advance time by enough to expire the first update.
171 - expect(Scheduler).toHaveYielded([]);
177 + assertLog([]);
178 expect(ReactNoop).toMatchRenderedOutput(null);
179
180 // Schedule another update.
181 ReactNoop.render(<TextClass text="B" />);
176 - // Both updates are batched
177 - expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
182 + await waitForAll(['B [render]', 'B [commit]']);
183 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
184
185 // Now do the same thing again, except this time don't flush any work in
186 // between the two updates.
187 ReactNoop.render(<TextClass text="A" />);
188 Scheduler.unstable_advanceTime(2000);
184 - expect(Scheduler).toHaveYielded([]);
189 + assertLog([]);
190 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
191 // Schedule another update.
192 ReactNoop.render(<TextClass text="B" />);
188 - // The updates should flush in the same batch, since as far as the scheduler
189 - // knows, they may have occurred inside the same event.
190 - expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
193 + await waitForAll(['B [render]', 'B [commit]']);
194 });
195
196 it(
197 'two updates of like priority in the same event always flush within the ' +
198 "same batch, even if there's a sync update in between",
196 - () => {
199 + async () => {
200 class TextClass extends React.Component {
201 componentDidMount() {
202 Scheduler.unstable_yieldValue(`${this.props.text} [commit]`);
@@ -220,25 +223,22 @@ describe('ReactExpiration', () => {
223 });
224 // Advance the timer.
225 Scheduler.unstable_advanceTime(2000);
223 - // Partially flush the first update, then interrupt it.
224 - expect(Scheduler).toFlushAndYieldThrough(['A [render]']);
226 + await waitFor(['A [render]']);
227 interrupt();
228
227 - // Don't advance time by enough to expire the first update.
228 - expect(Scheduler).toHaveYielded([]);
229 + assertLog([]);
230 expect(ReactNoop).toMatchRenderedOutput(null);
231
232 // Schedule another update.
233 ReactNoop.render(<TextClass text="B" />);
233 - // Both updates are batched
234 - expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
234 + await waitForAll(['B [render]', 'B [commit]']);
235 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
236
237 // Now do the same thing again, except this time don't flush any work in
238 // between the two updates.
239 ReactNoop.render(<TextClass text="A" />);
240 Scheduler.unstable_advanceTime(2000);
241 - expect(Scheduler).toHaveYielded([]);
241 + assertLog([]);
242 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
243
244 // Perform some synchronous work. The scheduler must assume we're inside
@@ -247,13 +247,11 @@ describe('ReactExpiration', () => {
247
248 // Schedule another update.
249 ReactNoop.render(<TextClass text="B" />);
250 - // The updates should flush in the same batch, since as far as the scheduler
251 - // knows, they may have occurred inside the same event.
252 - expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
250 + await waitForAll(['B [render]', 'B [commit]']);
251 },
252 );
253
256 - it('cannot update at the same expiration time that is already rendering', () => {
254 + it('cannot update at the same expiration time that is already rendering', async () => {
255 const store = {text: 'initial'};
256 const subscribers = [];
257 class Connected extends React.Component {
@@ -292,7 +290,7 @@ describe('ReactExpiration', () => {
290 React.startTransition(() => {
291 ReactNoop.render(<App />);
292 });
295 - expect(Scheduler).toFlushAndYield([
293 + await waitForAll([
294 'initial [A] [render]',
295 'initial [B] [render]',
296 'initial [C] [render]',
@@ -307,10 +305,7 @@ describe('ReactExpiration', () => {
305 React.startTransition(() => {
306 subscribers.forEach(s => s.setState({text: '1'}));
307 });
310 - expect(Scheduler).toFlushAndYieldThrough([
311 - '1 [A] [render]',
312 - '1 [B] [render]',
313 - ]);
308 + await waitFor(['1 [A] [render]', '1 [B] [render]']);
309
310 // Before the update can finish, update again. Even though no time has
311 // advanced, this update should be given a different expiration time than
@@ -318,13 +313,10 @@ describe('ReactExpiration', () => {
313 React.startTransition(() => {
314 subscribers.forEach(s => s.setState({text: '2'}));
315 });
321 - expect(Scheduler).toFlushAndYieldThrough([
322 - '1 [C] [render]',
323 - '1 [D] [render]',
324 - ]);
316 + await waitFor(['1 [C] [render]', '1 [D] [render]']);
317 });
318
327 - it('stops yielding if CPU-bound update takes too long to finish', () => {
319 + it('stops yielding if CPU-bound update takes too long to finish', async () => {
320 const root = ReactNoop.createRoot();
321 function App() {
322 return (
@@ -342,18 +334,18 @@ describe('ReactExpiration', () => {
334 root.render(<App />);
335 });
336
345 - expect(Scheduler).toFlushAndYieldThrough(['A']);
346 - expect(Scheduler).toFlushAndYieldThrough(['B']);
347 - expect(Scheduler).toFlushAndYieldThrough(['C']);
337 + await waitFor(['A']);
338 + await waitFor(['B']);
339 + await waitFor(['C']);
340
341 Scheduler.unstable_advanceTime(10000);
342
343 flushNextRenderIfExpired();
352 - expect(Scheduler).toHaveYielded(['D', 'E']);
344 + assertLog(['D', 'E']);
345 expect(root).toMatchRenderedOutput('ABCDE');
346 });
347
356 - it('root expiration is measured from the time of the first update', () => {
348 + it('root expiration is measured from the time of the first update', async () => {
349 Scheduler.unstable_advanceTime(10000);
350
351 const root = ReactNoop.createRoot();
@@ -372,14 +364,14 @@ describe('ReactExpiration', () => {
364 root.render(<App />);
365 });
366
375 - expect(Scheduler).toFlushAndYieldThrough(['A']);
376 - expect(Scheduler).toFlushAndYieldThrough(['B']);
377 - expect(Scheduler).toFlushAndYieldThrough(['C']);
367 + await waitFor(['A']);
368 + await waitFor(['B']);
369 + await waitFor(['C']);
370
371 Scheduler.unstable_advanceTime(10000);
372
373 flushNextRenderIfExpired();
382 - expect(Scheduler).toHaveYielded(['D', 'E']);
374 + assertLog(['D', 'E']);
375 expect(root).toMatchRenderedOutput('ABCDE');
376 });
377
@@ -404,14 +396,14 @@ describe('ReactExpiration', () => {
396
397 // The update should not have expired yet.
398 flushNextRenderIfExpired();
407 - expect(Scheduler).toHaveYielded([]);
399 + assertLog([]);
400
401 expect(ReactNoop).toMatchRenderedOutput(null);
402
403 // Advance the time some more to expire the update.
404 Scheduler.unstable_advanceTime(10000);
405 flushNextRenderIfExpired();
414 - expect(Scheduler).toHaveYielded([]);
406 + assertLog([]);
407 expect(ReactNoop).toMatchRenderedOutput('Hi');
408 });
409
@@ -427,14 +419,14 @@ describe('ReactExpiration', () => {
419 ReactNoop.render('Hi');
420 });
421 flushNextRenderIfExpired();
430 - expect(Scheduler).toHaveYielded([]);
422 + assertLog([]);
423 expect(ReactNoop).toMatchRenderedOutput(null);
424
425 // Advancing by ~5 seconds should be sufficient to expire the update. (I
426 // used a slightly larger number to allow for possible rounding.)
427 Scheduler.unstable_advanceTime(6000);
428 flushNextRenderIfExpired();
437 - expect(Scheduler).toHaveYielded([]);
429 + assertLog([]);
430 expect(ReactNoop).toMatchRenderedOutput('Hi');
431 });
432
@@ -463,7 +455,7 @@ describe('ReactExpiration', () => {
455 await act(async () => {
456 root.render(<App />);
457 });
466 - expect(Scheduler).toHaveYielded(['Sync pri: 0', 'Normal pri: 0']);
458 + assertLog(['Sync pri: 0', 'Normal pri: 0']);
459 expect(root).toMatchRenderedOutput('Sync pri: 0, Normal pri: 0');
460
461 // First demonstrate what happens when there's no starvation
@@ -471,18 +463,16 @@ describe('ReactExpiration', () => {
463 React.startTransition(() => {
464 updateNormalPri();
465 });
474 - expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
466 + await waitFor(['Sync pri: 0']);
467 updateSyncPri();
476 - expect(Scheduler).toHaveYielded(['Sync pri: 1', 'Normal pri: 0']);
468 + assertLog(['Sync pri: 1', 'Normal pri: 0']);
469
470 // The remaining work hasn't expired, so the render phase is time sliced.
471 // In other words, we can flush just the first child without flushing
472 // the rest.
473 Scheduler.unstable_flushNumberOfYields(1);
482 - // Yield right after first child.
483 - expect(Scheduler).toHaveYielded(['Sync pri: 1']);
484 - // Now do the rest.
485 - expect(Scheduler).toFlushAndYield(['Normal pri: 1']);
474 + assertLog(['Sync pri: 1']);
475 + await waitForAll(['Normal pri: 1']);
476 });
477 expect(root).toMatchRenderedOutput('Sync pri: 1, Normal pri: 1');
478
@@ -491,7 +481,7 @@ describe('ReactExpiration', () => {
481 React.startTransition(() => {
482 updateNormalPri();
483 });
494 - expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
484 + await waitFor(['Sync pri: 1']);
485
486 // This time, a lot of time has elapsed since the normal pri update
487 // started rendering. (This should advance time by some number that's
@@ -500,12 +490,12 @@ describe('ReactExpiration', () => {
490 Scheduler.unstable_advanceTime(10000);
491
492 updateSyncPri();
503 - expect(Scheduler).toHaveYielded(['Sync pri: 2', 'Normal pri: 1']);
493 + assertLog(['Sync pri: 2', 'Normal pri: 1']);
494
495 // The remaining work _has_ expired, so the render phase is _not_ time
496 // sliced. Attempting to flush just the first child also flushes the rest.
497 Scheduler.unstable_flushNumberOfYields(1);
508 - expect(Scheduler).toHaveYielded(['Sync pri: 2', 'Normal pri: 2']);
498 + assertLog(['Sync pri: 2', 'Normal pri: 2']);
499 });
500 expect(root).toMatchRenderedOutput('Sync pri: 2, Normal pri: 2');
501 });
@@ -534,16 +524,16 @@ describe('ReactExpiration', () => {
524 await act(async () => {
525 root.render(<App />);
526 });
537 - expect(Scheduler).toHaveYielded(['Sync pri: 0', 'Idle pri: 0']);
527 + assertLog(['Sync pri: 0', 'Idle pri: 0']);
528 expect(root).toMatchRenderedOutput('Sync pri: 0, Idle pri: 0');
529
530 // First demonstrate what happens when there's no starvation
531 await act(async () => {
532 updateIdlePri();
543 - expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
533 + await waitFor(['Sync pri: 0']);
534 updateSyncPri();
535 });
546 - expect(Scheduler).toHaveYielded([
536 + assertLog([
537 // Interrupt idle update to render sync update
538 'Sync pri: 1',
539 'Idle pri: 0',
@@ -556,7 +546,7 @@ describe('ReactExpiration', () => {
546 // Do the same thing, but starve the first update
547 await act(async () => {
548 updateIdlePri();
559 - expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
549 + await waitFor(['Sync pri: 1']);
550
551 // Advance a ridiculously large amount of time to demonstrate that the
552 // idle work never expires
@@ -564,8 +554,7 @@ describe('ReactExpiration', () => {
554
555 updateSyncPri();
556 });
567 - // Same thing should happen as last time
568 - expect(Scheduler).toHaveYielded([
557 + assertLog([
558 // Interrupt idle update to render sync update
559 'Sync pri: 2',
560 'Idle pri: 1',
@@ -597,14 +586,14 @@ describe('ReactExpiration', () => {
586 await act(async () => {
587 root.render(<App />);
588 });
600 - expect(Scheduler).toHaveYielded(['A0', 'B0', 'C']);
589 + assertLog(['A0', 'B0', 'C']);
590 expect(root).toMatchRenderedOutput('A0B0C');
591
592 await act(async () => {
593 startTransition(() => {
594 setA(1);
595 });
607 - expect(Scheduler).toFlushAndYieldThrough(['A1']);
596 + await waitFor(['A1']);
597 startTransition(() => {
598 setB(1);
599 });
@@ -614,12 +603,12 @@ describe('ReactExpiration', () => {
603 // (entangled), we should be able to finish the in-progress transition
604 // without also including the next one.
605 Scheduler.unstable_flushNumberOfYields(1);
617 - expect(Scheduler).toHaveYielded(['B0', 'C']);
606 + assertLog(['B0', 'C']);
607 expect(root).toMatchRenderedOutput('A1B0C');
608
609 // The next transition also finishes without yielding.
610 Scheduler.unstable_flushNumberOfYields(1);
622 - expect(Scheduler).toHaveYielded(['A1', 'B1', 'C']);
611 + assertLog(['A1', 'B1', 'C']);
612 expect(root).toMatchRenderedOutput('A1B1C');
613 });
614 });
@@ -642,28 +631,21 @@ describe('ReactExpiration', () => {
631 await resolveText('A0');
632 root.render(<App step={0} />);
633 });
645 - expect(Scheduler).toHaveYielded(['A0', 'B', 'C']);
634 + assertLog(['A0', 'B', 'C']);
635 expect(root).toMatchRenderedOutput('A0BC');
636
637 await act(async () => {
638 React.startTransition(() => {
639 root.render(<App step={1} />);
640 });
652 - expect(Scheduler).toFlushAndYield([
653 - 'Suspend! [A1]',
654 - 'B',
655 - 'C',
656 - 'Loading...',
657 - ]);
641 + await waitForAll(['Suspend! [A1]', 'B', 'C', 'Loading...']);
642
643 // Lots of time elapses before the promise resolves
644 Scheduler.unstable_advanceTime(10000);
645 await resolveText('A1');
662 - expect(Scheduler).toHaveYielded(['Promise resolved [A1]']);
646 + assertLog(['Promise resolved [A1]']);
647
664 - // But the update doesn't expire, because it was IO bound. So we can
665 - // partially rendering without finishing.
666 - expect(Scheduler).toFlushAndYieldThrough(['A1']);
648 + await waitFor(['A1']);
649 expect(root).toMatchRenderedOutput('A0BC');
650
651 // Lots more time elapses. We're CPU-bound now, so we should treat this
@@ -672,7 +654,7 @@ describe('ReactExpiration', () => {
654
655 // The rest of the update finishes without yielding.
656 Scheduler.unstable_flushNumberOfYields(1);
675 - expect(Scheduler).toHaveYielded(['B', 'C']);
657 + assertLog(['B', 'C']);
658 });
659 });
660
@@ -696,13 +678,13 @@ describe('ReactExpiration', () => {
678 await act(async () => {
679 root.render(<App />);
680 });
699 - expect(Scheduler).toHaveYielded(['A0', 'B0']);
681 + assertLog(['A0', 'B0']);
682
683 await act(async () => {
684 startTransition(() => {
685 setA(1);
686 });
705 - expect(Scheduler).toFlushAndYieldThrough(['A1']);
687 + await waitFor(['A1']);
688
689 // Expire the in-progress update
690 Scheduler.unstable_advanceTime(10000);
@@ -710,12 +692,12 @@ describe('ReactExpiration', () => {
692 ReactNoop.flushSync(() => {
693 setB(1);
694 });
713 - expect(Scheduler).toHaveYielded(['A0', 'B1']);
695 + assertLog(['A0', 'B1']);
696
697 // Now flush the original update. Because it expired, it should finish
698 // without yielding.
699 Scheduler.unstable_flushNumberOfYields(1);
718 - expect(Scheduler).toHaveYielded(['A1', 'B1']);
700 + assertLog(['A1', 'B1']);
701 });
702 });
703
@@ -737,7 +719,7 @@ describe('ReactExpiration', () => {
719 await act(async () => {
720 root.render(<App step={0} />);
721 });
740 - expect(Scheduler).toHaveYielded(['A0', 'B0', 'C0', 'Effect: 0']);
722 + assertLog(['A0', 'B0', 'C0', 'Effect: 0']);
723 expect(root).toMatchRenderedOutput('A0B0C0');
724
725 await act(async () => {
@@ -749,9 +731,8 @@ describe('ReactExpiration', () => {
731
732 // The update finishes without yielding. But it does not flush the effect.
733 Scheduler.unstable_flushNumberOfYields(1);
752 - expect(Scheduler).toHaveYielded(['A1', 'B1', 'C1']);
734 + assertLog(['A1', 'B1', 'C1']);
735 });
754 - // The effect flushes after paint.
755 - expect(Scheduler).toHaveYielded(['Effect: 1']);
736 + assertLog(['Effect: 1']);
737 });
738 });
packages/react-reconciler/src/__tests__/ReactFlushSync-test.js
+37 -31
@@ -5,6 +5,8 @@ let act;
5 let useState;
6 let useEffect;
7 let startTransition;
8 +let assertLog;
9 +let waitForPaint;
10
11 // TODO: Migrate tests to React DOM instead of React Noop
12
@@ -19,6 +21,10 @@ describe('ReactFlushSync', () => {
21 useState = React.useState;
22 useEffect = React.useEffect;
23 startTransition = React.startTransition;
24 +
25 + const InternalTestUtils = require('internal-test-utils');
26 + assertLog = InternalTestUtils.assertLog;
27 + waitForPaint = InternalTestUtils.waitForPaint;
28 });
29
30 function Text({text}) {
@@ -27,6 +33,8 @@ describe('ReactFlushSync', () => {
33 }
34
35 test('changes priority of updates in useEffect', async () => {
36 + spyOnDev(console, 'error').mockImplementation(() => {});
37 +
38 function App() {
39 const [syncState, setSyncState] = useState(0);
40 const [state, setState] = useState(0);
@@ -44,29 +52,33 @@ describe('ReactFlushSync', () => {
52 React.startTransition(() => {
53 root.render(<App />);
54 });
47 - // This will yield right before the passive effect fires
48 - expect(Scheduler).toFlushUntilNextPaint(['0, 0']);
55 + await waitForPaint(['0, 0']);
56
57 // The passive effect will schedule a sync update and a normal update.
58 // They should commit in two separate batches. First the sync one.
52 - expect(() => {
53 - expect(Scheduler).toFlushUntilNextPaint(
54 - gate(flags => flags.enableUnifiedSyncLane) ? ['1, 1'] : ['1, 0'],
55 - );
56 - }).toErrorDev('flushSync was called from inside a lifecycle method');
59 + await waitForPaint(
60 + gate(flags => flags.enableUnifiedSyncLane) ? ['1, 1'] : ['1, 0'],
61 + );
62
63 // The remaining update is not sync
64 ReactNoop.flushSync();
60 - expect(Scheduler).toHaveYielded([]);
65 + assertLog([]);
66
67 if (gate(flags => flags.enableUnifiedSyncLane)) {
63 - expect(Scheduler).toFlushUntilNextPaint([]);
68 + await waitForPaint([]);
69 } else {
65 - // Now flush it.
66 - expect(Scheduler).toFlushUntilNextPaint(['1, 1']);
70 + await waitForPaint(['1, 1']);
71 }
72 });
73 expect(root).toMatchRenderedOutput('1, 1');
74 +
75 + if (__DEV__) {
76 + expect(console.error.mock.calls[0][0]).toContain(
77 + 'flushSync was called from inside a lifecycle method. React ' +
78 + 'cannot flush when React is already rendering. Consider moving this ' +
79 + 'call to a scheduler task or micro task.%s',
80 + );
81 + }
82 });
83
84 test('nested with startTransition', async () => {
@@ -84,7 +96,7 @@ describe('ReactFlushSync', () => {
96 await act(async () => {
97 root.render(<App />);
98 });
87 - expect(Scheduler).toHaveYielded(['0, 0']);
99 + assertLog(['0, 0']);
100 expect(root).toMatchRenderedOutput('0, 0');
101
102 await act(async () => {
@@ -100,12 +112,10 @@ describe('ReactFlushSync', () => {
112 });
113 });
114 });
103 - // Only the sync update should have flushed
104 - expect(Scheduler).toHaveYielded(['1, 0']);
115 + assertLog(['1, 0']);
116 expect(root).toMatchRenderedOutput('1, 0');
117 });
107 - // Now the async update has flushed, too.
108 - expect(Scheduler).toHaveYielded(['1, 1']);
118 + assertLog(['1, 1']);
119 expect(root).toMatchRenderedOutput('1, 1');
120 });
121
@@ -122,7 +132,7 @@ describe('ReactFlushSync', () => {
132 ReactNoop.flushSync(() => {
133 root.render(<App />);
134 });
125 - expect(Scheduler).toHaveYielded([
135 + assertLog([
136 'Child',
137 // Because the pending effect was the result of a sync update, calling
138 // flushSync should flush it.
@@ -145,15 +155,14 @@ describe('ReactFlushSync', () => {
155 ReactNoop.flushSync(() => {
156 root.render(<App />);
157 });
148 - expect(Scheduler).toHaveYielded([
158 + assertLog([
159 'Child',
160 // Because we're in legacy mode, we shouldn't have flushed the passive
161 // effects yet.
162 ]);
163 expect(root).toMatchRenderedOutput('Child');
164 });
155 - // Effect flushes after paint.
156 - expect(Scheduler).toHaveYielded(['Effect']);
165 + assertLog(['Effect']);
166 });
167
168 test('flush pending passive effects before scope is called in legacy mode', async () => {
@@ -172,7 +181,7 @@ describe('ReactFlushSync', () => {
181 ReactNoop.flushSync(() => {
182 root.render(<App step={1} />);
183 });
175 - expect(Scheduler).toHaveYielded([
184 + assertLog([
185 1,
186 // Because we're in legacy mode, we shouldn't have flushed the passive
187 // effects yet.
@@ -184,10 +193,10 @@ describe('ReactFlushSync', () => {
193 // fired, before the scope function is called.
194 root.render(<App step={currentStep + 1} />);
195 });
187 - expect(Scheduler).toHaveYielded(['Effect: 1', 2]);
196 + assertLog(['Effect: 1', 2]);
197 expect(root).toMatchRenderedOutput('2');
198 });
190 - expect(Scheduler).toHaveYielded(['Effect: 2']);
199 + assertLog(['Effect: 2']);
200 });
201
202 test("do not flush passive effects synchronously when they aren't the result of a sync render", async () => {
@@ -201,15 +210,14 @@ describe('ReactFlushSync', () => {
210 const root = ReactNoop.createRoot();
211 await act(async () => {
212 root.render(<App />);
204 - expect(Scheduler).toFlushUntilNextPaint([
213 + await waitForPaint([
214 'Child',
215 // Because the passive effect was not the result of a sync update, it
216 // should not flush before paint.
217 ]);
218 expect(root).toMatchRenderedOutput('Child');
219 });
211 - // Effect flushes after paint.
212 - expect(Scheduler).toHaveYielded(['Effect']);
220 + assertLog(['Effect']);
221 });
222
223 test('does not flush pending passive effects', async () => {
@@ -223,15 +231,13 @@ describe('ReactFlushSync', () => {
231 const root = ReactNoop.createRoot();
232 await act(async () => {
233 root.render(<App />);
226 - expect(Scheduler).toFlushUntilNextPaint(['Child']);
234 + await waitForPaint(['Child']);
235 expect(root).toMatchRenderedOutput('Child');
236
237 // Passive effects are pending. Calling flushSync should not affect them.
238 ReactNoop.flushSync();
231 - // Effects still haven't fired.
232 - expect(Scheduler).toHaveYielded([]);
239 + assertLog([]);
240 });
234 - // Now the effects have fired.
235 - expect(Scheduler).toHaveYielded(['Effect']);
241 + assertLog(['Effect']);
242 });
243 });
packages/react-reconciler/src/__tests__/ReactFragment-test.js
+86 -84
@@ -12,6 +12,7 @@
12 let React;
13 let ReactNoop;
14 let Scheduler;
15 +let waitForAll;
16
17 describe('ReactFragment', () => {
18 beforeEach(function () {
@@ -20,9 +21,12 @@ describe('ReactFragment', () => {
21 React = require('react');
22 ReactNoop = require('react-noop-renderer');
23 Scheduler = require('scheduler');
24 +
25 + const InternalTestUtils = require('internal-test-utils');
26 + waitForAll = InternalTestUtils.waitForAll;
27 });
28
25 - it('should render a single child via noop renderer', () => {
29 + it('should render a single child via noop renderer', async () => {
30 const element = (
31 <>
32 <span>foo</span>
@@ -30,21 +34,21 @@ describe('ReactFragment', () => {
34 );
35
36 ReactNoop.render(element);
33 - expect(Scheduler).toFlushWithoutYielding();
37 + await waitForAll([]);
38
39 expect(ReactNoop).toMatchRenderedOutput(<span>foo</span>);
40 });
41
38 - it('should render zero children via noop renderer', () => {
42 + it('should render zero children via noop renderer', async () => {
43 const element = <React.Fragment />;
44
45 ReactNoop.render(element);
42 - expect(Scheduler).toFlushWithoutYielding();
46 + await waitForAll([]);
47
48 expect(ReactNoop).toMatchRenderedOutput(null);
49 });
50
47 - it('should render multiple children via noop renderer', () => {
51 + it('should render multiple children via noop renderer', async () => {
52 const element = (
53 <>
54 hello <span>world</span>
@@ -52,7 +56,7 @@ describe('ReactFragment', () => {
56 );
57
58 ReactNoop.render(element);
55 - expect(Scheduler).toFlushWithoutYielding();
59 + await waitForAll([]);
60
61 expect(ReactNoop).toMatchRenderedOutput(
62 <>
@@ -61,13 +65,13 @@ describe('ReactFragment', () => {
65 );
66 });
67
64 - it('should render an iterable via noop renderer', () => {
68 + it('should render an iterable via noop renderer', async () => {
69 const element = (
70 <>{new Set([<span key="a">hi</span>, <span key="b">bye</span>])}</>
71 );
72
73 ReactNoop.render(element);
70 - expect(Scheduler).toFlushWithoutYielding();
74 + await waitForAll([]);
75
76 expect(ReactNoop).toMatchRenderedOutput(
77 <>
@@ -77,7 +81,7 @@ describe('ReactFragment', () => {
81 );
82 });
83
80 - it('should preserve state of children with 1 level nesting', function () {
84 + it('should preserve state of children with 1 level nesting', async function () {
85 const ops = [];
86
87 class Stateful extends React.Component {
@@ -102,10 +106,10 @@ describe('ReactFragment', () => {
106 }
107
108 ReactNoop.render(<Foo condition={true} />);
105 - expect(Scheduler).toFlushWithoutYielding();
109 + await waitForAll([]);
110
111 ReactNoop.render(<Foo condition={false} />);
108 - expect(Scheduler).toFlushWithoutYielding();
112 + await waitForAll([]);
113
114 expect(ops).toEqual(['Update Stateful']);
115 expect(ReactNoop).toMatchRenderedOutput(
@@ -116,13 +120,13 @@ describe('ReactFragment', () => {
120 );
121
122 ReactNoop.render(<Foo condition={true} />);
119 - expect(Scheduler).toFlushWithoutYielding();
123 + await waitForAll([]);
124
125 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
126 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
127 });
128
125 - it('should preserve state between top-level fragments', function () {
129 + it('should preserve state between top-level fragments', async function () {
130 const ops = [];
131
132 class Stateful extends React.Component {
@@ -148,22 +152,22 @@ describe('ReactFragment', () => {
152 }
153
154 ReactNoop.render(<Foo condition={true} />);
151 - expect(Scheduler).toFlushWithoutYielding();
155 + await waitForAll([]);
156
157 ReactNoop.render(<Foo condition={false} />);
154 - expect(Scheduler).toFlushWithoutYielding();
158 + await waitForAll([]);
159
160 expect(ops).toEqual(['Update Stateful']);
161 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
162
163 ReactNoop.render(<Foo condition={true} />);
160 - expect(Scheduler).toFlushWithoutYielding();
164 + await waitForAll([]);
165
166 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
167 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
168 });
169
166 - it('should preserve state of children nested at same level', function () {
170 + it('should preserve state of children nested at same level', async function () {
171 const ops = [];
172
173 class Stateful extends React.Component {
@@ -198,10 +202,10 @@ describe('ReactFragment', () => {
202 }
203
204 ReactNoop.render(<Foo condition={true} />);
201 - expect(Scheduler).toFlushWithoutYielding();
205 + await waitForAll([]);
206
207 ReactNoop.render(<Foo condition={false} />);
204 - expect(Scheduler).toFlushWithoutYielding();
208 + await waitForAll([]);
209
210 expect(ops).toEqual(['Update Stateful']);
211 expect(ReactNoop).toMatchRenderedOutput(
@@ -212,13 +216,13 @@ describe('ReactFragment', () => {
216 );
217
218 ReactNoop.render(<Foo condition={true} />);
215 - expect(Scheduler).toFlushWithoutYielding();
219 + await waitForAll([]);
220
221 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
222 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
223 });
224
221 - it('should not preserve state in non-top-level fragment nesting', function () {
225 + it('should not preserve state in non-top-level fragment nesting', async function () {
226 const ops = [];
227
228 class Stateful extends React.Component {
@@ -246,22 +250,22 @@ describe('ReactFragment', () => {
250 }
251
252 ReactNoop.render(<Foo condition={true} />);
249 - expect(Scheduler).toFlushWithoutYielding();
253 + await waitForAll([]);
254
255 ReactNoop.render(<Foo condition={false} />);
252 - expect(Scheduler).toFlushWithoutYielding();
256 + await waitForAll([]);
257
258 expect(ops).toEqual([]);
259 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
260
261 ReactNoop.render(<Foo condition={true} />);
258 - expect(Scheduler).toFlushWithoutYielding();
262 + await waitForAll([]);
263
264 expect(ops).toEqual([]);
265 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
266 });
267
264 - it('should not preserve state of children if nested 2 levels without siblings', function () {
268 + it('should not preserve state of children if nested 2 levels without siblings', async function () {
269 const ops = [];
270
271 class Stateful extends React.Component {
@@ -287,22 +291,22 @@ describe('ReactFragment', () => {
291 }
292
293 ReactNoop.render(<Foo condition={true} />);
290 - expect(Scheduler).toFlushWithoutYielding();
294 + await waitForAll([]);
295
296 ReactNoop.render(<Foo condition={false} />);
293 - expect(Scheduler).toFlushWithoutYielding();
297 + await waitForAll([]);
298
299 expect(ops).toEqual([]);
300 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
301
302 ReactNoop.render(<Foo condition={true} />);
299 - expect(Scheduler).toFlushWithoutYielding();
303 + await waitForAll([]);
304
305 expect(ops).toEqual([]);
306 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
307 });
308
305 - it('should not preserve state of children if nested 2 levels with siblings', function () {
309 + it('should not preserve state of children if nested 2 levels with siblings', async function () {
310 const ops = [];
311
312 class Stateful extends React.Component {
@@ -329,10 +333,10 @@ describe('ReactFragment', () => {
333 }
334
335 ReactNoop.render(<Foo condition={true} />);
332 - expect(Scheduler).toFlushWithoutYielding();
336 + await waitForAll([]);
337
338 ReactNoop.render(<Foo condition={false} />);
335 - expect(Scheduler).toFlushWithoutYielding();
339 + await waitForAll([]);
340
341 expect(ops).toEqual([]);
342 expect(ReactNoop).toMatchRenderedOutput(
@@ -343,13 +347,13 @@ describe('ReactFragment', () => {
347 );
348
349 ReactNoop.render(<Foo condition={true} />);
346 - expect(Scheduler).toFlushWithoutYielding();
350 + await waitForAll([]);
351
352 expect(ops).toEqual([]);
353 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
354 });
355
352 - it('should preserve state between array nested in fragment and fragment', function () {
356 + it('should preserve state between array nested in fragment and fragment', async function () {
357 const ops = [];
358
359 class Stateful extends React.Component {
@@ -373,22 +377,22 @@ describe('ReactFragment', () => {
377 }
378
379 ReactNoop.render(<Foo condition={true} />);
376 - expect(Scheduler).toFlushWithoutYielding();
380 + await waitForAll([]);
381
382 ReactNoop.render(<Foo condition={false} />);
379 - expect(Scheduler).toFlushWithoutYielding();
383 + await waitForAll([]);
384
385 expect(ops).toEqual(['Update Stateful']);
386 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
387
388 ReactNoop.render(<Foo condition={true} />);
385 - expect(Scheduler).toFlushWithoutYielding();
389 + await waitForAll([]);
390
391 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
392 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
393 });
394
391 - it('should preserve state between top level fragment and array', function () {
395 + it('should preserve state between top level fragment and array', async function () {
396 const ops = [];
397
398 class Stateful extends React.Component {
@@ -412,22 +416,22 @@ describe('ReactFragment', () => {
416 }
417
418 ReactNoop.render(<Foo condition={true} />);
415 - expect(Scheduler).toFlushWithoutYielding();
419 + await waitForAll([]);
420
421 ReactNoop.render(<Foo condition={false} />);
418 - expect(Scheduler).toFlushWithoutYielding();
422 + await waitForAll([]);
423
424 expect(ops).toEqual(['Update Stateful']);
425 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
426
427 ReactNoop.render(<Foo condition={true} />);
424 - expect(Scheduler).toFlushWithoutYielding();
428 + await waitForAll([]);
429
430 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
431 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
432 });
433
430 - it('should not preserve state between array nested in fragment and double nested fragment', function () {
434 + it('should not preserve state between array nested in fragment and double nested fragment', async function () {
435 const ops = [];
436
437 class Stateful extends React.Component {
@@ -453,22 +457,22 @@ describe('ReactFragment', () => {
457 }
458
459 ReactNoop.render(<Foo condition={true} />);
456 - expect(Scheduler).toFlushWithoutYielding();
460 + await waitForAll([]);
461
462 ReactNoop.render(<Foo condition={false} />);
459 - expect(Scheduler).toFlushWithoutYielding();
463 + await waitForAll([]);
464
465 expect(ops).toEqual([]);
466 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
467
468 ReactNoop.render(<Foo condition={true} />);
465 - expect(Scheduler).toFlushWithoutYielding();
469 + await waitForAll([]);
470
471 expect(ops).toEqual([]);
472 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
473 });
474
471 - it('should not preserve state between array nested in fragment and double nested array', function () {
475 + it('should not preserve state between array nested in fragment and double nested array', async function () {
476 const ops = [];
477
478 class Stateful extends React.Component {
@@ -490,22 +494,22 @@ describe('ReactFragment', () => {
494 }
495
496 ReactNoop.render(<Foo condition={true} />);
493 - expect(Scheduler).toFlushWithoutYielding();
497 + await waitForAll([]);
498
499 ReactNoop.render(<Foo condition={false} />);
496 - expect(Scheduler).toFlushWithoutYielding();
500 + await waitForAll([]);
501
502 expect(ops).toEqual([]);
503 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
504
505 ReactNoop.render(<Foo condition={true} />);
502 - expect(Scheduler).toFlushWithoutYielding();
506 + await waitForAll([]);
507
508 expect(ops).toEqual([]);
509 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
510 });
511
508 - it('should preserve state between double nested fragment and double nested array', function () {
512 + it('should preserve state between double nested fragment and double nested array', async function () {
513 const ops = [];
514
515 class Stateful extends React.Component {
@@ -531,22 +535,22 @@ describe('ReactFragment', () => {
535 }
536
537 ReactNoop.render(<Foo condition={true} />);
534 - expect(Scheduler).toFlushWithoutYielding();
538 + await waitForAll([]);
539
540 ReactNoop.render(<Foo condition={false} />);
537 - expect(Scheduler).toFlushWithoutYielding();
541 + await waitForAll([]);
542
543 expect(ops).toEqual(['Update Stateful']);
544 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
545
546 ReactNoop.render(<Foo condition={true} />);
543 - expect(Scheduler).toFlushWithoutYielding();
547 + await waitForAll([]);
548
549 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
550 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
551 });
552
549 - it('should not preserve state of children when the keys are different', function () {
553 + it('should not preserve state of children when the keys are different', async function () {
554 const ops = [];
555
556 class Stateful extends React.Component {
@@ -573,10 +577,10 @@ describe('ReactFragment', () => {
577 }
578
579 ReactNoop.render(<Foo condition={true} />);
576 - expect(Scheduler).toFlushWithoutYielding();
580 + await waitForAll([]);
581
582 ReactNoop.render(<Foo condition={false} />);
579 - expect(Scheduler).toFlushWithoutYielding();
583 + await waitForAll([]);
584
585 expect(ops).toEqual([]);
586 expect(ReactNoop).toMatchRenderedOutput(
@@ -587,13 +591,13 @@ describe('ReactFragment', () => {
591 );
592
593 ReactNoop.render(<Foo condition={true} />);
590 - expect(Scheduler).toFlushWithoutYielding();
594 + await waitForAll([]);
595
596 expect(ops).toEqual([]);
597 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
598 });
599
596 - it('should not preserve state between unkeyed and keyed fragment', function () {
600 + it('should not preserve state between unkeyed and keyed fragment', async function () {
601 const ops = [];
602
603 class Stateful extends React.Component {
@@ -619,22 +623,22 @@ describe('ReactFragment', () => {
623 }
624
625 ReactNoop.render(<Foo condition={true} />);
622 - expect(Scheduler).toFlushWithoutYielding();
626 + await waitForAll([]);
627
628 ReactNoop.render(<Foo condition={false} />);
625 - expect(Scheduler).toFlushWithoutYielding();
629 + await waitForAll([]);
630
631 expect(ops).toEqual([]);
632 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
633
634 ReactNoop.render(<Foo condition={true} />);
631 - expect(Scheduler).toFlushWithoutYielding();
635 + await waitForAll([]);
636
637 expect(ops).toEqual([]);
638 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
639 });
640
637 - it('should preserve state with reordering in multiple levels', function () {
641 + it('should preserve state with reordering in multiple levels', async function () {
642 const ops = [];
643
644 class Stateful extends React.Component {
@@ -672,10 +676,10 @@ describe('ReactFragment', () => {
676 }
677
678 ReactNoop.render(<Foo condition={true} />);
675 - expect(Scheduler).toFlushWithoutYielding();
679 + await waitForAll([]);
680
681 ReactNoop.render(<Foo condition={false} />);
678 - expect(Scheduler).toFlushWithoutYielding();
682 + await waitForAll([]);
683
684 expect(ops).toEqual(['Update Stateful']);
685 expect(ReactNoop).toMatchRenderedOutput(
@@ -689,7 +693,7 @@ describe('ReactFragment', () => {
693 );
694
695 ReactNoop.render(<Foo condition={true} />);
692 - expect(Scheduler).toFlushWithoutYielding();
696 + await waitForAll([]);
697
698 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
699 expect(ReactNoop).toMatchRenderedOutput(
@@ -703,7 +707,7 @@ describe('ReactFragment', () => {
707 );
708 });
709
706 - it('should not preserve state when switching to a keyed fragment to an array', function () {
710 + it('should not preserve state when switching to a keyed fragment to an array', async function () {
711 const ops = [];
712
713 class Stateful extends React.Component {
@@ -735,7 +739,7 @@ describe('ReactFragment', () => {
739 }
740
741 ReactNoop.render(<Foo condition={true} />);
738 - expect(Scheduler).toFlushWithoutYielding();
742 + await waitForAll([]);
743
744 ReactNoop.render(<Foo condition={false} />);
745 expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
@@ -751,7 +755,7 @@ describe('ReactFragment', () => {
755 );
756
757 ReactNoop.render(<Foo condition={true} />);
754 - expect(Scheduler).toFlushWithoutYielding();
758 + await waitForAll([]);
759
760 expect(ops).toEqual([]);
761 expect(ReactNoop).toMatchRenderedOutput(
@@ -762,7 +766,7 @@ describe('ReactFragment', () => {
766 );
767 });
768
765 - it('should not preserve state when switching a nested unkeyed fragment to a passthrough component', function () {
769 + it('should not preserve state when switching a nested unkeyed fragment to a passthrough component', async function () {
770 const ops = [];
771
772 function Passthrough({children}) {
@@ -796,22 +800,22 @@ describe('ReactFragment', () => {
800 }
801
802 ReactNoop.render(<Foo condition={true} />);
799 - expect(Scheduler).toFlushWithoutYielding();
803 + await waitForAll([]);
804
805 ReactNoop.render(<Foo condition={false} />);
802 - expect(Scheduler).toFlushWithoutYielding();
806 + await waitForAll([]);
807
808 expect(ops).toEqual([]);
809 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
810
811 ReactNoop.render(<Foo condition={true} />);
808 - expect(Scheduler).toFlushWithoutYielding();
812 + await waitForAll([]);
813
814 expect(ops).toEqual([]);
815 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
816 });
817
814 - it('should not preserve state when switching a nested keyed fragment to a passthrough component', function () {
818 + it('should not preserve state when switching a nested keyed fragment to a passthrough component', async function () {
819 const ops = [];
820
821 function Passthrough({children}) {
@@ -845,22 +849,22 @@ describe('ReactFragment', () => {
849 }
850
851 ReactNoop.render(<Foo condition={true} />);
848 - expect(Scheduler).toFlushWithoutYielding();
852 + await waitForAll([]);
853
854 ReactNoop.render(<Foo condition={false} />);
851 - expect(Scheduler).toFlushWithoutYielding();
855 + await waitForAll([]);
856
857 expect(ops).toEqual([]);
858 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
859
860 ReactNoop.render(<Foo condition={true} />);
857 - expect(Scheduler).toFlushWithoutYielding();
861 + await waitForAll([]);
862
863 expect(ops).toEqual([]);
864 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
865 });
866
863 - it('should not preserve state when switching a nested keyed array to a passthrough component', function () {
867 + it('should not preserve state when switching a nested keyed array to a passthrough component', async function () {
868 const ops = [];
869
870 function Passthrough({children}) {
@@ -890,22 +894,22 @@ describe('ReactFragment', () => {
894 }
895
896 ReactNoop.render(<Foo condition={true} />);
893 - expect(Scheduler).toFlushWithoutYielding();
897 + await waitForAll([]);
898
899 ReactNoop.render(<Foo condition={false} />);
896 - expect(Scheduler).toFlushWithoutYielding();
900 + await waitForAll([]);
901
902 expect(ops).toEqual([]);
903 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
904
905 ReactNoop.render(<Foo condition={true} />);
902 - expect(Scheduler).toFlushWithoutYielding();
906 + await waitForAll([]);
907
908 expect(ops).toEqual([]);
909 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
910 });
911
908 - it('should preserve state when it does not change positions', function () {
912 + it('should preserve state when it does not change positions', async function () {
913 const ops = [];
914
915 class Stateful extends React.Component {
@@ -940,8 +944,7 @@ describe('ReactFragment', () => {
944 );
945
946 ReactNoop.render(<Foo condition={false} />);
943 - // The key warning gets deduped because it's in the same component.
944 - expect(Scheduler).toFlushWithoutYielding();
947 + await waitForAll([]);
948
949 expect(ops).toEqual(['Update Stateful']);
950 expect(ReactNoop).toMatchRenderedOutput(
@@ -952,8 +955,7 @@ describe('ReactFragment', () => {
955 );
956
957 ReactNoop.render(<Foo condition={true} />);
955 - // The key warning gets deduped because it's in the same component.
956 - expect(Scheduler).toFlushWithoutYielding();
958 + await waitForAll([]);
959
960 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
961 expect(ReactNoop).toMatchRenderedOutput(
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
+224 -332
@@ -280,13 +280,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
280
281 // Render empty shell.
282 ReactNoop.render(<Foo />);
283 - expect(Scheduler).toFlushAndYield(['Foo']);
283 + await waitForAll(['Foo']);
284
285 // The update will suspend.
286 React.startTransition(() => {
287 ReactNoop.render(<Foo renderBar={true} />);
288 });
289 - expect(Scheduler).toFlushAndYield([
289 + await waitForAll([
290 'Foo',
291 'Bar',
292 // A suspends
@@ -299,8 +299,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
299
300 // Resolve the data
301 await resolveText('A');
302 - // Renders successfully
303 - expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'A', 'B']);
302 + await waitForAll(['Foo', 'Bar', 'A', 'B']);
303 expect(ReactNoop).toMatchRenderedOutput(
304 <>
305 <span prop="A" />
@@ -322,7 +321,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
321 </Suspense>
322 </Fragment>,
323 );
325 - expect(Scheduler).toFlushAndYield([
324 + await waitForAll([
325 'Suspend! [A]',
326 'Loading A...',
327 'Suspend! [B]',
@@ -339,7 +338,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
338 // normal view. The second Suspense should still show the placeholder.
339 await resolveText('A');
340
342 - expect(Scheduler).toFlushAndYield(['A']);
341 + await waitForAll(['A']);
342 expect(ReactNoop).toMatchRenderedOutput(
343 <>
344 <span prop="A" />
@@ -351,7 +350,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
350 // normal view.
351 await resolveText('B');
352
354 - expect(Scheduler).toFlushAndYield(['B']);
353 + await waitForAll(['B']);
354 expect(ReactNoop).toMatchRenderedOutput(
355 <>
356 <span prop="A" />
@@ -364,7 +363,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
363 it('continues rendering siblings after suspending', async () => {
364 // A shell is needed. The update cause it to suspend.
365 ReactNoop.render(<Suspense fallback={<Text text="Loading..." />} />);
367 - expect(Scheduler).toFlushAndYield([]);
366 + await waitForAll([]);
367 // B suspends. Continue rendering the remaining siblings.
368 React.startTransition(() => {
369 ReactNoop.render(
@@ -376,21 +375,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
375 </Suspense>,
376 );
377 });
379 - // B suspends. Continue rendering the remaining siblings.
380 - expect(Scheduler).toFlushAndYield([
381 - 'A',
382 - 'Suspend! [B]',
383 - 'C',
384 - 'D',
385 - 'Loading...',
386 - ]);
378 + await waitForAll(['A', 'Suspend! [B]', 'C', 'D', 'Loading...']);
379 // Did not commit yet.
380 expect(ReactNoop).toMatchRenderedOutput(null);
381
382 // Wait for data to resolve
383 await resolveText('B');
392 - // Renders successfully
393 - expect(Scheduler).toFlushAndYield(['A', 'B', 'C', 'D']);
384 + await waitForAll(['A', 'B', 'C', 'D']);
385 expect(ReactNoop).toMatchRenderedOutput(
386 <>
387 <span prop="A" />
@@ -436,18 +427,18 @@ describe('ReactSuspenseWithNoopRenderer', () => {
427 }
428
429 ReactNoop.render(<App />);
439 - expect(Scheduler).toFlushAndYield([]);
430 + await waitForAll([]);
431 expect(ReactNoop).toMatchRenderedOutput(null);
432
433 React.startTransition(() => {
434 ReactNoop.render(<App renderContent={true} />);
435 });
445 - expect(Scheduler).toFlushAndYield(['Suspend! [Result]', 'Loading...']);
436 + await waitForAll(['Suspend! [Result]', 'Loading...']);
437 expect(ReactNoop).toMatchRenderedOutput(null);
438
439 await rejectText('Result', new Error('Failed to load: Result'));
440
450 - expect(Scheduler).toFlushAndYield([
441 + await waitForAll([
442 'Error! [Result]',
443
444 // React retries one more time
@@ -494,12 +485,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
485 }
486
487 ReactNoop.render(<App />);
497 - expect(Scheduler).toFlushAndYield(['Suspend! [Result]', 'Loading...']);
488 + await waitForAll(['Suspend! [Result]', 'Loading...']);
489 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
490
491 await rejectText('Result', new Error('Failed to load: Result'));
492
502 - expect(Scheduler).toFlushAndYield([
493 + await waitForAll([
494 'Error! [Result]',
495
496 // React retries one more time
@@ -526,9 +517,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
517
518 // Initial mount
519 ReactNoop.render(<App highPri="A" lowPri="1" />);
529 - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [1]', 'Loading...']);
520 + await waitForAll(['A', 'Suspend! [1]', 'Loading...']);
521 await resolveText('1');
531 - expect(Scheduler).toFlushAndYield(['A', '1']);
522 + await waitForAll(['A', '1']);
523 expect(ReactNoop).toMatchRenderedOutput(
524 <>
525 <span prop="A" />
@@ -538,7 +529,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
529
530 // Update the low-pri text
531 ReactNoop.render(<App highPri="A" lowPri="2" />);
541 - expect(Scheduler).toFlushAndYield([
532 + await waitForAll([
533 'A',
534 // Suspends
535 'Suspend! [2]',
@@ -583,13 +574,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
574 }
575
576 ReactNoop.render(<App showA={false} showB={false} />);
586 - expect(Scheduler).toFlushAndYield([]);
577 + await waitForAll([]);
578 expect(ReactNoop).toMatchRenderedOutput(null);
579
580 React.startTransition(() => {
581 ReactNoop.render(<App showA={true} showB={false} />);
582 });
592 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
583 + await waitForAll(['Suspend! [A]', 'Loading...']);
584 expect(ReactNoop).toMatchRenderedOutput(null);
585
586 // Advance React's virtual time by enough to fall into a new async bucket,
@@ -598,11 +589,11 @@ describe('ReactSuspenseWithNoopRenderer', () => {
589 React.startTransition(() => {
590 ReactNoop.render(<App showA={true} showB={true} />);
591 });
601 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'B', 'Loading...']);
592 + await waitForAll(['Suspend! [A]', 'B', 'Loading...']);
593 expect(ReactNoop).toMatchRenderedOutput(null);
594
595 await resolveText('A');
605 - expect(Scheduler).toFlushAndYield(['A', 'B']);
596 + await waitForAll(['A', 'B']);
597 expect(ReactNoop).toMatchRenderedOutput(
598 <>
599 <span prop="A" />
@@ -632,7 +623,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
623 ReactNoop.render(<App hide={true} />);
624 });
625
635 - expect(Scheduler).toFlushAndYield([
626 + await waitForAll([
627 // The first update suspends
628 'Suspend! [Async]',
629 // but we have another pending update that we can work on
@@ -675,7 +666,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
666 root.render(<App step={0} shouldSuspend={false} />);
667 });
668 await advanceTimers(1000);
678 - expect(Scheduler).toHaveYielded(['Sibling', 'Step 0']);
669 + assertLog(['Sibling', 'Step 0']);
670
671 // Schedule an update at several distinct expiration times
672 await act(async () => {
@@ -683,27 +674,27 @@ describe('ReactSuspenseWithNoopRenderer', () => {
674 root.render(<App step={1} shouldSuspend={true} />);
675 });
676 Scheduler.unstable_advanceTime(1000);
686 - expect(Scheduler).toFlushAndYieldThrough(['Sibling']);
677 + await waitFor(['Sibling']);
678 interrupt();
679
680 React.startTransition(() => {
681 root.render(<App step={2} shouldSuspend={true} />);
682 });
683 Scheduler.unstable_advanceTime(1000);
693 - expect(Scheduler).toFlushAndYieldThrough(['Sibling']);
684 + await waitFor(['Sibling']);
685 interrupt();
686
687 React.startTransition(() => {
688 root.render(<App step={3} shouldSuspend={true} />);
689 });
690 Scheduler.unstable_advanceTime(1000);
700 - expect(Scheduler).toFlushAndYieldThrough(['Sibling']);
691 + await waitFor(['Sibling']);
692 interrupt();
693
694 root.render(<App step={4} shouldSuspend={false} />);
695 });
696
706 - expect(Scheduler).toHaveYielded(['Sibling', 'Step 4']);
697 + assertLog(['Sibling', 'Step 4']);
698 });
699
700 // @gate enableLegacyCache
@@ -713,7 +704,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
704 <Suspense fallback={<Text text="Loading..." />} />
705 </Fragment>,
706 );
716 - expect(Scheduler).toFlushAndYield([]);
707 + await waitForAll([]);
708
709 ReactNoop.render(
710 <Fragment>
@@ -724,7 +715,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
715 </Fragment>,
716 );
717
727 - expect(Scheduler).toFlushAndYield([
718 + await waitForAll([
719 // The async child suspends
720 'Suspend! [Async]',
721 // Render the placeholder
@@ -739,9 +730,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
730 // the update.
731 ReactNoop.expire(10000);
732 await advanceTimers(10000);
742 - // No additional rendering work is required, since we already prepared
743 - // the placeholder.
744 - expect(Scheduler).toHaveYielded([]);
733 + assertLog([]);
734 // Should have committed the placeholder.
735 expect(ReactNoop).toMatchRenderedOutput(
736 <>
@@ -752,7 +741,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
741
742 // Once the promise resolves, we render the suspended view
743 await resolveText('Async');
755 - expect(Scheduler).toFlushAndYield(['Async']);
744 + await waitForAll(['Async']);
745 expect(ReactNoop).toMatchRenderedOutput(
746 <>
747 <span prop="Async" />
@@ -778,7 +767,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
767 </Fragment>,
768 );
769
781 - expect(Scheduler).toFlushAndYield([
770 + await waitForAll([
771 'Sync',
772 // The async content suspends
773 'Suspend! [Outer content]',
@@ -796,7 +785,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
785
786 // Resolve the outer promise.
787 await resolveText('Outer content');
799 - expect(Scheduler).toFlushAndYield([
788 + await waitForAll([
789 'Outer content',
790 'Suspend! [Inner content]',
791 'Loading inner...',
@@ -824,7 +813,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
813
814 // Finally, flush the inner promise. We should see the complete screen.
815 await resolveText('Inner content');
827 - expect(Scheduler).toFlushAndYield(['Inner content']);
816 + await waitForAll(['Inner content']);
817 expect(ReactNoop).toMatchRenderedOutput(
818 <>
819 <span prop="Sync" />
@@ -848,7 +837,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
837 </Fragment>,
838 ),
839 );
851 - expect(Scheduler).toHaveYielded([
840 + assertLog([
841 // The async child suspends
842 'Suspend! [Async]',
843 // We immediately render the fallback UI
@@ -866,7 +855,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
855
856 // Once the promise resolves, we render the suspended view
857 await resolveText('Async');
869 - expect(Scheduler).toFlushAndYield(['Async']);
858 + await waitForAll(['Async']);
859 expect(ReactNoop).toMatchRenderedOutput(
860 <>
861 <span prop="Async" />
@@ -889,7 +878,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
878 </Fragment>,
879 ),
880 );
892 - expect(Scheduler).toHaveYielded([
881 + assertLog([
882 'Suspend! [Async]',
883 'Suspend! [Loading (inner)...]',
884 'Sync',
@@ -906,7 +895,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
895 <Suspense fallback={<Text text="Loading..." />} />
896 </Fragment>,
897 );
909 - expect(Scheduler).toFlushAndYield([]);
898 + await waitForAll([]);
899
900 ReactNoop.render(
901 <Fragment>
@@ -917,7 +906,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
906 </Fragment>,
907 );
908
920 - expect(Scheduler).toFlushAndYield([
909 + await waitForAll([
910 // The async child suspends
911 'Suspend! [Async]',
912 'Loading...',
@@ -932,7 +921,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
921 // expiration time.
922 ReactNoop.expire(2000);
923 await advanceTimers(2000);
935 - expect(Scheduler).toFlushWithoutYielding();
924 + await waitForAll([]);
925 expect(ReactNoop).toMatchRenderedOutput(
926 <>
927 <span prop="Loading..." />
@@ -942,7 +931,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
931
932 // Once the promise resolves, we render the suspended view
933 await resolveText('Async');
945 - expect(Scheduler).toFlushAndYield(['Async']);
934 + await waitForAll(['Async']);
935 expect(ReactNoop).toMatchRenderedOutput(
936 <>
937 <span prop="Async" />
@@ -958,7 +947,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
947 <Suspense fallback={<Text text="Loading..." />} />
948 </Fragment>,
949 );
961 - expect(Scheduler).toFlushAndYield([]);
950 + await waitForAll([]);
951
952 React.startTransition(() => {
953 ReactNoop.render(
@@ -971,7 +960,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
960 );
961 });
962
974 - expect(Scheduler).toFlushAndYield([
963 + await waitForAll([
964 // The async child suspends
965 'Suspend! [Async]',
966 'Loading...',
@@ -987,13 +976,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
976 await advanceTimers(2000);
977 expect(ReactNoop).toMatchRenderedOutput(null);
978
990 - // Even flushing won't yield a fallback in a transition.
991 - expect(Scheduler).toFlushWithoutYielding();
979 + await waitForAll([]);
980 expect(ReactNoop).toMatchRenderedOutput(null);
981
982 // Once the promise resolves, we render the suspended view
983 await resolveText('Async');
996 - expect(Scheduler).toFlushAndYield(['Async', 'Sync']);
984 + await waitForAll(['Async', 'Sync']);
985 expect(ReactNoop).toMatchRenderedOutput(
986 <>
987 <span prop="Async" />
@@ -1010,7 +998,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
998 <Suspense fallback={<Text text="Loading..." />} />
999 </>,
1000 );
1013 - expect(Scheduler).toFlushAndYield([]);
1001 + await waitForAll([]);
1002 expect(root).toMatchRenderedOutput(null);
1003 React.startTransition(() => {
1004 root.render(
@@ -1022,13 +1010,11 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1010 </>,
1011 );
1012 });
1025 - expect(Scheduler).toFlushAndYieldThrough(['Suspend! [Async]', 'Sibling']);
1013 + await waitFor(['Suspend! [Async]', 'Sibling']);
1014
1015 await resolveText('Async');
1016
1029 - // Because we're already showing a fallback, interrupt the current render
1030 - // and restart immediately.
1031 - expect(Scheduler).toFlushAndYield(['Async', 'Sibling']);
1017 + await waitForAll(['Async', 'Sibling']);
1018 expect(root).toMatchRenderedOutput(
1019 <>
1020 <span prop="Async" />
@@ -1063,17 +1049,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1049 <AsyncText text="B" />
1050 </Suspense>,
1051 );
1066 - expect(Scheduler).toFlushAndYield([
1067 - 'Suspend! [A]',
1068 - 'Suspend! [B]',
1069 - 'Loading...',
1070 - ]);
1052 + await waitForAll(['Suspend! [A]', 'Suspend! [B]', 'Loading...']);
1053 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1054
1055 await resolveText('A');
1056 await resolveText('B');
1057
1076 - expect(Scheduler).toFlushAndYield(['A', 'B']);
1058 + await waitForAll(['A', 'B']);
1059 expect(ReactNoop).toMatchRenderedOutput(
1060 <>
1061 <span prop="A" />
@@ -1085,7 +1067,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1067 // @gate enableLegacyCache
1068 it('can resume rendering earlier than a timeout', async () => {
1069 ReactNoop.render(<Suspense fallback={<Text text="Loading..." />} />);
1088 - expect(Scheduler).toFlushAndYield([]);
1070 + await waitForAll([]);
1071
1072 React.startTransition(() => {
1073 ReactNoop.render(
@@ -1094,13 +1076,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1076 </Suspense>,
1077 );
1078 });
1097 - expect(Scheduler).toFlushAndYield(['Suspend! [Async]', 'Loading...']);
1079 + await waitForAll(['Suspend! [Async]', 'Loading...']);
1080 expect(ReactNoop).toMatchRenderedOutput(null);
1081
1082 // Resolve the promise
1083 await resolveText('Async');
1102 - // We can now resume rendering
1103 - expect(Scheduler).toFlushAndYield(['Async']);
1084 + await waitForAll(['Async']);
1085 expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
1086 });
1087
@@ -1121,37 +1102,34 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1102 // First mount without suspending. This ensures we already have content
1103 // showing so that subsequent updates will suspend.
1104 ReactNoop.render(<App text="S" />);
1124 - expect(Scheduler).toFlushAndYield(['S']);
1105 + await waitForAll(['S']);
1106
1107 // Schedule an update, and suspend for up to 5 seconds.
1108 React.startTransition(() => ReactNoop.render(<App text="A" />));
1128 - // The update should suspend.
1129 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1109 + await waitForAll(['Suspend! [A]', 'Loading...']);
1110 expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1111
1112 // Advance time until right before it expires.
1113 await advanceTimers(4999);
1114 ReactNoop.expire(4999);
1135 - expect(Scheduler).toFlushWithoutYielding();
1115 + await waitForAll([]);
1116 expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1117
1118 // Schedule another low priority update.
1119 React.startTransition(() => ReactNoop.render(<App text="B" />));
1140 - // This update should also suspend.
1141 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1120 + await waitForAll(['Suspend! [B]', 'Loading...']);
1121 expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1122
1123 // Schedule a regular update. Its expiration time will fall between
1124 // the expiration times of the previous two updates.
1125 ReactNoop.render(<App text="C" />);
1147 - expect(Scheduler).toFlushAndYield(['C']);
1126 + await waitForAll(['C']);
1127 expect(ReactNoop).toMatchRenderedOutput(<span prop="C" />);
1128
1129 // Flush the remaining work.
1130 await resolveText('A');
1131 await resolveText('B');
1153 - // Nothing else to render.
1154 - expect(Scheduler).toFlushWithoutYielding();
1132 + await waitForAll([]);
1133 expect(ReactNoop).toMatchRenderedOutput(<span prop="C" />);
1134 });
1135
@@ -1188,17 +1166,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1166 jest.advanceTimersByTime(1000);
1167 ReactNoop.render(<Foo text="goodbye" />);
1168
1191 - expect(Scheduler).toFlushAndYield([
1192 - 'Suspend! [goodbye]',
1193 - 'Loading...',
1194 - 'Commit: goodbye',
1195 - ]);
1169 + await waitForAll(['Suspend! [goodbye]', 'Loading...', 'Commit: goodbye']);
1170 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1171
1172 await resolveText('goodbye');
1173 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1174
1201 - expect(Scheduler).toFlushAndYield(['goodbye']);
1175 + await waitForAll(['goodbye']);
1176 expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
1177 });
1178
@@ -1223,18 +1197,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1197 }
1198
1199 ReactNoop.render(<App />);
1226 - expect(Scheduler).toFlushAndYield([
1227 - 'Suspend! [A]',
1228 - 'Suspend! [B]',
1229 - 'Suspend! [C]',
1230 - ]);
1200 + await waitForAll(['Suspend! [A]', 'Suspend! [B]', 'Suspend! [C]']);
1201 expect(ReactNoop).toMatchRenderedOutput('Loading...');
1202
1203 await resolveText('A');
1204 await resolveText('B');
1205 await resolveText('C');
1206
1237 - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']);
1207 + await waitForAll(['A', 'B', 'C']);
1208 expect(ReactNoop).toMatchRenderedOutput(
1209 <>
1210 <span prop="A" />
@@ -1257,14 +1227,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1227
1228 // Times out immediately, ignoring the specified threshold.
1229 ReactNoop.renderLegacySyncRoot(<App />);
1260 - expect(Scheduler).toHaveYielded(['Suspend! [Result]', 'Loading...']);
1230 + assertLog(['Suspend! [Result]', 'Loading...']);
1231 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1232
1233 await act(async () => {
1234 resolveText('Result');
1235 });
1236
1267 - expect(Scheduler).toHaveYielded(['Result']);
1237 + assertLog(['Result']);
1238 expect(ReactNoop).toMatchRenderedOutput(<span prop="Result" />);
1239 });
1240
@@ -1300,7 +1270,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1270 // Initial mount.
1271 await seedNextTextCache('Step: 1');
1272 ReactNoop.renderLegacySyncRoot(<App />);
1303 - expect(Scheduler).toHaveYielded(['Step: 1', 'Sibling']);
1273 + assertLog(['Step: 1', 'Sibling']);
1274 expect(ReactNoop).toMatchRenderedOutput(
1275 <>
1276 <span prop="Step: 1" />
@@ -1333,7 +1303,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1303 await act(async () => {
1304 resolveText('Step: 2');
1305 });
1336 - expect(Scheduler).toHaveYielded(['Step: 2']);
1306 + assertLog(['Step: 2']);
1307 expect(ReactNoop).toMatchRenderedOutput(
1308 <>
1309 <span prop="Step: 2" />
@@ -1381,7 +1351,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1351 ReactNoop.renderLegacySyncRoot(<App />, () =>
1352 Scheduler.unstable_yieldValue('Commit root'),
1353 );
1384 - expect(Scheduler).toHaveYielded([
1354 + assertLog([
1355 'A',
1356 'Suspend! [B]',
1357 'C',
@@ -1407,7 +1377,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1377 resolveText('B');
1378 });
1379
1410 - expect(Scheduler).toHaveYielded(['B']);
1380 + assertLog(['B']);
1381 expect(ReactNoop).toMatchRenderedOutput(
1382 <>
1383 <span prop="A" />
@@ -1442,22 +1412,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1412 </Suspense>,
1413 );
1414
1445 - expect(Scheduler).toHaveYielded([
1446 - 'constructor',
1447 - 'Suspend! [Hi]',
1448 - 'Loading...',
1449 - ]);
1415 + assertLog(['constructor', 'Suspend! [Hi]', 'Loading...']);
1416 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1417
1418 await act(async () => {
1419 resolveText('Hi');
1420 });
1421
1456 - expect(Scheduler).toHaveYielded([
1457 - 'constructor',
1458 - 'Hi',
1459 - 'componentDidMount',
1460 - ]);
1422 + assertLog(['constructor', 'Hi', 'componentDidMount']);
1423 expect(ReactNoop).toMatchRenderedOutput(<span prop="Hi" />);
1424 });
1425
@@ -1489,7 +1451,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1451
1452 ReactNoop.renderLegacySyncRoot(<Demo />);
1453
1492 - expect(Scheduler).toHaveYielded([
1454 + assertLog([
1455 'Suspend! [Hi]',
1456 'Loading...',
1457 // Re-render due to lifecycle update
@@ -1499,7 +1461,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1461 await act(async () => {
1462 resolveText('Hi');
1463 });
1502 - expect(Scheduler).toHaveYielded(['Hi']);
1464 + assertLog(['Hi']);
1465 expect(ReactNoop).toMatchRenderedOutput(<span prop="Hi" />);
1466 });
1467
@@ -1532,7 +1494,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1494
1495 ReactNoop.renderLegacySyncRoot(<App middleText="B" />);
1496
1535 - expect(Scheduler).toHaveYielded([
1497 + assertLog([
1498 'Suspend! [Hi]',
1499 'Loading...',
1500 // The child should have already been hidden
@@ -1545,7 +1507,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1507 await act(async () => {
1508 resolveText('Hi');
1509 });
1548 - expect(Scheduler).toHaveYielded(['Hi']);
1510 + assertLog(['Hi']);
1511 });
1512 } else {
1513 // @gate enableLegacyCache
@@ -1578,7 +1540,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1540
1541 ReactNoop.renderLegacySyncRoot(<App middleText="B" />);
1542
1581 - expect(Scheduler).toHaveYielded([
1543 + assertLog([
1544 'Suspend! [Hi]',
1545 'Loading...',
1546 // The child should have already been hidden
@@ -1589,7 +1551,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1551 resolveText('Hi');
1552 });
1553
1592 - expect(Scheduler).toHaveYielded(['Hi']);
1554 + assertLog(['Hi']);
1555 });
1556 }
1557
@@ -1622,7 +1584,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1584 </ErrorBoundary>,
1585 );
1586
1625 - expect(Scheduler).toHaveYielded(['Suspend! [Async]']);
1587 + assertLog(['Suspend! [Async]']);
1588 expect(ReactNoop).toMatchRenderedOutput(
1589 'Caught an error: Error in host config.',
1590 );
@@ -1661,7 +1623,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1623 await act(async () => {
1624 root.render(<App />);
1625 });
1664 - expect(Scheduler).toHaveYielded(['Mount']);
1626 + assertLog(['Mount']);
1627 expect(root).toMatchRenderedOutput('Child');
1628
1629 // Suspend the child. This puts it into an inconsistent state.
@@ -1674,7 +1636,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1636 await act(async () => {
1637 root.render(null);
1638 });
1677 - expect(Scheduler).toHaveYielded(['Unmount']);
1639 + assertLog(['Unmount']);
1640 });
1641 });
1642
@@ -1726,7 +1688,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1688 ReactNoop.renderLegacySyncRoot(<App />, () =>
1689 Scheduler.unstable_yieldValue('Commit root'),
1690 );
1729 - expect(Scheduler).toHaveYielded([
1691 + assertLog([
1692 'A',
1693 'Suspend! [B]',
1694 'C',
@@ -1802,7 +1764,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1764 ReactNoop.renderLegacySyncRoot(<App text="B" />, () =>
1765 Scheduler.unstable_yieldValue('Commit root'),
1766 );
1805 - expect(Scheduler).toHaveYielded([
1767 + assertLog([
1768 'A',
1769 'Suspend! [B]',
1770 'C',
@@ -1816,8 +1778,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1778 'Commit root',
1779 ]);
1780
1819 - // Flush passive effects.
1820 - expect(Scheduler).toFlushAndYield([
1781 + await waitForAll([
1782 'Effect [A]',
1783 // B's effect should not fire because it suspended
1784 // 'Effect [B]',
@@ -1837,7 +1798,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1798 resolveText('B');
1799 });
1800
1840 - expect(Scheduler).toHaveYielded([
1801 + assertLog([
1802 'B',
1803 'Destroy Layout Effect [Loading...]',
1804 'Layout Effect [B]',
@@ -1850,7 +1811,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1811 Scheduler.unstable_yieldValue('Commit root'),
1812 );
1813
1853 - expect(Scheduler).toHaveYielded([
1814 + assertLog([
1815 'A',
1816 'Suspend! [B2]',
1817 'C',
@@ -1862,8 +1823,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1823 'Commit root',
1824 ]);
1825
1865 - // Flush passive effects.
1866 - expect(Scheduler).toFlushAndYield([
1826 + await waitForAll([
1827 // B2's effect should not fire because it suspended
1828 // 'Effect [B2]',
1829 'Effect [Loading...]',
@@ -1873,7 +1833,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1833 resolveText('B2');
1834 });
1835
1876 - expect(Scheduler).toHaveYielded([
1836 + assertLog([
1837 'B2',
1838 'Destroy Layout Effect [Loading...]',
1839 'Destroy Layout Effect [B]',
@@ -1896,19 +1856,18 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1856 }
1857
1858 ReactNoop.render(<Foo />);
1899 - expect(Scheduler).toFlushAndYield(['Foo']);
1859 + await waitForAll(['Foo']);
1860
1861 React.startTransition(() => {
1862 ReactNoop.render(<Foo renderContent={true} />);
1863 });
1864 Scheduler.unstable_advanceTime(100);
1865 await advanceTimers(100);
1906 - // Start rendering
1907 - expect(Scheduler).toFlushAndYieldThrough(['Foo']);
1866 + await waitFor(['Foo']);
1867 // For some reason it took a long time to render Foo.
1868 Scheduler.unstable_advanceTime(1250);
1869 await advanceTimers(1250);
1911 - expect(Scheduler).toFlushAndYield([
1870 + await waitForAll([
1871 // A suspends
1872 'Suspend! [A]',
1873 'Loading...',
@@ -1919,24 +1878,19 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1878 // Flush some of the time
1879 Scheduler.unstable_advanceTime(450);
1880 await advanceTimers(450);
1922 - // Because we've already been waiting for so long we can
1923 - // wait a bit longer. Still nothing...
1924 - expect(Scheduler).toFlushWithoutYielding();
1881 + await waitForAll([]);
1882 expect(ReactNoop).toMatchRenderedOutput(null);
1883
1884 // Eventually we'll show the fallback.
1885 Scheduler.unstable_advanceTime(500);
1886 await advanceTimers(500);
1930 - // No need to rerender.
1931 - expect(Scheduler).toFlushWithoutYielding();
1887 + await waitForAll([]);
1888 // Since this is a transition, we never fallback.
1889 expect(ReactNoop).toMatchRenderedOutput(null);
1890
1891 // Flush the promise completely
1892 await resolveText('A');
1937 - // Renders successfully
1938 - // TODO: Why does this render Foo
1939 - expect(Scheduler).toFlushAndYield(['Foo', 'A']);
1893 + await waitForAll(['Foo', 'A']);
1894 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
1895 });
1896
@@ -1955,8 +1909,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1909 }
1910
1911 ReactNoop.render(<Foo />);
1958 - // Start rendering
1959 - expect(Scheduler).toFlushAndYield([
1912 + await waitForAll([
1913 'Foo',
1914 // A suspends
1915 'Suspend! [A]',
@@ -1972,8 +1925,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1925 Scheduler.unstable_advanceTime(5000);
1926 await advanceTimers(5000);
1927
1975 - // Retry with the new content.
1976 - expect(Scheduler).toFlushAndYield([
1928 + await waitForAll([
1929 'A',
1930 // B still suspends
1931 'Suspend! [B]',
@@ -1991,8 +1943,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1943
1944 // Flush the last promise completely
1945 await resolveText('B');
1994 - // Renders successfully
1995 - expect(Scheduler).toFlushAndYield(['B']);
1946 + await waitForAll(['B']);
1947 expect(ReactNoop).toMatchRenderedOutput(
1948 <>
1949 <span prop="A" />
@@ -2016,8 +1967,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1967 }
1968
1969 ReactNoop.render(<Foo />);
2019 - // Start rendering
2020 - expect(Scheduler).toFlushAndYield([
1970 + await waitForAll([
1971 'Foo',
1972 // A suspends
1973 'Suspend! [A]',
@@ -2030,8 +1980,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1980
1981 await resolveText('A');
1982
2033 - // Retry with the new content.
2034 - expect(Scheduler).toFlushAndYield([
1983 + await waitForAll([
1984 'A',
1985 // B still suspends
1986 'Suspend! [B]',
@@ -2046,8 +1995,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1995 // Before we commit another Promise resolves.
1996 // We're still showing the first loading state.
1997 expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2049 - // Restart and render the complete content.
2050 - expect(Scheduler).toFlushAndYield(['A', 'B']);
1998 + await waitForAll(['A', 'B']);
1999 expect(ReactNoop).toMatchRenderedOutput(
2000 <>
2001 <span prop="A" />
@@ -2068,18 +2016,18 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2016 }
2017
2018 ReactNoop.render(<Foo />);
2071 - expect(Scheduler).toFlushAndYield(['Foo']);
2019 + await waitForAll(['Foo']);
2020
2021 React.startTransition(() => {
2022 ReactNoop.render(<Foo renderContent={true} />);
2023 });
2076 - expect(Scheduler).toFlushAndYieldThrough(['Foo']);
2024 + await waitFor(['Foo']);
2025
2026 // Advance some time.
2027 Scheduler.unstable_advanceTime(100);
2028 await advanceTimers(100);
2029
2082 - expect(Scheduler).toFlushAndYield([
2030 + await waitForAll([
2031 // A suspends
2032 'Suspend! [A]',
2033 'Loading...',
@@ -2092,11 +2040,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2040 Scheduler.unstable_advanceTime(500);
2041 jest.advanceTimersByTime(500);
2042
2095 - // We should have already shown the fallback.
2096 - // When we wrote this test, we inferred the start time of high priority
2097 - // updates as way earlier in the past. This test ensures that we don't
2098 - // use this assumption to add a very long JND.
2099 - expect(Scheduler).toFlushWithoutYielding();
2043 + await waitForAll([]);
2044 // Transitions never fallback.
2045 expect(ReactNoop).toMatchRenderedOutput(null);
2046 });
@@ -2173,14 +2117,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2117 ReactNoop.render(<App />);
2118 });
2119
2176 - expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2120 + assertLog(['Suspend! [A]']);
2121 expect(ReactNoop).toMatchRenderedOutput('Loading...');
2122
2123 act(() => {
2124 ReactNoop.flushSync(() => showB());
2125 });
2126
2183 - expect(Scheduler).toHaveYielded(['Suspend! [A]', 'Suspend! [B]']);
2127 + assertLog(['Suspend! [A]', 'Suspend! [B]']);
2128 });
2129
2130 // TODO: flip to "warns" when this is implemented again.
@@ -2236,7 +2180,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2180 // also make sure lowpriority is okay
2181 await act(async () => show(true));
2182
2239 - expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2183 + assertLog(['Suspend! [A]']);
2184 await resolveText('A');
2185
2186 expect(ReactNoop).toMatchRenderedOutput('Loading...');
@@ -2262,7 +2206,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2206 // also make sure lowpriority is okay
2207 await act(async () => _setShow(true));
2208
2265 - expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2209 + assertLog(['Suspend! [A]']);
2210 await resolveText('A');
2211
2212 expect(ReactNoop).toMatchRenderedOutput('Loading...');
@@ -2286,17 +2230,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2230 }
2231
2232 ReactNoop.render(<Foo />);
2289 - expect(Scheduler).toFlushAndYield([
2290 - 'Foo',
2291 - 'Suspend! [A]',
2292 - 'B',
2293 - 'Initial load...',
2294 - ]);
2233 + await waitForAll(['Foo', 'Suspend! [A]', 'B', 'Initial load...']);
2234 expect(ReactNoop).toMatchRenderedOutput(<span prop="Initial load..." />);
2235
2236 // Eventually we resolve and show the data.
2237 await resolveText('A');
2299 - expect(Scheduler).toFlushAndYield(['A', 'B']);
2238 + await waitForAll(['A', 'B']);
2239 expect(ReactNoop).toMatchRenderedOutput(
2240 <>
2241 <span prop="A" />
@@ -2306,13 +2245,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2245
2246 // Update to show C
2247 ReactNoop.render(<Foo showC={true} />);
2309 - expect(Scheduler).toFlushAndYield([
2310 - 'Foo',
2311 - 'A',
2312 - 'Suspend! [C]',
2313 - 'Updating...',
2314 - 'B',
2315 - ]);
2248 + await waitForAll(['Foo', 'A', 'Suspend! [C]', 'Updating...', 'B']);
2249 // Flush to skip suspended time.
2250 Scheduler.unstable_advanceTime(600);
2251 await advanceTimers(600);
@@ -2328,7 +2261,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2261
2262 // Later we load the data.
2263 await resolveText('C');
2331 - expect(Scheduler).toFlushAndYield(['A', 'C']);
2264 + await waitForAll(['A', 'C']);
2265 expect(ReactNoop).toMatchRenderedOutput(
2266 <>
2267 <span prop="A" />
@@ -2354,7 +2287,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2287 }
2288
2289 ReactNoop.render(<Foo />);
2357 - expect(Scheduler).toFlushAndYield([
2290 + await waitForAll([
2291 'Foo',
2292 'Suspend! [A]',
2293 'B',
@@ -2364,7 +2297,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2297
2298 // Eventually we resolve and show the data.
2299 await resolveText('A');
2367 - expect(Scheduler).toFlushAndYield(['A']);
2300 + await waitForAll(['A']);
2301 expect(ReactNoop).toMatchRenderedOutput(
2302 <>
2303 <span prop="A" />
@@ -2374,7 +2307,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2307
2308 // Update to show C
2309 ReactNoop.render(<Foo showC={true} />);
2377 - expect(Scheduler).toFlushAndYield([
2310 + await waitForAll([
2311 'Foo',
2312 'A',
2313 'Suspend! [C]',
@@ -2393,7 +2326,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2326
2327 // Later we load the data.
2328 await resolveText('C');
2396 - expect(Scheduler).toFlushAndYield(['A', 'C']);
2329 + await waitForAll(['A', 'C']);
2330 expect(ReactNoop).toMatchRenderedOutput(
2331 <>
2332 <span prop="A" />
@@ -2422,12 +2355,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2355 }
2356
2357 ReactNoop.render(<Foo />);
2425 - expect(Scheduler).toFlushAndYield([
2426 - 'Foo',
2427 - 'A',
2428 - 'Suspend! [B]',
2429 - 'Loading B...',
2430 - ]);
2358 + await waitForAll(['Foo', 'A', 'Suspend! [B]', 'Loading B...']);
2359 // Flush to skip suspended time.
2360 Scheduler.unstable_advanceTime(600);
2361 await advanceTimers(600);
@@ -2463,19 +2391,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2391 }
2392
2393 ReactNoop.render(<Foo />);
2466 - expect(Scheduler).toFlushAndYield(['Foo', 'A']);
2394 + await waitForAll(['Foo', 'A']);
2395 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2396
2397 React.startTransition(() => {
2398 ReactNoop.render(<Foo showB={true} />);
2399 });
2400
2473 - expect(Scheduler).toFlushAndYield([
2474 - 'Foo',
2475 - 'A',
2476 - 'Suspend! [B]',
2477 - 'Loading B...',
2478 - ]);
2401 + await waitForAll(['Foo', 'A', 'Suspend! [B]', 'Loading B...']);
2402 // Still suspended.
2403 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2404
@@ -2506,14 +2429,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2429 }
2430
2431 ReactNoop.render(<Foo />);
2509 - expect(Scheduler).toFlushAndYield(['Foo', 'A']);
2432 + await waitForAll(['Foo', 'A']);
2433 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2434
2435 React.startTransition(() => {
2436 ReactNoop.render(<Foo showB={true} />);
2437 });
2438
2516 - expect(Scheduler).toFlushAndYield([
2439 + await waitForAll([
2440 'Foo',
2441 'A',
2442 'Suspend! [B]',
@@ -2543,7 +2466,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2466 }
2467
2468 ReactNoop.render(<Foo />);
2546 - expect(Scheduler).toFlushAndYield([]);
2469 + await waitForAll([]);
2470
2471 ReactNoop.render(<Foo renderContent={1} />);
2472
@@ -2552,14 +2475,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2475 Scheduler.unstable_advanceTime(1500);
2476 await advanceTimers(1500);
2477
2555 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A...']);
2478 + await waitForAll(['Suspend! [A]', 'Loading A...']);
2479 // We're still suspended.
2480 expect(ReactNoop).toMatchRenderedOutput(null);
2481
2482 // Schedule an update at idle pri.
2483 ReactNoop.idleUpdates(() => ReactNoop.render(<Foo renderContent={2} />));
2561 - // We won't even work on Idle priority.
2562 - expect(Scheduler).toFlushAndYield([]);
2484 + await waitForAll([]);
2485
2486 // We're still suspended.
2487 expect(ReactNoop).toMatchRenderedOutput(null);
@@ -2592,7 +2514,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2514 // Initial render.
2515 React.startTransition(() => ReactNoop.render(<App page="A" />));
2516
2595 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2517 + await waitForAll(['Suspend! [A]', 'Loading...']);
2518 // Only a short time is needed to unsuspend the initial loading state.
2519 Scheduler.unstable_advanceTime(400);
2520 await advanceTimers(400);
@@ -2600,13 +2522,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2522
2523 // Later we load the data.
2524 await resolveText('A');
2603 - expect(Scheduler).toFlushAndYield(['A']);
2525 + await waitForAll(['A']);
2526 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2527
2528 // Start transition.
2529 React.startTransition(() => ReactNoop.render(<App page="B" />));
2530
2609 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2531 + await waitForAll(['Suspend! [B]', 'Loading...']);
2532 Scheduler.unstable_advanceTime(100000);
2533 await advanceTimers(100000);
2534 // Even after lots of time has passed, we have still not yet flushed the
@@ -2614,7 +2536,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2536 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2537 // Later we load the data.
2538 await resolveText('B');
2617 - expect(Scheduler).toFlushAndYield(['B']);
2539 + await waitForAll(['B']);
2540 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2541 });
2542
@@ -2635,13 +2557,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2557 }
2558
2559 ReactNoop.render(<App />);
2638 - expect(Scheduler).toFlushAndYield([]);
2560 + await waitForAll([]);
2561
2562 // Initial render.
2563 await act(async () => {
2564 React.startTransition(() => transitionToPage('A'));
2565
2644 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2566 + await waitForAll(['Suspend! [A]', 'Loading...']);
2567 // Only a short time is needed to unsuspend the initial loading state.
2568 Scheduler.unstable_advanceTime(400);
2569 await advanceTimers(400);
@@ -2650,14 +2572,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2572
2573 // Later we load the data.
2574 await resolveText('A');
2653 - expect(Scheduler).toFlushAndYield(['A']);
2575 + await waitForAll(['A']);
2576 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2577
2578 // Start transition.
2579 await act(async () => {
2580 React.startTransition(() => transitionToPage('B'));
2581
2660 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2582 + await waitForAll(['Suspend! [B]', 'Loading...']);
2583 Scheduler.unstable_advanceTime(100000);
2584 await advanceTimers(100000);
2585 // Even after lots of time has passed, we have still not yet flushed the
@@ -2666,7 +2588,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2588 });
2589 // Later we load the data.
2590 await resolveText('B');
2669 - expect(Scheduler).toFlushAndYield(['B']);
2591 + await waitForAll(['B']);
2592 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2593 });
2594
@@ -2690,13 +2612,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2612 }
2613
2614 ReactNoop.render(<App />);
2693 - expect(Scheduler).toFlushAndYield([]);
2615 + await waitForAll([]);
2616
2617 // Initial render.
2618 await act(async () => {
2619 React.startTransition(() => transitionToPage('A'));
2620
2699 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2621 + await waitForAll(['Suspend! [A]', 'Loading...']);
2622 // Only a short time is needed to unsuspend the initial loading state.
2623 Scheduler.unstable_advanceTime(400);
2624 await advanceTimers(400);
@@ -2705,14 +2627,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2627
2628 // Later we load the data.
2629 await resolveText('A');
2708 - expect(Scheduler).toFlushAndYield(['A']);
2630 + await waitForAll(['A']);
2631 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2632
2633 // Start transition.
2634 await act(async () => {
2635 React.startTransition(() => transitionToPage('B'));
2636
2715 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2637 + await waitForAll(['Suspend! [B]', 'Loading...']);
2638 Scheduler.unstable_advanceTime(100000);
2639 await advanceTimers(100000);
2640 // Even after lots of time has passed, we have still not yet flushed the
@@ -2721,7 +2643,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2643 });
2644 // Later we load the data.
2645 await resolveText('B');
2724 - expect(Scheduler).toFlushAndYield(['B']);
2646 + await waitForAll(['B']);
2647 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2648 });
2649 });
@@ -2740,7 +2662,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2662 // Initial render.
2663 React.startTransition(() => ReactNoop.render(<App page="A" />));
2664
2743 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2665 + await waitForAll(['Suspend! [A]', 'Loading...']);
2666 // Only a short time is needed to unsuspend the initial loading state.
2667 Scheduler.unstable_advanceTime(400);
2668 await advanceTimers(400);
@@ -2748,13 +2670,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2670
2671 // Later we load the data.
2672 await resolveText('A');
2751 - expect(Scheduler).toFlushAndYield(['A']);
2673 + await waitForAll(['A']);
2674 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2675
2676 // Start transition.
2677 React.startTransition(() => ReactNoop.render(<App page="B" />));
2678
2757 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2679 + await waitForAll(['Suspend! [B]', 'Loading...']);
2680 Scheduler.unstable_advanceTime(2999);
2681 await advanceTimers(2999);
2682 // Since the timeout is infinite (or effectively infinite),
@@ -2763,12 +2685,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2685
2686 // Later we load the data.
2687 await resolveText('B');
2766 - expect(Scheduler).toFlushAndYield(['B']);
2688 + await waitForAll(['B']);
2689 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2690
2691 // Start a long (infinite) transition.
2692 React.startTransition(() => ReactNoop.render(<App page="C" />));
2771 - expect(Scheduler).toFlushAndYield(['Suspend! [C]', 'Loading...']);
2693 + await waitForAll(['Suspend! [C]', 'Loading...']);
2694
2695 // Even after lots of time has passed, we have still not yet flushed the
2696 // loading state.
@@ -2794,13 +2716,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2716 }
2717
2718 ReactNoop.render(<App />);
2797 - expect(Scheduler).toFlushAndYield([]);
2719 + await waitForAll([]);
2720
2721 // Initial render.
2722 await act(async () => {
2723 React.startTransition(() => transitionToPage('A'));
2724
2803 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2725 + await waitForAll(['Suspend! [A]', 'Loading...']);
2726 // Only a short time is needed to unsuspend the initial loading state.
2727 Scheduler.unstable_advanceTime(400);
2728 await advanceTimers(400);
@@ -2809,14 +2731,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2731
2732 // Later we load the data.
2733 await resolveText('A');
2812 - expect(Scheduler).toFlushAndYield(['A']);
2734 + await waitForAll(['A']);
2735 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2736
2737 // Start transition.
2738 await act(async () => {
2739 React.startTransition(() => transitionToPage('B'));
2740
2819 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2741 + await waitForAll(['Suspend! [B]', 'Loading...']);
2742
2743 Scheduler.unstable_advanceTime(2999);
2744 await advanceTimers(2999);
@@ -2827,14 +2749,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2749
2750 // Later we load the data.
2751 await resolveText('B');
2830 - expect(Scheduler).toFlushAndYield(['B']);
2752 + await waitForAll(['B']);
2753 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2754
2755 // Start a long (infinite) transition.
2756 await act(async () => {
2757 React.startTransition(() => transitionToPage('C'));
2758
2837 - expect(Scheduler).toFlushAndYield(['Suspend! [C]', 'Loading...']);
2759 + await waitForAll(['Suspend! [C]', 'Loading...']);
2760
2761 // Even after lots of time has passed, we have still not yet flushed the
2762 // loading state.
@@ -2864,13 +2786,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2786 }
2787
2788 ReactNoop.render(<App />);
2867 - expect(Scheduler).toFlushAndYield([]);
2789 + await waitForAll([]);
2790
2791 // Initial render.
2792 await act(async () => {
2793 React.startTransition(() => transitionToPage('A'));
2794
2873 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2795 + await waitForAll(['Suspend! [A]', 'Loading...']);
2796 // Only a short time is needed to unsuspend the initial loading state.
2797 Scheduler.unstable_advanceTime(400);
2798 await advanceTimers(400);
@@ -2879,14 +2801,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2801
2802 // Later we load the data.
2803 await resolveText('A');
2882 - expect(Scheduler).toFlushAndYield(['A']);
2804 + await waitForAll(['A']);
2805 expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2806
2807 // Start transition.
2808 await act(async () => {
2809 React.startTransition(() => transitionToPage('B'));
2810
2889 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2811 + await waitForAll(['Suspend! [B]', 'Loading...']);
2812 Scheduler.unstable_advanceTime(2999);
2813 await advanceTimers(2999);
2814 // Since the timeout is infinite (or effectively infinite),
@@ -2896,14 +2818,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2818
2819 // Later we load the data.
2820 await resolveText('B');
2899 - expect(Scheduler).toFlushAndYield(['B']);
2821 + await waitForAll(['B']);
2822 expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2823
2824 // Start a long (infinite) transition.
2825 await act(async () => {
2826 React.startTransition(() => transitionToPage('C'));
2827
2906 - expect(Scheduler).toFlushAndYield(['Suspend! [C]', 'Loading...']);
2828 + await waitForAll(['Suspend! [C]', 'Loading...']);
2829
2830 // Even after lots of time has passed, we have still not yet flushed the
2831 // loading state.
@@ -2931,9 +2853,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2853
2854 // Initial render.
2855 ReactNoop.render(<App page="A" />);
2934 - expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [A]', 'Loading...']);
2856 + await waitForAll(['Hi!', 'Suspend! [A]', 'Loading...']);
2857 await resolveText('A');
2936 - expect(Scheduler).toFlushAndYield(['Hi!', 'A']);
2858 + await waitForAll(['Hi!', 'A']);
2859 expect(ReactNoop).toMatchRenderedOutput(
2860 <>
2861 <span prop="Hi!" />
@@ -2944,7 +2866,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2866 // Start transition.
2867 React.startTransition(() => ReactNoop.render(<App page="B" />));
2868
2947 - expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [B]', 'Loading B...']);
2869 + await waitForAll(['Hi!', 'Suspend! [B]', 'Loading B...']);
2870
2871 // Suspended
2872 expect(ReactNoop).toMatchRenderedOutput(
@@ -2955,7 +2877,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2877 );
2878 Scheduler.unstable_advanceTime(1800);
2879 await advanceTimers(1800);
2958 - expect(Scheduler).toFlushAndYield([]);
2880 + await waitForAll([]);
2881 // We should still be suspended here because this loading state should be avoided.
2882 expect(ReactNoop).toMatchRenderedOutput(
2883 <>
@@ -2964,7 +2886,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2886 </>,
2887 );
2888 await resolveText('B');
2967 - expect(Scheduler).toFlushAndYield(['Hi!', 'B']);
2889 + await waitForAll(['Hi!', 'B']);
2890 expect(ReactNoop).toMatchRenderedOutput(
2891 <>
2892 <span prop="Hi!" />
@@ -2994,7 +2916,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2916
2917 // Initial render.
2918 ReactNoop.render(<App page="A" />);
2997 - expect(Scheduler).toFlushAndYield(['Hi!', 'A']);
2919 + await waitForAll(['Hi!', 'A']);
2920 expect(ReactNoop).toMatchRenderedOutput(
2921 <>
2922 <span prop="Hi!" />
@@ -3005,7 +2927,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2927 // Start transition.
2928 React.startTransition(() => ReactNoop.render(<App page="B" />));
2929
3008 - expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [B]', 'Loading B...']);
2930 + await waitForAll(['Hi!', 'Suspend! [B]', 'Loading B...']);
2931
2932 // Suspended
2933 expect(ReactNoop).toMatchRenderedOutput(
@@ -3016,7 +2938,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2938 );
2939 Scheduler.unstable_advanceTime(1800);
2940 await advanceTimers(1800);
3019 - expect(Scheduler).toFlushAndYield([]);
2941 + await waitForAll([]);
2942 // We should still be suspended here because this loading state should be avoided.
2943 expect(ReactNoop).toMatchRenderedOutput(
2944 <>
@@ -3025,7 +2947,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2947 </>,
2948 );
2949 await resolveText('B');
3028 - expect(Scheduler).toFlushAndYield(['Hi!', 'B']);
2950 + await waitForAll(['Hi!', 'B']);
2951 expect(ReactNoop).toMatchRenderedOutput(
2952 <>
2953 <span prop="Hi!" />
@@ -3051,13 +2973,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2973 await act(async () => {
2974 root.render(<App text="Initial" />);
2975 });
3054 - expect(Scheduler).toHaveYielded(['Suspend! [Initial]']);
2976 + assertLog(['Suspend! [Initial]']);
2977
2978 // Resolve initial render
2979 await act(async () => {
2980 await resolveText('Initial');
2981 });
3060 - expect(Scheduler).toHaveYielded(['Initial']);
2982 + assertLog(['Initial']);
2983 expect(root).toMatchRenderedOutput(<span prop="Initial" />);
2984
2985 await act(async () => {
@@ -3066,7 +2988,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2988 React.startTransition(() => {
2989 root.render(<App text="First update" />);
2990 });
3069 - expect(Scheduler).toFlushAndYield(['Suspend! [First update]']);
2991 + await waitForAll(['Suspend! [First update]']);
2992
2993 // Should not display a fallback
2994 expect(root).toMatchRenderedOutput(<span prop="Initial" />);
@@ -3075,7 +2997,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2997 React.startTransition(() => {
2998 root.render(<App text="Second update" />);
2999 });
3078 - expect(Scheduler).toFlushAndYield(['Suspend! [Second update]']);
3000 + await waitForAll(['Suspend! [Second update]']);
3001
3002 // Should not display a fallback
3003 expect(root).toMatchRenderedOutput(<span prop="Initial" />);
@@ -3113,22 +3035,19 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3035 root.render(<App />);
3036 });
3037
3116 - expect(Scheduler).toHaveYielded(['Foo']);
3038 + assertLog(['Foo']);
3039
3040 await act(async () => {
3041 foo.setState({suspend: true});
3042
3121 - // In the regression that this covers, we would neglect to reset the
3122 - // current debug phase after suspending (in the catch block), so React
3123 - // thinks we're still inside the render phase.
3124 - expect(Scheduler).toFlushAndYieldThrough(['Suspend!']);
3043 + await waitFor(['Suspend!']);
3044
3045 // Then when this setState happens, React would incorrectly fire a warning
3046 // about updates that happen the render phase (only fired by classes).
3047 foo.setState({suspend: false});
3048 });
3049
3131 - expect(Scheduler).toHaveYielded([
3050 + assertLog([
3051 // First setState
3052 'Foo',
3053 ]);
@@ -3159,7 +3078,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3078
3079 // Initial render.
3080 ReactNoop.render(<App showContent={false} />);
3162 - expect(Scheduler).toFlushAndYieldThrough(['Commit']);
3081 + await waitFor(['Commit']);
3082 expect(ReactNoop).toMatchRenderedOutput(<div hidden={true} />);
3083
3084 // Start transition.
@@ -3167,16 +3086,16 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3086 ReactNoop.render(<App showContent={true} />);
3087 });
3088
3170 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
3089 + await waitForAll(['Suspend! [A]', 'Loading...']);
3090 await resolveText('A');
3172 - expect(Scheduler).toFlushAndYieldThrough(['A', 'Commit']);
3091 + await waitFor(['A', 'Commit']);
3092 expect(ReactNoop).toMatchRenderedOutput(
3093 <>
3094 <div hidden={true} />
3095 <span prop="A" />
3096 </>,
3097 );
3179 - expect(Scheduler).toFlushAndYield(['Offscreen']);
3098 + await waitForAll(['Offscreen']);
3099 expect(ReactNoop).toMatchRenderedOutput(
3100 <>
3101 <div hidden={true}>Offscreen</div>
@@ -3208,27 +3127,26 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3127
3128 // Initial render.
3129 ReactNoop.render(<App showContent={false} />);
3211 - expect(Scheduler).toFlushAndYieldThrough(['Commit']);
3130 + await waitFor(['Commit']);
3131 expect(ReactNoop).toMatchRenderedOutput(<div hidden={true} />);
3132
3214 - // Partially render through the hidden content.
3215 - expect(Scheduler).toFlushAndYieldThrough(['Suspend! [A]']);
3133 + await waitFor(['Suspend! [A]']);
3134
3135 // Start transition.
3136 React.startTransition(() => {
3137 ReactNoop.render(<App showContent={true} />);
3138 });
3139
3222 - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
3140 + await waitForAll(['Suspend! [A]', 'Loading...']);
3141 await resolveText('A');
3224 - expect(Scheduler).toFlushAndYieldThrough(['A', 'Commit']);
3142 + await waitFor(['A', 'Commit']);
3143 expect(ReactNoop).toMatchRenderedOutput(
3144 <>
3145 <div hidden={true} />
3146 <span prop="A" />
3147 </>,
3148 );
3231 - expect(Scheduler).toFlushAndYield(['A', 'Offscreen']);
3149 + await waitForAll(['A', 'Offscreen']);
3150 expect(ReactNoop).toMatchRenderedOutput(
3151 <>
3152 <div hidden={true}>
@@ -3269,7 +3187,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3187 await act(async () => {
3188 root.render(<Parent />);
3189 });
3272 - expect(Scheduler).toHaveYielded(['A']);
3190 + assertLog(['A']);
3191 expect(root).toMatchRenderedOutput(<span prop="A" />);
3192
3193 await act(async () => {
@@ -3283,7 +3201,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3201 await resolveText('C');
3202 setText('C');
3203 });
3286 - expect(Scheduler).toHaveYielded([
3204 + assertLog([
3205 // First we attempt the high pri update. It suspends.
3206 'Suspend! [B]',
3207 'Loading...',
@@ -3323,7 +3241,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3241 await act(async () => {
3242 root.render(<Parent />);
3243 });
3326 - expect(Scheduler).toHaveYielded(['A']);
3244 + assertLog(['A']);
3245 expect(root).toMatchRenderedOutput(<span prop="A" />);
3246
3247 await act(async () => {
@@ -3337,7 +3255,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3255 setText('C');
3256 });
3257
3340 - expect(Scheduler).toFlushAndYield([
3258 + await waitForAll([
3259 // First we attempt the high pri update. It suspends.
3260 'Suspend! [B]',
3261 'Loading...',
@@ -3352,8 +3270,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3270 </>,
3271 );
3272
3355 - // Now flush the remaining work. The Idle update successfully finishes.
3356 - expect(Scheduler).toFlushAndYield(['C']);
3273 + await waitForAll(['C']);
3274 expect(root).toMatchRenderedOutput(<span prop="C" />);
3275 });
3276 },
@@ -3392,13 +3309,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3309 await act(async () => {
3310 root.render(<App />);
3311 });
3395 - expect(Scheduler).toHaveYielded(['A']);
3312 + assertLog(['A']);
3313 expect(root).toMatchRenderedOutput(<span prop="A" />);
3314
3315 await act(async () => {
3316 // Schedule an update inside the Suspense boundary that suspends.
3317 setAppText('B');
3401 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
3318 + await waitForAll(['Suspend! [B]', 'Loading...']);
3319 });
3320
3321 expect(root).toMatchRenderedOutput(
@@ -3418,7 +3335,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3335 });
3336 });
3337
3421 - expect(Scheduler).toHaveYielded([
3338 + assertLog([
3339 // First try to render the high pri update. Still suspended.
3340 'Suspend! [C]',
3341 'Loading...',
@@ -3477,7 +3394,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3394 await act(async () => {
3395 root.render(<Parent />);
3396 });
3480 - expect(Scheduler).toHaveYielded(['A']);
3397 + assertLog(['A']);
3398 // At this point, the setState return path follows current fiber.
3399 expect(root).toMatchRenderedOutput(<span prop="A" />);
3400
@@ -3486,7 +3403,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3403 await act(async () => {
3404 setText('B');
3405 });
3489 - expect(Scheduler).toHaveYielded(['B']);
3406 + assertLog(['B']);
3407 // Now the setState return path follows the *alternate* fiber.
3408 expect(root).toMatchRenderedOutput(<span prop="B" />);
3409
@@ -3494,7 +3411,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3411 await act(async () => {
3412 setText('C');
3413 });
3497 - expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
3414 + assertLog(['Suspend! [C]', 'Loading...']);
3415
3416 // Commit. This will insert a fragment fiber to wrap around the component
3417 // that triggered the update.
@@ -3516,9 +3433,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3433 await act(async () => {
3434 setText('D');
3435 });
3519 - // Even though the fragment fiber is not part of the return path, we should
3520 - // be able to finish rendering.
3521 - expect(Scheduler).toHaveYielded(['D']);
3436 + assertLog(['D']);
3437 expect(root).toMatchRenderedOutput(<span prop="D" />);
3438 },
3439 );
@@ -3554,7 +3469,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3469 await act(async () => {
3470 root.render(<Parent />);
3471 });
3557 - expect(Scheduler).toHaveYielded(['A']);
3472 + assertLog(['A']);
3473 // At this point, the setState return path follows current fiber.
3474 expect(root).toMatchRenderedOutput(<span prop="A" />);
3475
@@ -3563,7 +3478,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3478 await act(async () => {
3479 setText('B');
3480 });
3566 - expect(Scheduler).toHaveYielded(['B']);
3481 + assertLog(['B']);
3482 // Now the setState return path follows the *alternate* fiber.
3483 expect(root).toMatchRenderedOutput(<span prop="B" />);
3484
@@ -3571,7 +3486,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3486 await act(async () => {
3487 setText('C');
3488 });
3574 - expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
3489 + assertLog(['Suspend! [C]', 'Loading...']);
3490
3491 // Commit. This will insert a fragment fiber to wrap around the component
3492 // that triggered the update.
@@ -3598,9 +3513,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3513 setText('E');
3514 });
3515 });
3601 - // Even though the fragment fiber is not part of the return path, we should
3602 - // be able to finish rendering.
3603 - expect(Scheduler).toHaveYielded(['Suspend! [D]', 'E']);
3516 + assertLog(['Suspend! [D]', 'E']);
3517 expect(root).toMatchRenderedOutput(<span prop="E" />);
3518 },
3519 );
@@ -3663,7 +3576,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3576 await act(async () => {
3577 root.render(<Parent step={0} />);
3578 });
3666 - expect(Scheduler).toHaveYielded([
3579 + assertLog([
3580 'Outer text: A',
3581 'Outer step: 0',
3582 'Inner text: A',
@@ -3683,7 +3596,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3596 await act(async () => {
3597 setText('B');
3598 });
3686 - expect(Scheduler).toHaveYielded([
3599 + assertLog([
3600 'Outer text: B',
3601 'Outer step: 0',
3602 'Suspend! [Inner text: B]',
@@ -3709,10 +3622,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3622 });
3623 });
3624
3712 - // Only the outer part can update. The inner part should still show a
3713 - // fallback because we haven't finished loading B yet. Otherwise, the
3714 - // inner text would be inconsistent with the outer text.
3715 - expect(Scheduler).toHaveYielded([
3625 + assertLog([
3626 'Outer text: B',
3627 'Outer step: 1',
3628 'Suspend! [Inner text: B]',
@@ -3733,11 +3643,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3643 await act(async () => {
3644 await resolveText('Inner text: B');
3645 });
3736 - expect(Scheduler).toHaveYielded([
3737 - 'Inner text: B',
3738 - 'Inner step: 1',
3739 - 'Commit Child',
3740 - ]);
3646 + assertLog(['Inner text: B', 'Inner step: 1', 'Commit Child']);
3647 expect(root).toMatchRenderedOutput(
3648 <>
3649 <span prop="Outer text: B" />
@@ -3802,7 +3708,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3708 await act(async () => {
3709 root.render(<Parent step={0} />);
3710 });
3805 - expect(Scheduler).toHaveYielded(['Outer: A0', 'Inner: A0', 'Commit Child']);
3711 + assertLog(['Outer: A0', 'Inner: A0', 'Commit Child']);
3712 expect(root).toMatchRenderedOutput(
3713 <>
3714 <span prop="Outer: A0" />
@@ -3814,11 +3720,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3720 await act(async () => {
3721 setText('B');
3722 });
3817 - expect(Scheduler).toHaveYielded([
3818 - 'Outer: B0',
3819 - 'Suspend! [Inner: B0]',
3820 - 'Loading...',
3821 - ]);
3723 + assertLog(['Outer: B0', 'Suspend! [Inner: B0]', 'Loading...']);
3724 // Commit the placeholder
3725 await advanceTimers(250);
3726 expect(root).toMatchRenderedOutput(
@@ -3837,7 +3739,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3739 });
3740 });
3741
3840 - expect(Scheduler).toHaveYielded(['Outer: B1', 'Inner: B1', 'Commit Child']);
3742 + assertLog(['Outer: B1', 'Inner: B1', 'Commit Child']);
3743 expect(root).toMatchRenderedOutput(
3744 <>
3745 <span prop="Outer: B1" />
@@ -3895,20 +3797,15 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3797 await act(async () => {
3798 root.render(<Root />);
3799 });
3898 - expect(Scheduler).toHaveYielded(['']);
3800 + assertLog(['']);
3801 expect(root).toMatchRenderedOutput(<span prop="" />);
3802
3803 // Update to "a". That will suspend.
3804 await act(async () => {
3805 setTextWithShortTransition('a');
3904 - expect(Scheduler).toFlushAndYield([
3905 - 'Pending...',
3906 - '',
3907 - 'Suspend! [a]',
3908 - 'Loading...',
3909 - ]);
3806 + await waitForAll(['Pending...', '', 'Suspend! [a]', 'Loading...']);
3807 });
3911 - expect(Scheduler).toHaveYielded([]);
3808 + assertLog([]);
3809 expect(root).toMatchRenderedOutput(
3810 <>
3811 <span prop="Pending..." />
@@ -3919,7 +3816,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3816 // Update to "b". That will suspend, too.
3817 await act(async () => {
3818 setTextWithLongTransition('b');
3922 - expect(Scheduler).toFlushAndYield([
3819 + await waitForAll([
3820 // Neither is resolved yet.
3821 'Pending...',
3822 '',
@@ -3927,7 +3824,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3824 'Loading...',
3825 ]);
3826 });
3930 - expect(Scheduler).toHaveYielded([]);
3827 + assertLog([]);
3828 expect(root).toMatchRenderedOutput(
3829 <>
3830 <span prop="Pending..." />
@@ -3939,7 +3836,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3836 await act(async () => {
3837 await resolveText('a');
3838
3942 - expect(Scheduler).toFlushAndYield(['Suspend! [b]', 'Loading...']);
3839 + await waitForAll(['Suspend! [b]', 'Loading...']);
3840 expect(root).toMatchRenderedOutput(
3841 <>
3842 <span prop="Pending..." />
@@ -3951,7 +3848,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3848 await act(async () => {
3849 await resolveText('b');
3850 });
3954 - expect(Scheduler).toHaveYielded(['b']);
3851 + assertLog(['b']);
3852 // The bug was that the pending state got stuck forever.
3853 expect(root).toMatchRenderedOutput(<span prop="b" />);
3854 });
@@ -3977,7 +3874,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3874 </Suspense>,
3875 );
3876 });
3980 - expect(Scheduler).toHaveYielded(['A']);
3877 + assertLog(['A']);
3878 expect(root).toMatchRenderedOutput(<span prop="A" />);
3879
3880 await act(async () => {
@@ -3985,22 +3882,19 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3882 ReactNoop.idleUpdates(() => {
3883 setText('B');
3884 });
3988 - // Suspend the first update. The second update doesn't run because it has
3989 - // Idle priority.
3990 - expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
3885 + await waitForAll(['Suspend! [B]', 'Loading...']);
3886
3887 // Commit the fallback. Now we'll try working on Idle.
3888 jest.runAllTimers();
3889
3995 - // It also suspends.
3996 - expect(Scheduler).toFlushAndYield(['Suspend! [B]']);
3890 + await waitForAll(['Suspend! [B]']);
3891 });
3892
3893 await act(async () => {
3894 setText('B');
3895 await resolveText('B');
3896 });
4003 - expect(Scheduler).toHaveYielded(['B']);
3897 + assertLog(['B']);
3898 expect(root).toMatchRenderedOutput(<span prop="B" />);
3899 });
3900
@@ -4026,7 +3920,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3920 </>,
3921 );
3922 });
4029 - expect(Scheduler).toHaveYielded(['A', 'Suspend! [Async]', 'Loading...']);
3923 + assertLog(['A', 'Suspend! [Async]', 'Loading...']);
3924 expect(root).toMatchRenderedOutput(
3925 <>
3926 <span prop="A" />
@@ -4040,8 +3934,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3934 // Before the retry happens, schedule a new update.
3935 setText('B');
3936
4043 - // The update should be allowed to finish before the retry is attempted.
4044 - expect(Scheduler).toFlushUntilNextPaint(['B']);
3937 + await waitForPaint(['B']);
3938 expect(root).toMatchRenderedOutput(
3939 <>
3940 <span prop="B" />
@@ -4049,8 +3942,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3942 </>,
3943 );
3944 });
4052 - // Then do the retry.
4053 - expect(Scheduler).toHaveYielded(['Async']);
3945 + assertLog(['Async']);
3946 expect(root).toMatchRenderedOutput(
3947 <>
3948 <span prop="B" />
@@ -4087,13 +3979,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3979 await act(async () => {
3980 root.render(<App show={false} />);
3981 });
4090 - expect(Scheduler).toHaveYielded(['Mount Child']);
3982 + assertLog(['Mount Child']);
3983 expect(root).toMatchRenderedOutput(<span prop="Child" />);
3984
3985 await act(async () => {
3986 root.render(<App show={true} />);
3987 });
4096 - expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
3988 + assertLog(['Suspend! [Async]', 'Loading...']);
3989 expect(root).toMatchRenderedOutput(
3990 <>
3991 <span hidden={true} prop="Child" />
@@ -4104,7 +3996,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3996 await act(async () => {
3997 root.render(null);
3998 });
4107 - expect(Scheduler).toHaveYielded(['Unmount Child']);
3999 + assertLog(['Unmount Child']);
4000 });
4001
4002 // @gate enableLegacyCache
@@ -4135,13 +4027,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
4027 await act(async () => {
4028 root.render(<App show={false} />);
4029 });
4138 - expect(Scheduler).toHaveYielded(['Mount Child']);
4030 + assertLog(['Mount Child']);
4031 expect(root).toMatchRenderedOutput(<span prop="Child" />);
4032
4033 await act(async () => {
4034 root.render(<App show={true} />);
4035 });
4144 - expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
4036 + assertLog(['Suspend! [Async]', 'Loading...']);
4037 expect(root).toMatchRenderedOutput(
4038 <>
4039 <span hidden={true} prop="Child" />
@@ -4152,7 +4044,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
4044 await act(async () => {
4045 root.render(null);
4046 });
4155 - expect(Scheduler).toHaveYielded(['Unmount Child']);
4047 + assertLog(['Unmount Child']);
4048 });
4049
4050 // @gate enableLegacyCache
@@ -4202,7 +4094,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
4094 await act(async () => {
4095 root.render(<App showMore={false} />);
4096 });
4205 - expect(Scheduler).toHaveYielded([]);
4097 + assertLog([]);
4098 expect(root).toMatchRenderedOutput(<div />);
4099
4100 // Update. This will cause two separate trees to suspend. The first tree
@@ -4221,7 +4113,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
4113 await act(async () => {
4114 root.render(<App showMore={true} />);
4115 });
4224 - expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...', 'Hi']);
4116 + assertLog(['Suspend! [Async]', 'Loading...', 'Hi']);
4117 expect(root).toMatchRenderedOutput(
4118 <div>
4119 <span prop="Loading..." />