@samitouri / QOS-React-2 / commits / 9c32622cf0

Improve tests that use discrete events (#20667)

Ricky committed Jan 26, 2021 at 19:15 UTC 9c32622cf04e70dd40463ece25e83b7cc311b0ed
8 files changed +114 -99
packages/react-dom/src/__tests__/ReactDOMHooks-test.js
+11 -6
@@ -12,6 +12,7 @@
12 let React;
13 let ReactDOM;
14 let Scheduler;
15 +let act;
16
17 describe('ReactDOMHooks', () => {
18 let container;
@@ -22,6 +23,7 @@ describe('ReactDOMHooks', () => {
23 React = require('react');
24 ReactDOM = require('react-dom');
25 Scheduler = require('scheduler');
26 + act = require('react-dom/test-utils').unstable_concurrentAct;
27
28 container = document.createElement('div');
29 document.body.appendChild(container);
@@ -106,7 +108,7 @@ describe('ReactDOMHooks', () => {
108 });
109
110 // @gate experimental
109 - it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', () => {
111 + it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', async () => {
112 const {createRef, useCallback, useState} = React;
113
114 const Example = ({inputRef, labelRef}) => {
@@ -132,11 +134,14 @@ describe('ReactDOMHooks', () => {
134 Scheduler.unstable_flushAll();
135
136 inputRef.current.value = 'abc';
135 - inputRef.current.dispatchEvent(
136 - new Event('input', {bubbles: true, cancelable: true}),
137 - );
138 -
139 - Scheduler.unstable_flushAll();
137 + await act(async () => {
138 + inputRef.current.dispatchEvent(
139 + new Event('input', {
140 + bubbles: true,
141 + cancelable: true,
142 + }),
143 + );
144 + });
145
146 expect(labelRef.current.innerHTML).toBe('abc');
147 });
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+4 -4
@@ -187,7 +187,7 @@ function runActTests(label, render, unmount, rerender) {
187 expect(Scheduler).toHaveYielded([100]);
188 });
189
190 - it('flushes effects on every call', () => {
190 + it('flushes effects on every call', async () => {
191 function App() {
192 const [ctr, setCtr] = React.useState(0);
193 React.useEffect(() => {
@@ -209,16 +209,16 @@ function runActTests(label, render, unmount, rerender) {
209 button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
210 }
211
212 - act(() => {
212 + await act(async () => {
213 click();
214 click();
215 click();
216 });
217 // it consolidates the 3 updates, then fires the effect
218 expect(Scheduler).toHaveYielded([3]);
219 - act(click);
219 + await act(async () => click());
220 expect(Scheduler).toHaveYielded([4]);
221 - act(click);
221 + await act(async () => click());
222 expect(Scheduler).toHaveYielded([5]);
223 expect(button.innerHTML).toBe('5');
224 });
packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
+1 -1
@@ -685,7 +685,7 @@ describe('ChangeEventPlugin', () => {
685 });
686
687 // @gate experimental
688 - it('is async for non-input events', () => {
688 + it('is async for non-input events', async () => {
689 const root = ReactDOM.unstable_createRoot(container);
690 let input;
691
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+12 -8
@@ -13,6 +13,7 @@ describe('SimpleEventPlugin', function() {
13 let React;
14 let ReactDOM;
15 let Scheduler;
16 + let TestUtils;
17
18 let onClick;
19 let container;
@@ -39,6 +40,7 @@ describe('SimpleEventPlugin', function() {
40 React = require('react');
41 ReactDOM = require('react-dom');
42 Scheduler = require('scheduler');
43 + TestUtils = require('react-dom/test-utils');
44
45 onClick = jest.fn();
46 });
@@ -314,7 +316,7 @@ describe('SimpleEventPlugin', function() {
316 });
317
318 // @gate experimental
317 - it('end result of many interactive updates is deterministic', () => {
319 + it('end result of many interactive updates is deterministic', async () => {
320 container = document.createElement('div');
321 const root = ReactDOM.unstable_createRoot(container);
322 document.body.appendChild(container);
@@ -361,12 +363,14 @@ describe('SimpleEventPlugin', function() {
363 expect(button.textContent).toEqual('Count: 0');
364
365 // Click the button many more times
364 - click();
365 - click();
366 - click();
367 - click();
368 - click();
369 - click();
366 + await TestUtils.act(async () => {
367 + click();
368 + click();
369 + click();
370 + click();
371 + click();
372 + click();
373 + });
374
375 // Flush the remaining work
376 Scheduler.unstable_flushAll();
@@ -375,7 +379,7 @@ describe('SimpleEventPlugin', function() {
379 });
380
381 // @gate experimental
378 - it('flushes discrete updates in order', () => {
382 + it('flushes discrete updates in order', async () => {
383 container = document.createElement('div');
384 document.body.appendChild(container);
385
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+27 -24
@@ -232,7 +232,8 @@ describe('ReactIncrementalErrorHandling', () => {
232 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
233 });
234
235 - it("retries at a lower priority if there's additional pending work", () => {
235 + // @gate experimental
236 + it("retries at a lower priority if there's additional pending work", async () => {
237 function App(props) {
238 if (props.isBroken) {
239 Scheduler.unstable_yieldValue('error');
@@ -252,14 +253,14 @@ describe('ReactIncrementalErrorHandling', () => {
253 });
254 }
255
255 - ReactNoop.discreteUpdates(() => {
256 - ReactNoop.render(<App isBroken={true} />, onCommit);
257 - });
256 + ReactNoop.render(<App isBroken={true} />, onCommit);
257 expect(Scheduler).toFlushAndYieldThrough(['error']);
258 interrupt();
259
261 - // This update is in a separate batch
262 - ReactNoop.render(<App isBroken={false} />, onCommit);
260 + React.unstable_startTransition(() => {
261 + // This update is in a separate batch
262 + ReactNoop.render(<App isBroken={false} />, onCommit);
263 + });
264
265 expect(Scheduler).toFlushAndYieldThrough([
266 // The first render fails. But because there's a lower priority pending
@@ -311,16 +312,16 @@ describe('ReactIncrementalErrorHandling', () => {
312 });
313 }
314
314 - ReactNoop.discreteUpdates(() => {
315 - ReactNoop.render(<App isBroken={true} />, onCommit);
316 - });
315 + ReactNoop.render(<App isBroken={true} />, onCommit);
316 expect(Scheduler).toFlushAndYieldThrough(['error']);
317 interrupt();
318
319 expect(ReactNoop).toMatchRenderedOutput(null);
320
322 - // This update is in a separate batch
323 - ReactNoop.render(<App isBroken={false} />, onCommit);
321 + React.unstable_startTransition(() => {
322 + // This update is in a separate batch
323 + ReactNoop.render(<App isBroken={false} />, onCommit);
324 + });
325
326 expect(Scheduler).toFlushAndYieldThrough([
327 // The first render fails. But because there's a lower priority pending
@@ -1786,6 +1787,7 @@ describe('ReactIncrementalErrorHandling', () => {
1787 });
1788 }
1789
1790 + // @gate experimental
1791 it('uncaught errors should be discarded if the render is aborted', async () => {
1792 const root = ReactNoop.createRoot();
1793
@@ -1795,22 +1797,24 @@ describe('ReactIncrementalErrorHandling', () => {
1797 }
1798
1799 await ReactNoop.act(async () => {
1798 - ReactNoop.discreteUpdates(() => {
1799 - root.render(<Oops />);
1800 - });
1800 + root.render(<Oops />);
1801 +
1802 // Render past the component that throws, then yield.
1803 expect(Scheduler).toFlushAndYieldThrough(['Oops']);
1804 expect(root).toMatchRenderedOutput(null);
1805 // Interleaved update. When the root completes, instead of throwing the
1806 // error, it should try rendering again. This update will cause it to
1807 // recover gracefully.
1807 - root.render('Everything is fine.');
1808 + React.unstable_startTransition(() => {
1809 + root.render('Everything is fine.');
1810 + });
1811 });
1812
1813 // Should finish without throwing.
1814 expect(root).toMatchRenderedOutput('Everything is fine.');
1815 });
1816
1817 + // @gate experimental
1818 it('uncaught errors are discarded if the render is aborted, case 2', async () => {
1819 const {useState} = React;
1820 const root = ReactNoop.createRoot();
@@ -1835,21 +1839,20 @@ describe('ReactIncrementalErrorHandling', () => {
1839 });
1840
1841 await ReactNoop.act(async () => {
1838 - // Schedule a high pri and a low pri update on the root.
1839 - ReactNoop.discreteUpdates(() => {
1840 - root.render(<Oops />);
1842 + // Schedule a default pri and a low pri update on the root.
1843 + root.render(<Oops />);
1844 + React.unstable_startTransition(() => {
1845 + root.render(<AllGood />);
1846 });
1842 - root.render(<AllGood />);
1843 - // Render through just the high pri update. The low pri update remains on
1847 +
1848 + // Render through just the default pri update. The low pri update remains on
1849 // the queue.
1850 expect(Scheduler).toFlushAndYieldThrough(['Everything is fine.']);
1851
1847 - // Schedule a high pri update on a child that triggers an error.
1852 + // Schedule a default pri update on a child that triggers an error.
1853 // The root should capture this error. But since there's still a pending
1854 // update on the root, the error should be suppressed.
1850 - ReactNoop.discreteUpdates(() => {
1851 - setShouldThrow(true);
1852 - });
1855 + setShouldThrow(true);
1856 });
1857 // Should render the final state without throwing the error.
1858 expect(Scheduler).toHaveYielded(['Everything is fine.']);
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
+44 -47
@@ -570,14 +570,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
570 );
571 }
572
573 - // Schedule a high pri update and a low pri update, without rendering in
574 - // between.
575 - ReactNoop.discreteUpdates(() => {
576 - // High pri
577 - ReactNoop.render(<App />);
578 - });
573 + // Schedule a default pri update and a low pri update, without rendering in between.
574 + // Default pri
575 + ReactNoop.render(<App />);
576 // Low pri
580 - ReactNoop.render(<App hide={true} />);
577 + React.unstable_startTransition(() => {
578 + ReactNoop.render(<App hide={true} />);
579 + });
580
581 expect(Scheduler).toFlushAndYield([
582 // The first update suspends
@@ -1879,9 +1878,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1878 ReactNoop.render(<Foo />);
1879 expect(Scheduler).toFlushAndYield(['Foo']);
1880
1882 - ReactNoop.discreteUpdates(() =>
1883 - ReactNoop.render(<Foo renderContent={true} />),
1884 - );
1881 + ReactNoop.render(<Foo renderContent={true} />);
1882 expect(Scheduler).toFlushAndYieldThrough(['Foo']);
1883
1884 // Advance some time.
@@ -3080,48 +3077,48 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3077 // Schedule an update inside the Suspense boundary that suspends.
3078 setAppText('B');
3079 expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
3080 + });
3081
3084 - // Commit the placeholder
3085 - await advanceTimers(250);
3086 - expect(root).toMatchRenderedOutput(
3087 - <>
3088 - <span hidden={true} prop="A" />
3089 - <span prop="Loading..." />
3090 - </>,
3091 - );
3082 + expect(root).toMatchRenderedOutput(
3083 + <>
3084 + <span hidden={true} prop="A" />
3085 + <span prop="Loading..." />
3086 + </>,
3087 + );
3088
3093 - // Schedule a high pri update on the boundary, and a lower pri update
3094 - // on the fallback. We're testing to make sure the fallback can still
3095 - // update even though the primary tree is suspended.{
3096 - ReactNoop.discreteUpdates(() => {
3097 - setAppText('C');
3089 + // Schedule a default pri update on the boundary, and a lower pri update
3090 + // on the fallback. We're testing to make sure the fallback can still
3091 + // update even though the primary tree is suspended.
3092 + await ReactNoop.act(async () => {
3093 + setAppText('C');
3094 + React.unstable_startTransition(() => {
3095 + setFallbackText('Still loading...');
3096 });
3099 - setFallbackText('Still loading...');
3097 + });
3098
3101 - expect(Scheduler).toFlushAndYield([
3102 - // First try to render the high pri update. Still suspended.
3103 - 'Suspend! [C]',
3104 - 'Loading...',
3099 + expect(Scheduler).toHaveYielded([
3100 + // First try to render the high pri update. Still suspended.
3101 + 'Suspend! [C]',
3102 + 'Loading...',
3103
3106 - // In the expiration times model, once the high pri update suspends,
3107 - // we can't be sure if there's additional work at a lower priority
3108 - // that might unblock the tree. We do know that there's a lower
3109 - // priority update *somehwere* in the entire root, though (the update
3110 - // to the fallback). So we try rendering one more time, just in case.
3111 - // TODO: We shouldn't need to do this with lanes, because we always
3112 - // know exactly which lanes have pending work in each tree.
3113 - 'Suspend! [C]',
3114 -
3115 - // Then complete the update to the fallback.
3116 - 'Still loading...',
3117 - ]);
3118 - expect(root).toMatchRenderedOutput(
3119 - <>
3120 - <span hidden={true} prop="A" />
3121 - <span prop="Still loading..." />
3122 - </>,
3123 - );
3124 - });
3104 + // In the expiration times model, once the high pri update suspends,
3105 + // we can't be sure if there's additional work at a lower priority
3106 + // that might unblock the tree. We do know that there's a lower
3107 + // priority update *somehwere* in the entire root, though (the update
3108 + // to the fallback). So we try rendering one more time, just in case.
3109 + // TODO: We shouldn't need to do this with lanes, because we always
3110 + // know exactly which lanes have pending work in each tree.
3111 + 'Suspend! [C]',
3112 +
3113 + // Then complete the update to the fallback.
3114 + 'Still loading...',
3115 + ]);
3116 + expect(root).toMatchRenderedOutput(
3117 + <>
3118 + <span hidden={true} prop="A" />
3119 + <span prop="Still loading..." />
3120 + </>,
3121 + );
3122 },
3123 );
3124
packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js
+5 -4
@@ -1397,12 +1397,13 @@ describe('useMutableSource', () => {
1397 // Now mutate A. Both hooks should update.
1398 // This is at high priority so that it doesn't get batched with default
1399 // priority updates that might fire during the passive effect
1400 - ReactNoop.discreteUpdates(() => {
1401 - mutateA('a1');
1400 + await ReactNoop.act(async () => {
1401 + ReactNoop.discreteUpdates(() => {
1402 + mutateA('a1');
1403 + });
1404 });
1403 - expect(Scheduler).toFlushUntilNextPaint([]);
1405
1405 - expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');
1406 + expect(root).toMatchRenderedOutput('first: a1, second: a1');
1407 });
1408
1409 expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');
packages/react-refresh/src/__tests__/ReactFresh-test.js
+10 -5
@@ -2409,7 +2409,7 @@ describe('ReactFresh', () => {
2409 }
2410 });
2411
2412 - it('can hot reload offscreen components', () => {
2412 + it('can hot reload offscreen components', async () => {
2413 if (__DEV__ && __EXPERIMENTAL__) {
2414 const AppV1 = prepare(() => {
2415 function Hello() {
@@ -2471,10 +2471,15 @@ describe('ReactFresh', () => {
2471 expect(el.firstChild.textContent).toBe('0');
2472 expect(el.firstChild.style.color).toBe('red');
2473
2474 - el.firstChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
2475 - expect(el.firstChild.textContent).toBe('0');
2476 - expect(el.firstChild.style.color).toBe('red');
2477 - expect(Scheduler).toFlushAndYieldThrough(['Hello#layout']);
2474 + await act(async () => {
2475 + el.firstChild.dispatchEvent(
2476 + new MouseEvent('click', {
2477 + bubbles: true,
2478 + }),
2479 + );
2480 + });
2481 +
2482 + expect(Scheduler).toHaveYielded(['Hello#layout']);
2483 expect(el.firstChild.textContent).toBe('1');
2484 expect(el.firstChild.style.color).toBe('red');
2485