@samitouri / QOS-React / commits / ccb2a8a44e

Replace test renderer's fake Scheduler implementation with mock build (#14970)

* Replace test renderer's fake Scheduler implementation with mock build The test renderer has its own mock implementation of the Scheduler interface, with the ability to partially render work in tests. Now that this functionality has been lifted into a proper mock Scheduler build, we can use that instead. * Fix Profiler tests in prod

Andrew Clark committed Feb 28, 2019 at 10:50 UTC ccb2a8a44eee74504f1e3f3bebc272cbb1fcc0f0
16 files changed +598 -761
packages/jest-react/src/JestReact.js
+2 -1
@@ -26,7 +26,8 @@ function captureAssertion(fn) {
26 }
27
28 function assertYieldsWereCleared(root) {
29 - const actualYields = root.unstable_clearYields();
29 + const Scheduler = root._Scheduler;
30 + const actualYields = Scheduler.unstable_clearYields();
31 invariant(
32 actualYields.length === 0,
33 'Log of yielded values is not empty. ' +
packages/react-cache/src/__tests__/ReactCache-test.internal.js
+13 -60
@@ -14,55 +14,15 @@ let createResource;
14 let React;
15 let ReactFeatureFlags;
16 let ReactTestRenderer;
17 +let Scheduler;
18 let Suspense;
19 let TextResource;
20 let textResourceShouldFail;
20 -let flushScheduledWork;
21 -let evictLRU;
21
22 describe('ReactCache', () => {
23 beforeEach(() => {
24 jest.resetModules();
25
27 - let currentPriorityLevel = 3;
28 -
29 - jest.mock('scheduler', () => {
30 - let callbacks = [];
31 - return {
32 - unstable_scheduleCallback(callback) {
33 - const callbackIndex = callbacks.length;
34 - callbacks.push(callback);
35 - return {callbackIndex};
36 - },
37 - flushScheduledWork() {
38 - while (callbacks.length) {
39 - const callback = callbacks.pop();
40 - callback();
41 - }
42 - },
43 -
44 - unstable_ImmediatePriority: 1,
45 - unstable_UserBlockingPriority: 2,
46 - unstable_NormalPriority: 3,
47 - unstable_LowPriority: 4,
48 - unstable_IdlePriority: 5,
49 -
50 - unstable_runWithPriority(priorityLevel, fn) {
51 - const prevPriorityLevel = currentPriorityLevel;
52 - currentPriorityLevel = priorityLevel;
53 - try {
54 - return fn();
55 - } finally {
56 - currentPriorityLevel = prevPriorityLevel;
57 - }
58 - },
59 -
60 - unstable_getCurrentPriorityLevel() {
61 - return currentPriorityLevel;
62 - },
63 - };
64 - });
65 -
26 ReactFeatureFlags = require('shared/ReactFeatureFlags');
27 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
28 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
@@ -71,8 +31,7 @@ describe('ReactCache', () => {
31 ReactCache = require('react-cache');
32 createResource = ReactCache.unstable_createResource;
33 ReactTestRenderer = require('react-test-renderer');
74 - flushScheduledWork = require('scheduler').flushScheduledWork;
75 - evictLRU = flushScheduledWork;
34 + Scheduler = require('scheduler');
35
36 TextResource = createResource(([text, ms = 0]) => {
37 let listeners = null;
@@ -86,16 +45,12 @@ describe('ReactCache', () => {
45 listeners = [{resolve, reject}];
46 setTimeout(() => {
47 if (textResourceShouldFail) {
89 - ReactTestRenderer.unstable_yield(
90 - `Promise rejected [${text}]`,
91 - );
48 + Scheduler.yieldValue(`Promise rejected [${text}]`);
49 status = 'rejected';
50 value = new Error('Failed to load: ' + text);
51 listeners.forEach(listener => listener.reject(value));
52 } else {
96 - ReactTestRenderer.unstable_yield(
97 - `Promise resolved [${text}]`,
98 - );
53 + Scheduler.yieldValue(`Promise resolved [${text}]`);
54 status = 'resolved';
55 value = text;
56 listeners.forEach(listener => listener.resolve(value));
@@ -123,7 +78,7 @@ describe('ReactCache', () => {
78 });
79
80 function Text(props) {
126 - ReactTestRenderer.unstable_yield(props.text);
81 + Scheduler.yieldValue(props.text);
82 return props.text;
83 }
84
@@ -131,13 +86,13 @@ describe('ReactCache', () => {
86 const text = props.text;
87 try {
88 TextResource.read([props.text, props.ms]);
134 - ReactTestRenderer.unstable_yield(text);
89 + Scheduler.yieldValue(text);
90 return text;
91 } catch (promise) {
92 if (typeof promise.then === 'function') {
138 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
93 + Scheduler.yieldValue(`Suspend! [${text}]`);
94 } else {
140 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
95 + Scheduler.yieldValue(`Error! [${text}]`);
96 }
97 throw promise;
98 }
@@ -201,7 +156,7 @@ describe('ReactCache', () => {
156 });
157
158 function App() {
204 - ReactTestRenderer.unstable_yield('App');
159 + Scheduler.yieldValue('App');
160 return BadTextResource.read(['Hi', 100]);
161 }
162
@@ -284,9 +239,7 @@ describe('ReactCache', () => {
239 expect(root).toMatchRenderedOutput('145');
240
241 // We've now rendered values 1, 2, 3, 4, 5, over our limit of 3. The least
287 - // recently used values are 2 and 3. They will be evicted during the
288 - // next sweep.
289 - evictLRU();
242 + // recently used values are 2 and 3. They should have been evicted.
243
244 root.update(
245 <Suspense fallback={<Text text="Loading..." />}>
@@ -368,13 +321,13 @@ describe('ReactCache', () => {
321 const text = props.text;
322 try {
323 const actualText = BadTextResource.read([props.text, props.ms]);
371 - ReactTestRenderer.unstable_yield(actualText);
324 + Scheduler.yieldValue(actualText);
325 return actualText;
326 } catch (promise) {
327 if (typeof promise.then === 'function') {
375 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
328 + Scheduler.yieldValue(`Suspend! [${text}]`);
329 } else {
377 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
330 + Scheduler.yieldValue(`Error! [${text}]`);
331 }
332 throw promise;
333 }
packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js
+4 -6
@@ -5,6 +5,7 @@ describe('ErrorBoundaryReconciliation', () => {
5 let React;
6 let ReactFeatureFlags;
7 let ReactTestRenderer;
8 + let Scheduler;
9 let span;
10
11 beforeEach(() => {
@@ -14,6 +15,7 @@ describe('ErrorBoundaryReconciliation', () => {
15 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
16 ReactTestRenderer = require('react-test-renderer');
17 React = require('react');
18 + Scheduler = require('scheduler');
19
20 DidCatchErrorBoundary = class extends React.Component {
21 state = {error: null};
@@ -56,9 +58,7 @@ describe('ErrorBoundaryReconciliation', () => {
58 </ErrorBoundary>,
59 {unstable_isConcurrent: isConcurrent},
60 );
59 - if (isConcurrent) {
60 - renderer.unstable_flushAll();
61 - }
61 + Scheduler.flushAll();
62 expect(renderer).toMatchRenderedOutput(<span prop="BrokenRender" />);
63
64 expect(() => {
@@ -67,9 +67,7 @@ describe('ErrorBoundaryReconciliation', () => {
67 <BrokenRender fail={true} />
68 </ErrorBoundary>,
69 );
70 - if (isConcurrent) {
71 - renderer.unstable_flushAll();
72 - }
70 + Scheduler.flushAll();
71 }).toWarnDev(isConcurrent ? ['invalid', 'invalid'] : ['invalid']);
72 const Fallback = fallbackTagName;
73 expect(renderer).toMatchRenderedOutput(<Fallback prop="ErrorBoundary" />);
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+24 -28
@@ -15,6 +15,7 @@
15 let React;
16 let ReactFeatureFlags;
17 let ReactTestRenderer;
18 +let Scheduler;
19 let ReactDOMServer;
20 let act;
21
@@ -28,6 +29,7 @@ describe('ReactHooks', () => {
29 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
30 React = require('react');
31 ReactTestRenderer = require('react-test-renderer');
32 + Scheduler = require('scheduler');
33 ReactDOMServer = require('react-dom/server');
34 act = ReactTestRenderer.act;
35 });
@@ -53,7 +55,7 @@ describe('ReactHooks', () => {
55 const {useState, useLayoutEffect} = React;
56
57 function Child({text}) {
56 - ReactTestRenderer.unstable_yield('Child: ' + text);
58 + Scheduler.yieldValue('Child: ' + text);
59 return text;
60 }
61
@@ -66,9 +68,9 @@ describe('ReactHooks', () => {
68 setCounter2 = _setCounter2;
69
70 const text = `${counter1}, ${counter2}`;
69 - ReactTestRenderer.unstable_yield(`Parent: ${text}`);
71 + Scheduler.yieldValue(`Parent: ${text}`);
72 useLayoutEffect(() => {
71 - ReactTestRenderer.unstable_yield(`Effect: ${text}`);
73 + Scheduler.yieldValue(`Effect: ${text}`);
74 });
75 return <Child text={text} />;
76 }
@@ -161,7 +163,7 @@ describe('ReactHooks', () => {
163 const {useState, memo} = React;
164
165 function Child({text}) {
164 - ReactTestRenderer.unstable_yield('Child: ' + text);
166 + Scheduler.yieldValue('Child: ' + text);
167 return text;
168 }
169
@@ -174,7 +176,7 @@ describe('ReactHooks', () => {
176 setCounter2 = _setCounter2;
177
178 const text = `${counter1}, ${counter2} (${theme})`;
177 - ReactTestRenderer.unstable_yield(`Parent: ${text}`);
179 + Scheduler.yieldValue(`Parent: ${text}`);
180 return <Child text={text} />;
181 }
182
@@ -243,7 +245,7 @@ describe('ReactHooks', () => {
245 const [counter, _setCounter] = useState(0);
246 setCounter = _setCounter;
247
246 - ReactTestRenderer.unstable_yield(`Count: ${counter}`);
248 + Scheduler.yieldValue(`Count: ${counter}`);
249 return counter;
250 }
251
@@ -277,7 +279,7 @@ describe('ReactHooks', () => {
279 const [counter, _dispatch] = useReducer((s, a) => a, 0);
280 dispatch = _dispatch;
281
280 - ReactTestRenderer.unstable_yield(`Count: ${counter}`);
282 + Scheduler.yieldValue(`Count: ${counter}`);
283 return counter;
284 }
285
@@ -311,7 +313,7 @@ describe('ReactHooks', () => {
313 let setTheme;
314 function ThemeProvider({children}) {
315 const [theme, _setTheme] = useState('light');
314 - ReactTestRenderer.unstable_yield('Theme: ' + theme);
316 + Scheduler.yieldValue('Theme: ' + theme);
317 setTheme = _setTheme;
318 return (
319 <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
@@ -319,7 +321,7 @@ describe('ReactHooks', () => {
321 }
322
323 function Child({text}) {
322 - ReactTestRenderer.unstable_yield('Child: ' + text);
324 + Scheduler.yieldValue('Child: ' + text);
325 return text;
326 }
327
@@ -331,9 +333,9 @@ describe('ReactHooks', () => {
333 const theme = useContext(ThemeContext);
334
335 const text = `${counter} (${theme})`;
334 - ReactTestRenderer.unstable_yield(`Parent: ${text}`);
336 + Scheduler.yieldValue(`Parent: ${text}`);
337 useLayoutEffect(() => {
336 - ReactTestRenderer.unstable_yield(`Effect: ${text}`);
338 + Scheduler.yieldValue(`Effect: ${text}`);
339 });
340 return <Child text={text} />;
341 }
@@ -392,7 +394,7 @@ describe('ReactHooks', () => {
394 const {useState, useLayoutEffect} = React;
395
396 function Child({text}) {
395 - ReactTestRenderer.unstable_yield('Child: ' + text);
397 + Scheduler.yieldValue('Child: ' + text);
398 return text;
399 }
400
@@ -400,9 +402,9 @@ describe('ReactHooks', () => {
402 function Parent() {
403 const [counter, _setCounter] = useState(0);
404 setCounter = _setCounter;
403 - ReactTestRenderer.unstable_yield('Parent: ' + counter);
405 + Scheduler.yieldValue('Parent: ' + counter);
406 useLayoutEffect(() => {
405 - ReactTestRenderer.unstable_yield('Effect: ' + counter);
407 + Scheduler.yieldValue('Effect: ' + counter);
408 });
409 return <Child text={counter} />;
410 }
@@ -470,7 +472,7 @@ describe('ReactHooks', () => {
472 const {useState} = React;
473
474 function Child({text}) {
473 - ReactTestRenderer.unstable_yield('Child: ' + text);
475 + Scheduler.yieldValue('Child: ' + text);
476 return text;
477 }
478
@@ -478,7 +480,7 @@ describe('ReactHooks', () => {
480 function Parent() {
481 const [counter, _setCounter] = useState(0);
482 setCounter = _setCounter;
481 - ReactTestRenderer.unstable_yield('Parent: ' + counter);
483 + Scheduler.yieldValue('Parent: ' + counter);
484 return <Child text={counter} />;
485 }
486
@@ -489,9 +491,7 @@ describe('ReactHooks', () => {
491
492 const update = value => {
493 setCounter(previous => {
492 - ReactTestRenderer.unstable_yield(
493 - `Compute state (${previous} -> ${value})`,
494 - );
494 + Scheduler.yieldValue(`Compute state (${previous} -> ${value})`);
495 return value;
496 });
497 };
@@ -530,7 +530,7 @@ describe('ReactHooks', () => {
530 const {useState} = React;
531
532 function Child({text}) {
533 - ReactTestRenderer.unstable_yield('Child: ' + text);
533 + Scheduler.yieldValue('Child: ' + text);
534 return text;
535 }
536
@@ -538,7 +538,7 @@ describe('ReactHooks', () => {
538 function Parent() {
539 const [counter, _setCounter] = useState(1);
540 setCounter = _setCounter;
541 - ReactTestRenderer.unstable_yield('Parent: ' + counter);
541 + Scheduler.yieldValue('Parent: ' + counter);
542 return <Child text={counter} />;
543 }
544
@@ -550,9 +550,7 @@ describe('ReactHooks', () => {
550 const update = compute => {
551 setCounter(previous => {
552 const value = compute(previous);
553 - ReactTestRenderer.unstable_yield(
554 - `Compute state (${previous} -> ${value})`,
555 - );
553 + Scheduler.yieldValue(`Compute state (${previous} -> ${value})`);
554 return value;
555 });
556 };
@@ -590,9 +588,7 @@ describe('ReactHooks', () => {
588 const {useLayoutEffect} = React;
589 function App(props) {
590 useLayoutEffect(() => {
593 - ReactTestRenderer.unstable_yield(
594 - 'Did commit: ' + props.dependencies.join(', '),
595 - );
591 + Scheduler.yieldValue('Did commit: ' + props.dependencies.join(', '));
592 }, props.dependencies);
593 return props.dependencies;
594 }
@@ -613,7 +609,7 @@ describe('ReactHooks', () => {
609 const {useMemo} = React;
610 function App({text, hasDeps}) {
611 const resolvedText = useMemo(() => {
616 - ReactTestRenderer.unstable_yield('Compute');
612 + Scheduler.yieldValue('Compute');
613 return text.toUpperCase();
614 }, hasDeps ? null : [text]);
615 return resolvedText;
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+38 -47
@@ -1,6 +1,7 @@
1 let PropTypes;
2 let React;
3 let ReactTestRenderer;
4 +let Scheduler;
5 let ReactFeatureFlags;
6 let Suspense;
7 let lazy;
@@ -16,10 +17,11 @@ describe('ReactLazy', () => {
17 Suspense = React.Suspense;
18 lazy = React.lazy;
19 ReactTestRenderer = require('react-test-renderer');
20 + Scheduler = require('scheduler');
21 });
22
23 function Text(props) {
22 - ReactTestRenderer.unstable_yield(props.text);
24 + Scheduler.yieldValue(props.text);
25 return props.text;
26 }
27
@@ -203,10 +205,10 @@ describe('ReactLazy', () => {
205 it('mount and reorder', async () => {
206 class Child extends React.Component {
207 componentDidMount() {
206 - ReactTestRenderer.unstable_yield('Did mount: ' + this.props.label);
208 + Scheduler.yieldValue('Did mount: ' + this.props.label);
209 }
210 componentDidUpdate() {
209 - ReactTestRenderer.unstable_yield('Did update: ' + this.props.label);
211 + Scheduler.yieldValue('Did update: ' + this.props.label);
212 }
213 render() {
214 return <Text text={this.props.label} />;
@@ -287,7 +289,7 @@ describe('ReactLazy', () => {
289
290 it('resolves defaultProps without breaking memoization', async () => {
291 function LazyImpl(props) {
290 - ReactTestRenderer.unstable_yield('Lazy');
292 + Scheduler.yieldValue('Lazy');
293 return (
294 <React.Fragment>
295 <Text text={props.siblingText} />
@@ -337,44 +339,38 @@ describe('ReactLazy', () => {
339 state = {};
340
341 static getDerivedStateFromProps(props) {
340 - ReactTestRenderer.unstable_yield(
341 - `getDerivedStateFromProps: ${props.text}`,
342 - );
342 + Scheduler.yieldValue(`getDerivedStateFromProps: ${props.text}`);
343 return null;
344 }
345
346 constructor(props) {
347 super(props);
348 - ReactTestRenderer.unstable_yield(`constructor: ${this.props.text}`);
348 + Scheduler.yieldValue(`constructor: ${this.props.text}`);
349 }
350
351 componentDidMount() {
352 - ReactTestRenderer.unstable_yield(
353 - `componentDidMount: ${this.props.text}`,
354 - );
352 + Scheduler.yieldValue(`componentDidMount: ${this.props.text}`);
353 }
354
355 componentDidUpdate(prevProps) {
358 - ReactTestRenderer.unstable_yield(
356 + Scheduler.yieldValue(
357 `componentDidUpdate: ${prevProps.text} -> ${this.props.text}`,
358 );
359 }
360
361 componentWillUnmount() {
364 - ReactTestRenderer.unstable_yield(
365 - `componentWillUnmount: ${this.props.text}`,
366 - );
362 + Scheduler.yieldValue(`componentWillUnmount: ${this.props.text}`);
363 }
364
365 shouldComponentUpdate(nextProps) {
370 - ReactTestRenderer.unstable_yield(
366 + Scheduler.yieldValue(
367 `shouldComponentUpdate: ${this.props.text} -> ${nextProps.text}`,
368 );
369 return true;
370 }
371
372 getSnapshotBeforeUpdate(prevProps) {
377 - ReactTestRenderer.unstable_yield(
373 + Scheduler.yieldValue(
374 `getSnapshotBeforeUpdate: ${prevProps.text} -> ${this.props.text}`,
375 );
376 return null;
@@ -443,19 +439,17 @@ describe('ReactLazy', () => {
439 state = {};
440
441 UNSAFE_componentWillMount() {
446 - ReactTestRenderer.unstable_yield(
447 - `UNSAFE_componentWillMount: ${this.props.text}`,
448 - );
442 + Scheduler.yieldValue(`UNSAFE_componentWillMount: ${this.props.text}`);
443 }
444
445 UNSAFE_componentWillUpdate(nextProps) {
452 - ReactTestRenderer.unstable_yield(
446 + Scheduler.yieldValue(
447 `UNSAFE_componentWillUpdate: ${this.props.text} -> ${nextProps.text}`,
448 );
449 }
450
451 UNSAFE_componentWillReceiveProps(nextProps) {
458 - ReactTestRenderer.unstable_yield(
452 + Scheduler.yieldValue(
453 `UNSAFE_componentWillReceiveProps: ${this.props.text} -> ${
454 nextProps.text
455 }`,
@@ -512,7 +506,7 @@ describe('ReactLazy', () => {
506
507 it('resolves defaultProps on the outer wrapper but warns', async () => {
508 function T(props) {
515 - ReactTestRenderer.unstable_yield(props.inner + ' ' + props.outer);
509 + Scheduler.yieldValue(props.inner + ' ' + props.outer);
510 return props.inner + ' ' + props.outer;
511 }
512 T.defaultProps = {inner: 'Hi'};
@@ -580,9 +574,7 @@ describe('ReactLazy', () => {
574 <BadLazy />
575 </Suspense>,
576 );
583 - expect(() => {
584 - root.unstable_flushAll();
585 - }).toThrow(
577 + expect(Scheduler).toFlushAndThrow(
578 'Element type is invalid. Received a promise that resolves to: 42. ' +
579 'Lazy element type must resolve to a class or function.',
580 );
@@ -610,9 +602,7 @@ describe('ReactLazy', () => {
602 <Lazy2 text="Hello" />
603 </Suspense>,
604 );
613 - expect(() => {
614 - root.unstable_flushAll();
615 - }).toThrow(
605 + expect(Scheduler).toFlushAndThrow(
606 'Element type is invalid. Received a promise that resolves to: [object Object]. ' +
607 'Lazy element type must resolve to a class or function.' +
608 (__DEV__
@@ -659,10 +649,11 @@ describe('ReactLazy', () => {
649 // Mount
650 await Promise.resolve();
651 expect(() => {
662 - root.unstable_flushAll();
663 - }).toWarnDev(
652 + expect(Scheduler);
653 + Scheduler.flushAll();
654 + }).toWarnDev([
655 'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
665 - );
656 + ]);
657 expect(root).toMatchRenderedOutput('22');
658
659 // Update
@@ -672,7 +663,7 @@ describe('ReactLazy', () => {
663 <LazyAdd inner={false} outer={false} />
664 </Suspense>,
665 );
675 - root.unstable_flushAll();
666 + expect(Scheduler).toFlushWithoutYielding();
667 }).toWarnDev(
668 'Invalid prop `inner` of type `boolean` supplied to `Add`, expected `number`.',
669 );
@@ -844,7 +835,7 @@ describe('ReactLazy', () => {
835 // Mount
836 await Promise.resolve();
837 expect(() => {
847 - root.unstable_flushAll();
838 + expect(Scheduler).toFlushAndYield(['Inner default text']);
839 }).toWarnDev(
840 'The prop `text` is marked as required in `T`, but its value is `undefined`',
841 );
@@ -857,7 +848,7 @@ describe('ReactLazy', () => {
848 <LazyText text={null} />
849 </Suspense>,
850 );
860 - root.unstable_flushAll();
851 + expect(Scheduler).toFlushAndYield([null]);
852 }).toWarnDev(
853 'The prop `text` is marked as required in `T`, but its value is `null`',
854 );
@@ -866,7 +857,7 @@ describe('ReactLazy', () => {
857
858 it('includes lazy-loaded component in warning stack', async () => {
859 const LazyFoo = lazy(() => {
869 - ReactTestRenderer.unstable_yield('Started loading');
860 + Scheduler.yieldValue('Started loading');
861 const Foo = props => <div>{[<Text text="A" />, <Text text="B" />]}</div>;
862 return fakeImport(Foo);
863 });
@@ -909,7 +900,7 @@ describe('ReactLazy', () => {
900 }
901 return fakeImport(
902 React.forwardRef((props, ref) => {
912 - ReactTestRenderer.unstable_yield('forwardRef');
903 + Scheduler.yieldValue('forwardRef');
904 return <Bar ref={ref} />;
905 }),
906 );
@@ -959,7 +950,7 @@ describe('ReactLazy', () => {
950
951 // Mount
952 await Promise.resolve();
962 - root.unstable_flushAll();
953 + expect(Scheduler).toFlushWithoutYielding();
954 expect(root).toMatchRenderedOutput('4');
955
956 // Update (shallowly equal)
@@ -968,7 +959,7 @@ describe('ReactLazy', () => {
959 <LazyAdd outer={2} />
960 </Suspense>,
961 );
971 - root.unstable_flushAll();
962 + expect(Scheduler).toFlushWithoutYielding();
963 expect(root).toMatchRenderedOutput('4');
964
965 // Update
@@ -977,7 +968,7 @@ describe('ReactLazy', () => {
968 <LazyAdd outer={3} />
969 </Suspense>,
970 );
980 - root.unstable_flushAll();
971 + expect(Scheduler).toFlushWithoutYielding();
972 expect(root).toMatchRenderedOutput('5');
973
974 // Update (shallowly equal)
@@ -986,7 +977,7 @@ describe('ReactLazy', () => {
977 <LazyAdd outer={3} />
978 </Suspense>,
979 );
989 - root.unstable_flushAll();
980 + expect(Scheduler).toFlushWithoutYielding();
981 expect(root).toMatchRenderedOutput('5');
982
983 // Update (explicit props)
@@ -995,7 +986,7 @@ describe('ReactLazy', () => {
986 <LazyAdd outer={1} inner={1} />
987 </Suspense>,
988 );
998 - root.unstable_flushAll();
989 + expect(Scheduler).toFlushWithoutYielding();
990 expect(root).toMatchRenderedOutput('2');
991
992 // Update (explicit props, shallowly equal)
@@ -1004,7 +995,7 @@ describe('ReactLazy', () => {
995 <LazyAdd outer={1} inner={1} />
996 </Suspense>,
997 );
1007 - root.unstable_flushAll();
998 + expect(Scheduler).toFlushWithoutYielding();
999 expect(root).toMatchRenderedOutput('2');
1000
1001 // Update
@@ -1013,7 +1004,7 @@ describe('ReactLazy', () => {
1004 <LazyAdd outer={1} />
1005 </Suspense>,
1006 );
1016 - root.unstable_flushAll();
1007 + expect(Scheduler).toFlushWithoutYielding();
1008 expect(root).toMatchRenderedOutput('3');
1009 });
1010
@@ -1043,7 +1034,7 @@ describe('ReactLazy', () => {
1034
1035 // Mount
1036 await Promise.resolve();
1046 - root.unstable_flushAll();
1037 + expect(Scheduler).toFlushWithoutYielding();
1038 expect(root).toMatchRenderedOutput('4');
1039
1040 // Update
@@ -1052,7 +1043,7 @@ describe('ReactLazy', () => {
1043 <LazyAdd outer={3} />
1044 </Suspense>,
1045 );
1055 - root.unstable_flushAll();
1046 + expect(Scheduler).toFlushWithoutYielding();
1047 expect(root).toMatchRenderedOutput('5');
1048
1049 // Update
@@ -1061,7 +1052,7 @@ describe('ReactLazy', () => {
1052 <LazyAdd />
1053 </Suspense>,
1054 );
1064 - root.unstable_flushAll();
1055 + expect(Scheduler).toFlushWithoutYielding();
1056 expect(root).toMatchRenderedOutput('2');
1057 });
1058
packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+38 -56
@@ -1,12 +1,11 @@
1 let React;
2 let ReactTestRenderer;
3 let ReactFeatureFlags;
4 +let Scheduler;
5 let ReactCache;
6 let Suspense;
7 let act;
8
8 -// let JestReact;
9 -
9 let TextResource;
10 let textResourceShouldFail;
11
@@ -21,7 +20,7 @@ describe('ReactSuspense', () => {
20 React = require('react');
21 ReactTestRenderer = require('react-test-renderer');
22 act = ReactTestRenderer.act;
24 - // JestReact = require('jest-react');
23 + Scheduler = require('scheduler');
24 ReactCache = require('react-cache');
25
26 Suspense = React.Suspense;
@@ -38,16 +37,12 @@ describe('ReactSuspense', () => {
37 listeners = [{resolve, reject}];
38 setTimeout(() => {
39 if (textResourceShouldFail) {
41 - ReactTestRenderer.unstable_yield(
42 - `Promise rejected [${text}]`,
43 - );
40 + Scheduler.yieldValue(`Promise rejected [${text}]`);
41 status = 'rejected';
42 value = new Error('Failed to load: ' + text);
43 listeners.forEach(listener => listener.reject(value));
44 } else {
48 - ReactTestRenderer.unstable_yield(
49 - `Promise resolved [${text}]`,
50 - );
45 + Scheduler.yieldValue(`Promise resolved [${text}]`);
46 status = 'resolved';
47 value = text;
48 listeners.forEach(listener => listener.resolve(value));
@@ -74,7 +69,7 @@ describe('ReactSuspense', () => {
69 });
70
71 function Text(props) {
77 - ReactTestRenderer.unstable_yield(props.text);
72 + Scheduler.yieldValue(props.text);
73 return props.text;
74 }
75
@@ -82,13 +77,13 @@ describe('ReactSuspense', () => {
77 const text = props.text;
78 try {
79 TextResource.read([props.text, props.ms]);
85 - ReactTestRenderer.unstable_yield(text);
80 + Scheduler.yieldValue(text);
81 return text;
82 } catch (promise) {
83 if (typeof promise.then === 'function') {
89 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
84 + Scheduler.yieldValue(`Suspend! [${text}]`);
85 } else {
91 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
86 + Scheduler.yieldValue(`Error! [${text}]`);
87 }
88 throw promise;
89 }
@@ -96,12 +91,12 @@ describe('ReactSuspense', () => {
91
92 it('suspends rendering and continues later', () => {
93 function Bar(props) {
99 - ReactTestRenderer.unstable_yield('Bar');
94 + Scheduler.yieldValue('Bar');
95 return props.children;
96 }
97
98 function Foo() {
104 - ReactTestRenderer.unstable_yield('Foo');
99 + Scheduler.yieldValue('Foo');
100 return (
101 <Suspense fallback={<Text text="Loading..." />}>
102 <Bar>
@@ -208,10 +203,10 @@ describe('ReactSuspense', () => {
203
204 function Async() {
205 if (!didResolve) {
211 - ReactTestRenderer.unstable_yield('Suspend!');
206 + Scheduler.yieldValue('Suspend!');
207 throw thenable;
208 }
214 - ReactTestRenderer.unstable_yield('Async');
209 + Scheduler.yieldValue('Async');
210 return 'Async';
211 }
212
@@ -243,10 +238,10 @@ describe('ReactSuspense', () => {
238 it('mounts a lazy class component in non-concurrent mode', async () => {
239 class Class extends React.Component {
240 componentDidMount() {
246 - ReactTestRenderer.unstable_yield('Did mount: ' + this.props.label);
241 + Scheduler.yieldValue('Did mount: ' + this.props.label);
242 }
243 componentDidUpdate() {
249 - ReactTestRenderer.unstable_yield('Did update: ' + this.props.label);
244 + Scheduler.yieldValue('Did update: ' + this.props.label);
245 }
246 render() {
247 return <Text text={this.props.label} />;
@@ -304,7 +299,7 @@ describe('ReactSuspense', () => {
299 });
300
301 it('throws if tree suspends and none of the Suspense ancestors have a fallback', () => {
307 - const root = ReactTestRenderer.create(
302 + ReactTestRenderer.create(
303 <Suspense>
304 <AsyncText text="Hi" ms={1000} />
305 </Suspense>,
@@ -313,34 +308,23 @@ describe('ReactSuspense', () => {
308 },
309 );
310
316 - let err;
317 - try {
318 - root.unstable_flushAll();
319 - } catch (e) {
320 - err = e;
321 - }
322 - expect(err.message.replace(/at .+?:\d+/g, 'at **')).toBe(
323 - 'AsyncText suspended while rendering, but no fallback UI was specified.\n' +
324 - '\n' +
325 - 'Add a <Suspense fallback=...> component higher in the tree to provide ' +
326 - 'a loading indicator or placeholder to display.\n' +
327 - (__DEV__ ? ' in AsyncText (at **)\n' : ' in AsyncText\n') +
328 - (__DEV__ ? ' in Suspense (at **)' : ' in Suspense'),
311 + expect(Scheduler).toFlushAndThrow(
312 + 'AsyncText suspended while rendering, but no fallback UI was specified.',
313 );
330 - expect(ReactTestRenderer).toHaveYielded(['Suspend! [Hi]', 'Suspend! [Hi]']);
314 + expect(Scheduler).toHaveYielded(['Suspend! [Hi]', 'Suspend! [Hi]']);
315 });
316
317 describe('outside concurrent mode', () => {
318 it('a mounted class component can suspend without losing state', () => {
319 class TextWithLifecycle extends React.Component {
320 componentDidMount() {
337 - ReactTestRenderer.unstable_yield(`Mount [${this.props.text}]`);
321 + Scheduler.yieldValue(`Mount [${this.props.text}]`);
322 }
323 componentDidUpdate() {
340 - ReactTestRenderer.unstable_yield(`Update [${this.props.text}]`);
324 + Scheduler.yieldValue(`Update [${this.props.text}]`);
325 }
326 componentWillUnmount() {
343 - ReactTestRenderer.unstable_yield(`Unmount [${this.props.text}]`);
327 + Scheduler.yieldValue(`Unmount [${this.props.text}]`);
328 }
329 render() {
330 return <Text {...this.props} />;
@@ -351,17 +335,15 @@ describe('ReactSuspense', () => {
335 class AsyncTextWithLifecycle extends React.Component {
336 state = {step: 1};
337 componentDidMount() {
354 - ReactTestRenderer.unstable_yield(
355 - `Mount [${this.props.text}:${this.state.step}]`,
356 - );
338 + Scheduler.yieldValue(`Mount [${this.props.text}:${this.state.step}]`);
339 }
340 componentDidUpdate() {
359 - ReactTestRenderer.unstable_yield(
341 + Scheduler.yieldValue(
342 `Update [${this.props.text}:${this.state.step}]`,
343 );
344 }
345 componentWillUnmount() {
364 - ReactTestRenderer.unstable_yield(
346 + Scheduler.yieldValue(
347 `Unmount [${this.props.text}:${this.state.step}]`,
348 );
349 }
@@ -371,13 +353,13 @@ describe('ReactSuspense', () => {
353 const ms = this.props.ms;
354 try {
355 TextResource.read([text, ms]);
374 - ReactTestRenderer.unstable_yield(text);
356 + Scheduler.yieldValue(text);
357 return text;
358 } catch (promise) {
359 if (typeof promise.then === 'function') {
378 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
360 + Scheduler.yieldValue(`Suspend! [${text}]`);
361 } else {
380 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
362 + Scheduler.yieldValue(`Error! [${text}]`);
363 }
364 throw promise;
365 }
@@ -556,20 +538,20 @@ describe('ReactSuspense', () => {
538 it('suspends in a class that has componentWillUnmount and is then deleted', () => {
539 class AsyncTextWithUnmount extends React.Component {
540 componentWillUnmount() {
559 - ReactTestRenderer.unstable_yield('will unmount');
541 + Scheduler.yieldValue('will unmount');
542 }
543 render() {
544 const text = this.props.text;
545 const ms = this.props.ms;
546 try {
547 TextResource.read([text, ms]);
566 - ReactTestRenderer.unstable_yield(text);
548 + Scheduler.yieldValue(text);
549 return text;
550 } catch (promise) {
551 if (typeof promise.then === 'function') {
570 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
552 + Scheduler.yieldValue(`Suspend! [${text}]`);
553 } else {
572 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
554 + Scheduler.yieldValue(`Error! [${text}]`);
555 }
556 throw promise;
557 }
@@ -600,20 +582,20 @@ describe('ReactSuspense', () => {
582
583 useLayoutEffect(
584 () => {
603 - ReactTestRenderer.unstable_yield('Did commit: ' + text);
585 + Scheduler.yieldValue('Did commit: ' + text);
586 },
587 [text],
588 );
589
590 try {
591 TextResource.read([props.text, props.ms]);
610 - ReactTestRenderer.unstable_yield(text);
592 + Scheduler.yieldValue(text);
593 return text;
594 } catch (promise) {
595 if (typeof promise.then === 'function') {
614 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
596 + Scheduler.yieldValue(`Suspend! [${text}]`);
597 } else {
616 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
598 + Scheduler.yieldValue(`Error! [${text}]`);
599 }
600 throw promise;
601 }
@@ -840,13 +822,13 @@ describe('ReactSuspense', () => {
822 const fullText = `${text}:${step}`;
823 try {
824 TextResource.read([fullText, ms]);
843 - ReactTestRenderer.unstable_yield(fullText);
825 + Scheduler.yieldValue(fullText);
826 return fullText;
827 } catch (promise) {
828 if (typeof promise.then === 'function') {
847 - ReactTestRenderer.unstable_yield(`Suspend! [${fullText}]`);
829 + Scheduler.yieldValue(`Suspend! [${fullText}]`);
830 } else {
849 - ReactTestRenderer.unstable_yield(`Error! [${fullText}]`);
831 + Scheduler.yieldValue(`Error! [${fullText}]`);
832 }
833 throw promise;
834 }
packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js
+11 -11
@@ -1,6 +1,7 @@
1 let React;
2 let Suspense;
3 let ReactTestRenderer;
4 +let Scheduler;
5 let ReactFeatureFlags;
6 let Random;
7
@@ -26,6 +27,7 @@ describe('ReactSuspenseFuzz', () => {
27 React = require('react');
28 Suspense = React.Suspense;
29 ReactTestRenderer = require('react-test-renderer');
30 + Scheduler = require('scheduler');
31 Random = require('random-seed');
32 });
33
@@ -55,7 +57,7 @@ describe('ReactSuspenseFuzz', () => {
57 };
58 const timeoutID = setTimeout(() => {
59 pendingTasks.delete(task);
58 - ReactTestRenderer.unstable_yield(task.label);
60 + Scheduler.yieldValue(task.label);
61 setStep(i + 1);
62 }, remountAfter);
63 pendingTasks.add(task);
@@ -88,7 +90,7 @@ describe('ReactSuspenseFuzz', () => {
90 };
91 const timeoutID = setTimeout(() => {
92 pendingTasks.delete(task);
91 - ReactTestRenderer.unstable_yield(task.label);
93 + Scheduler.yieldValue(task.label);
94 setStep([i + 1, suspendFor]);
95 }, beginAfter);
96 pendingTasks.add(task);
@@ -120,24 +122,24 @@ describe('ReactSuspenseFuzz', () => {
122 setTimeout(() => {
123 cache.set(fullText, fullText);
124 pendingTasks.delete(task);
123 - ReactTestRenderer.unstable_yield(task.label);
125 + Scheduler.yieldValue(task.label);
126 resolve();
127 }, delay);
128 },
129 };
130 cache.set(fullText, thenable);
129 - ReactTestRenderer.unstable_yield(`Suspended! [${fullText}]`);
131 + Scheduler.yieldValue(`Suspended! [${fullText}]`);
132 throw thenable;
133 } else if (typeof resolvedText.then === 'function') {
134 const thenable = resolvedText;
133 - ReactTestRenderer.unstable_yield(`Suspended! [${fullText}]`);
135 + Scheduler.yieldValue(`Suspended! [${fullText}]`);
136 throw thenable;
137 }
138 } else {
139 resolvedText = fullText;
140 }
141
140 - ReactTestRenderer.unstable_yield(resolvedText);
142 + Scheduler.yieldValue(resolvedText);
143 return resolvedText;
144 }
145
@@ -151,7 +153,7 @@ describe('ReactSuspenseFuzz', () => {
153 {children}
154 </ShouldSuspendContext.Provider>,
155 );
154 - root.unstable_flushAll();
156 + Scheduler.unstable_flushWithoutYielding();
157
158 let elapsedTime = 0;
159 while (pendingTasks && pendingTasks.size > 0) {
@@ -159,7 +161,7 @@ describe('ReactSuspenseFuzz', () => {
161 throw new Error('Something did not resolve properly.');
162 }
163 ReactTestRenderer.act(() => jest.advanceTimersByTime(1000));
162 - root.unstable_flushAll();
164 + Scheduler.unstable_flushWithoutYielding();
165 }
166
167 return root.toJSON();
@@ -189,9 +191,7 @@ describe('ReactSuspenseFuzz', () => {
191 const concurrentOutput = renderToRoot(concurrentRoot, children);
192 expect(concurrentOutput).toEqual(expectedOutput);
193 concurrentRoot.unmount();
192 - concurrentRoot.unstable_flushAll();
193 -
194 - ReactTestRenderer.unstable_clearYields();
194 + Scheduler.unstable_flushWithoutYielding();
195 }
196
197 function pickRandomWeighted(rand, options) {
packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js
+16 -28
@@ -19,11 +19,10 @@ runPlaceholderTests('ReactSuspensePlaceholder (persistence)', () =>
19 );
20
21 function runPlaceholderTests(suiteLabel, loadReactNoop) {
22 - let advanceTimeBy;
23 - let mockNow;
22 let Profiler;
23 let React;
24 let ReactTestRenderer;
25 + let Scheduler;
26 let ReactFeatureFlags;
27 let ReactCache;
28 let Suspense;
@@ -34,20 +33,13 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
33 beforeEach(() => {
34 jest.resetModules();
35
37 - let currentTime = 0;
38 - mockNow = jest.fn().mockImplementation(() => currentTime);
39 - global.Date.now = mockNow;
40 - advanceTimeBy = amount => {
41 - currentTime += amount;
42 - };
43 -
36 ReactFeatureFlags = require('shared/ReactFeatureFlags');
37 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
38 ReactFeatureFlags.enableProfilerTimer = true;
39 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
40 React = require('react');
41 ReactTestRenderer = require('react-test-renderer');
50 - ReactTestRenderer.unstable_setNowImplementation(mockNow);
42 + Scheduler = require('scheduler');
43 ReactCache = require('react-cache');
44
45 Profiler = React.unstable_Profiler;
@@ -65,16 +57,12 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
57 listeners = [{resolve, reject}];
58 setTimeout(() => {
59 if (textResourceShouldFail) {
68 - ReactTestRenderer.unstable_yield(
69 - `Promise rejected [${text}]`,
70 - );
60 + Scheduler.yieldValue(`Promise rejected [${text}]`);
61 status = 'rejected';
62 value = new Error('Failed to load: ' + text);
63 listeners.forEach(listener => listener.reject(value));
64 } else {
75 - ReactTestRenderer.unstable_yield(
76 - `Promise resolved [${text}]`,
77 - );
65 + Scheduler.yieldValue(`Promise resolved [${text}]`);
66 status = 'resolved';
67 value = text;
68 listeners.forEach(listener => listener.resolve(value));
@@ -101,22 +89,22 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
89 });
90
91 function Text({fakeRenderDuration = 0, text = 'Text'}) {
104 - advanceTimeBy(fakeRenderDuration);
105 - ReactTestRenderer.unstable_yield(text);
92 + Scheduler.advanceTime(fakeRenderDuration);
93 + Scheduler.yieldValue(text);
94 return text;
95 }
96
97 function AsyncText({fakeRenderDuration = 0, ms, text}) {
110 - advanceTimeBy(fakeRenderDuration);
98 + Scheduler.advanceTime(fakeRenderDuration);
99 try {
100 TextResource.read([text, ms]);
113 - ReactTestRenderer.unstable_yield(text);
101 + Scheduler.yieldValue(text);
102 return text;
103 } catch (promise) {
104 if (typeof promise.then === 'function') {
117 - ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
105 + Scheduler.yieldValue(`Suspend! [${text}]`);
106 } else {
119 - ReactTestRenderer.unstable_yield(`Error! [${text}]`);
107 + Scheduler.yieldValue(`Error! [${text}]`);
108 }
109 throw promise;
110 }
@@ -126,7 +114,7 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
114 class HiddenText extends React.PureComponent {
115 render() {
116 const text = this.props.text;
129 - ReactTestRenderer.unstable_yield(text);
117 + Scheduler.yieldValue(text);
118 return <span hidden={true}>{text}</span>;
119 }
120 }
@@ -243,19 +231,19 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
231 onRender = jest.fn();
232
233 const Fallback = () => {
246 - ReactTestRenderer.unstable_yield('Fallback');
247 - advanceTimeBy(10);
234 + Scheduler.yieldValue('Fallback');
235 + Scheduler.advanceTime(10);
236 return 'Loading...';
237 };
238
239 const Suspending = () => {
252 - ReactTestRenderer.unstable_yield('Suspending');
253 - advanceTimeBy(2);
240 + Scheduler.yieldValue('Suspending');
241 + Scheduler.advanceTime(2);
242 return <AsyncText ms={1000} text="Loaded" fakeRenderDuration={1} />;
243 };
244
245 App = ({shouldSuspend, text = 'Text', textRenderDuration = 5}) => {
258 - ReactTestRenderer.unstable_yield('App');
246 + Scheduler.yieldValue('App');
247 return (
248 <Profiler id="root" onRender={onRender}>
249 <Suspense maxDuration={500} fallback={<Fallback />}>
packages/react-test-renderer/src/ReactTestHostConfig.js
+7 -12
@@ -7,13 +7,8 @@
7 * @flow
8 */
9
10 +import * as Scheduler from 'scheduler/unstable_mock';
11 import warning from 'shared/warning';
11 -import {
12 - nowImplementation as TestRendererSchedulingNowImplementation,
13 - scheduleDeferredCallback as TestRendererSchedulingScheduleDeferredCallback,
14 - cancelDeferredCallback as TestRendererSchedulingCancelDeferredCallback,
15 - shouldYield as TestRendererSchedulingShouldYield,
16 -} from './ReactTestRendererScheduling';
12
13 export type Type = string;
14 export type Props = Object;
@@ -202,16 +197,16 @@ export function createTextInstance(
197 export const isPrimaryRenderer = false;
198 // This approach enables `now` to be mocked by tests,
199 // Even after the reconciler has initialized and read host config values.
205 -export const now = () => TestRendererSchedulingNowImplementation();
206 -export const scheduleDeferredCallback = TestRendererSchedulingScheduleDeferredCallback;
207 -export const cancelDeferredCallback = TestRendererSchedulingCancelDeferredCallback;
208 -export const shouldYield = TestRendererSchedulingShouldYield;
200 +export const now = Scheduler.unstable_now;
201 +export const scheduleDeferredCallback = Scheduler.unstable_scheduleCallback;
202 +export const cancelDeferredCallback = Scheduler.unstable_cancelCallback;
203 +export const shouldYield = Scheduler.unstable_shouldYield;
204
205 export const scheduleTimeout = setTimeout;
206 export const cancelTimeout = clearTimeout;
207 export const noTimeout = -1;
213 -export const schedulePassiveEffects = scheduleDeferredCallback;
214 -export const cancelPassiveEffects = cancelDeferredCallback;
208 +export const schedulePassiveEffects = Scheduler.unstable_scheduleCallback;
209 +export const cancelPassiveEffects = Scheduler.unstable_cancelCallback;
210
211 // -------------------
212 // Mutation
packages/react-test-renderer/src/ReactTestRenderer.js
+5 -16
@@ -11,6 +11,7 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
11 import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot';
12 import type {Instance, TextInstance} from './ReactTestHostConfig';
13
14 +import * as Scheduler from 'scheduler/unstable_mock';
15 import {
16 getPublicRootInstance,
17 createContainer,
@@ -42,13 +43,6 @@ import ReactVersion from 'shared/ReactVersion';
43 import warningWithoutStack from 'shared/warningWithoutStack';
44
45 import {getPublicInstance} from './ReactTestHostConfig';
45 -import {
46 - flushAll,
47 - flushNumberOfYields,
48 - clearYields,
49 - setNowImplementation,
50 - yieldValue,
51 -} from './ReactTestRendererScheduling';
46
47 type TestRendererOptions = {
48 createNodeMock: (element: React$Element<any>) => any,
@@ -430,6 +424,8 @@ function propsMatch(props: Object, filter: Object): boolean {
424 }
425
426 const ReactTestRendererFiber = {
427 + _Scheduler: Scheduler,
428 +
429 create(element: React$Element<any>, options: TestRendererOptions) {
430 let createNodeMock = defaultTestOptions.createNodeMock;
431 let isConcurrent = false;
@@ -455,6 +451,8 @@ const ReactTestRendererFiber = {
451 updateContainer(element, root, null, null);
452
453 const entry = {
454 + _Scheduler: Scheduler,
455 +
456 root: undefined, // makes flow happy
457 // we define a 'getter' for 'root' below using 'Object.defineProperty'
458 toJSON(): Array<ReactTestRendererNode> | ReactTestRendererNode | null {
@@ -518,13 +516,9 @@ const ReactTestRendererFiber = {
516 return getPublicRootInstance(root);
517 },
518
521 - unstable_flushAll: flushAll,
519 unstable_flushSync<T>(fn: () => T): T {
523 - clearYields();
520 return flushSync(fn);
521 },
526 - unstable_flushNumberOfYields: flushNumberOfYields,
527 - unstable_clearYields: clearYields,
522 };
523
524 Object.defineProperty(
@@ -555,15 +549,10 @@ const ReactTestRendererFiber = {
549 return entry;
550 },
551
558 - unstable_yield: yieldValue,
559 - unstable_clearYields: clearYields,
560 -
552 /* eslint-disable camelcase */
553 unstable_batchedUpdates: batchedUpdates,
554 /* eslint-enable camelcase */
555
565 - unstable_setNowImplementation: setNowImplementation,
566 -
556 act(callback: () => void): Thenable {
557 // note: keep these warning messages in sync with
558 // createNoop.js and ReactTestUtils.js
packages/react-test-renderer/src/ReactTestRendererScheduling.js deleted
-86
@@ -1,86 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -// Current virtual time
11 -export let nowImplementation = () => 0;
12 -export let scheduledCallback: (() => mixed) | null = null;
13 -export let yieldedValues: Array<mixed> = [];
14 -
15 -let didStop: boolean = false;
16 -let expectedNumberOfYields: number = -1;
17 -
18 -export function scheduleDeferredCallback(
19 - callback: () => mixed,
20 - options?: {timeout: number},
21 -): number {
22 - scheduledCallback = callback;
23 - const fakeCallbackId = 0;
24 - return fakeCallbackId;
25 -}
26 -
27 -export function cancelDeferredCallback(timeoutID: number): void {
28 - scheduledCallback = null;
29 -}
30 -
31 -export function setNowImplementation(implementation: () => number): void {
32 - nowImplementation = implementation;
33 -}
34 -
35 -export function shouldYield() {
36 - if (
37 - expectedNumberOfYields !== -1 &&
38 - yieldedValues.length >= expectedNumberOfYields
39 - ) {
40 - // We yielded at least as many values as expected. Stop rendering.
41 - didStop = true;
42 - return true;
43 - }
44 - // Keep rendering.
45 - return false;
46 -}
47 -
48 -export function flushAll(): Array<mixed> {
49 - yieldedValues = [];
50 - while (scheduledCallback !== null) {
51 - const cb = scheduledCallback;
52 - scheduledCallback = null;
53 - cb();
54 - }
55 - const values = yieldedValues;
56 - yieldedValues = [];
57 - return values;
58 -}
59 -
60 -export function flushNumberOfYields(count: number): Array<mixed> {
61 - expectedNumberOfYields = count;
62 - didStop = false;
63 - yieldedValues = [];
64 - try {
65 - while (scheduledCallback !== null && !didStop) {
66 - const cb = scheduledCallback;
67 - scheduledCallback = null;
68 - cb();
69 - }
70 - return yieldedValues;
71 - } finally {
72 - expectedNumberOfYields = -1;
73 - didStop = false;
74 - yieldedValues = [];
75 - }
76 -}
77 -
78 -export function yieldValue(value: mixed): void {
79 - yieldedValues.push(value);
80 -}
81 -
82 -export function clearYields(): Array<mixed> {
83 - const values = yieldedValues;
84 - yieldedValues = [];
85 - return values;
86 -}
packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
+10 -8
@@ -12,12 +12,14 @@
12
13 let React;
14 let ReactTestRenderer;
15 +let Scheduler;
16
17 describe('ReactTestRendererAsync', () => {
18 beforeEach(() => {
19 jest.resetModules();
20 React = require('react');
21 ReactTestRenderer = require('react-test-renderer');
22 + Scheduler = require('scheduler');
23 });
24
25 it('flushAll flushes all work', () => {
@@ -46,7 +48,7 @@ describe('ReactTestRendererAsync', () => {
48
49 it('flushAll returns array of yielded values', () => {
50 function Child(props) {
49 - ReactTestRenderer.unstable_yield(props.children);
51 + Scheduler.yieldValue(props.children);
52 return props.children;
53 }
54 function Parent(props) {
@@ -72,7 +74,7 @@ describe('ReactTestRendererAsync', () => {
74
75 it('flushThrough flushes until the expected values is yielded', () => {
76 function Child(props) {
75 - ReactTestRenderer.unstable_yield(props.children);
77 + Scheduler.yieldValue(props.children);
78 return props.children;
79 }
80 function Parent(props) {
@@ -100,7 +102,7 @@ describe('ReactTestRendererAsync', () => {
102
103 it('supports high priority interruptions', () => {
104 function Child(props) {
103 - ReactTestRenderer.unstable_yield(props.children);
105 + Scheduler.yieldValue(props.children);
106 return props.children;
107 }
108
@@ -141,7 +143,7 @@ describe('ReactTestRendererAsync', () => {
143 describe('Jest matchers', () => {
144 it('toFlushAndYieldThrough', () => {
145 const Yield = ({id}) => {
144 - ReactTestRenderer.unstable_yield(id);
146 + Scheduler.yieldValue(id);
147 return id;
148 };
149
@@ -163,7 +165,7 @@ describe('ReactTestRendererAsync', () => {
165
166 it('toFlushAndYield', () => {
167 const Yield = ({id}) => {
166 - ReactTestRenderer.unstable_yield(id);
168 + Scheduler.yieldValue(id);
169 return id;
170 };
171
@@ -197,7 +199,7 @@ describe('ReactTestRendererAsync', () => {
199
200 it('toFlushAndThrow', () => {
201 const Yield = ({id}) => {
200 - ReactTestRenderer.unstable_yield(id);
202 + Scheduler.yieldValue(id);
203 return id;
204 };
205
@@ -254,7 +256,7 @@ describe('ReactTestRendererAsync', () => {
256
257 it('toHaveYielded', () => {
258 const Yield = ({id}) => {
257 - ReactTestRenderer.unstable_yield(id);
259 + Scheduler.yieldValue(id);
260 return id;
261 };
262
@@ -278,7 +280,7 @@ describe('ReactTestRendererAsync', () => {
280 const renderer = ReactTestRenderer.create(<div />, {
281 unstable_isConcurrent: true,
282 });
281 - ReactTestRenderer.unstable_yield('Something');
283 + Scheduler.yieldValue('Something');
284 expect(() => expect(renderer).toFlushWithoutYielding()).toThrow(
285 'Log of yielded values is not empty.',
286 );
packages/react/src/__tests__/ReactProfiler-test.internal.js
+417 -361
@@ -16,9 +16,7 @@ let ReactNoop;
16 let Scheduler;
17 let ReactCache;
18 let ReactTestRenderer;
19 -let advanceTimeBy;
19 let SchedulerTracing;
21 -let mockNow;
20 let AdvanceTime;
21 let AsyncText;
22 let Text;
@@ -31,12 +29,6 @@ function loadModules({
29 replayFailedUnitOfWorkWithInvokeGuardedCallback = false,
30 useNoopRenderer = false,
31 } = {}) {
34 - let currentTime = 0;
35 -
36 - mockNow = jest.fn().mockImplementation(() => currentTime);
37 -
38 - global.Date.now = mockNow;
39 -
32 ReactFeatureFlags = require('shared/ReactFeatureFlags');
33 ReactFeatureFlags.debugRenderPhaseSideEffects = false;
34 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
@@ -55,13 +47,8 @@ function loadModules({
47 } else {
48 ReactNoop = null;
49 ReactTestRenderer = require('react-test-renderer');
58 - ReactTestRenderer.unstable_setNowImplementation(mockNow);
50 }
51
61 - advanceTimeBy = amount => {
62 - currentTime += amount;
63 - };
64 -
52 AdvanceTime = class extends React.Component {
53 static defaultProps = {
54 byAmount: 10,
@@ -72,25 +59,17 @@ function loadModules({
59 }
60 render() {
61 // Simulate time passing when this component is rendered
75 - advanceTimeBy(this.props.byAmount);
62 + Scheduler.advanceTime(this.props.byAmount);
63 return this.props.children || null;
64 }
65 };
66
67 resourcePromise = null;
68
82 - function yieldForRenderer(value) {
83 - if (ReactNoop) {
84 - ReactNoop.yield(value);
85 - } else {
86 - ReactTestRenderer.unstable_yield(value);
87 - }
88 - }
89 -
69 TextResource = ReactCache.unstable_createResource(([text, ms = 0]) => {
70 resourcePromise = new Promise((resolve, reject) =>
71 setTimeout(() => {
93 - yieldForRenderer(`Promise resolved [${text}]`);
72 + Scheduler.yieldValue(`Promise resolved [${text}]`);
73 resolve(text);
74 }, ms),
75 );
@@ -100,20 +79,20 @@ function loadModules({
79 AsyncText = ({ms, text}) => {
80 try {
81 TextResource.read([text, ms]);
103 - yieldForRenderer(`AsyncText [${text}]`);
82 + Scheduler.yieldValue(`AsyncText [${text}]`);
83 return text;
84 } catch (promise) {
85 if (typeof promise.then === 'function') {
107 - yieldForRenderer(`Suspend [${text}]`);
86 + Scheduler.yieldValue(`Suspend [${text}]`);
87 } else {
109 - yieldForRenderer(`Error [${text}]`);
88 + Scheduler.yieldValue(`Error [${text}]`);
89 }
90 throw promise;
91 }
92 };
93
94 Text = ({text}) => {
116 - yieldForRenderer(`Text [${text}]`);
95 + Scheduler.yieldValue(`Text [${text}]`);
96 return text;
97 };
98 }
@@ -258,7 +237,7 @@ describe('Profiler', () => {
237 const callback = jest.fn();
238
239 const Yield = ({value}) => {
261 - ReactTestRenderer.unstable_yield(value);
240 + Scheduler.yieldValue(value);
241 return null;
242 };
243
@@ -280,6 +259,28 @@ describe('Profiler', () => {
259 });
260
261 it('does not record times for components outside of Profiler tree', () => {
262 + // Mock the Scheduler module so we can track how many times the current
263 + // time is read
264 + jest.mock('scheduler/unstable_mock', obj => {
265 + const ActualScheduler = require.requireActual(
266 + 'scheduler/unstable_mock',
267 + );
268 + return {
269 + ...ActualScheduler,
270 + unstable_now: function mockUnstableNow() {
271 + ActualScheduler.yieldValue('read current time');
272 + return ActualScheduler.unstable_now();
273 + },
274 + };
275 + });
276 +
277 + jest.resetModules();
278 +
279 + loadModules({enableSchedulerTracing});
280 +
281 + // Clear yields in case the current time is read during initialization.
282 + Scheduler.unstable_clearYields();
283 +
284 ReactTestRenderer.create(
285 <div>
286 <AdvanceTime />
@@ -294,13 +295,19 @@ describe('Profiler', () => {
295 // 2. To compute the update expiration time
296 // 3. To record the commit time
297 // No additional calls from ProfilerTimer are expected.
297 - expect(mockNow).toHaveBeenCalledTimes(2);
298 + expect(Scheduler).toHaveYielded([
299 + 'read current time',
300 + 'read current time',
301 + ]);
302 +
303 + // Remove mock
304 + jest.unmock('scheduler/unstable_mock');
305 });
306
307 it('logs render times for both mount and update', () => {
308 const callback = jest.fn();
309
303 - advanceTimeBy(5); // 0 -> 5
310 + Scheduler.advanceTime(5); // 0 -> 5
311
312 const renderer = ReactTestRenderer.create(
313 <React.unstable_Profiler id="test" onRender={callback}>
@@ -323,7 +330,7 @@ describe('Profiler', () => {
330
331 callback.mockReset();
332
326 - advanceTimeBy(20); // 15 -> 35
333 + Scheduler.advanceTime(20); // 15 -> 35
334
335 renderer.update(
336 <React.unstable_Profiler id="test" onRender={callback}>
@@ -346,7 +353,7 @@ describe('Profiler', () => {
353
354 callback.mockReset();
355
349 - advanceTimeBy(20); // 45 -> 65
356 + Scheduler.advanceTime(20); // 45 -> 65
357
358 renderer.update(
359 <React.unstable_Profiler id="test" onRender={callback}>
@@ -371,7 +378,7 @@ describe('Profiler', () => {
378 it('includes render times of nested Profilers in their parent times', () => {
379 const callback = jest.fn();
380
374 - advanceTimeBy(5); // 0 -> 5
381 + Scheduler.advanceTime(5); // 0 -> 5
382
383 ReactTestRenderer.create(
384 <React.Fragment>
@@ -406,7 +413,7 @@ describe('Profiler', () => {
413 it('traces sibling Profilers separately', () => {
414 const callback = jest.fn();
415
409 - advanceTimeBy(5); // 0 -> 5
416 + Scheduler.advanceTime(5); // 0 -> 5
417
418 ReactTestRenderer.create(
419 <React.Fragment>
@@ -439,7 +446,7 @@ describe('Profiler', () => {
446 it('does not include time spent outside of profile root', () => {
447 const callback = jest.fn();
448
442 - advanceTimeBy(5); // 0 -> 5
449 + Scheduler.advanceTime(5); // 0 -> 5
450
451 ReactTestRenderer.create(
452 <React.Fragment>
@@ -514,7 +521,7 @@ describe('Profiler', () => {
521 it('decreases actual time but not base time when sCU prevents an update', () => {
522 const callback = jest.fn();
523
517 - advanceTimeBy(5); // 0 -> 5
524 + Scheduler.advanceTime(5); // 0 -> 5
525
526 const renderer = ReactTestRenderer.create(
527 <React.unstable_Profiler id="test" onRender={callback}>
@@ -526,7 +533,7 @@ describe('Profiler', () => {
533
534 expect(callback).toHaveBeenCalledTimes(1);
535
529 - advanceTimeBy(30); // 28 -> 58
536 + Scheduler.advanceTime(30); // 28 -> 58
537
538 renderer.update(
539 <React.unstable_Profiler id="test" onRender={callback}>
@@ -557,22 +564,22 @@ describe('Profiler', () => {
564 class WithLifecycles extends React.Component {
565 state = {};
566 static getDerivedStateFromProps() {
560 - advanceTimeBy(3);
567 + Scheduler.advanceTime(3);
568 return null;
569 }
570 shouldComponentUpdate() {
564 - advanceTimeBy(7);
571 + Scheduler.advanceTime(7);
572 return true;
573 }
574 render() {
568 - advanceTimeBy(5);
575 + Scheduler.advanceTime(5);
576 return null;
577 }
578 }
579
580 const callback = jest.fn();
581
575 - advanceTimeBy(5); // 0 -> 5
582 + Scheduler.advanceTime(5); // 0 -> 5
583
584 const renderer = ReactTestRenderer.create(
585 <React.unstable_Profiler id="test" onRender={callback}>
@@ -580,7 +587,7 @@ describe('Profiler', () => {
587 </React.unstable_Profiler>,
588 );
589
583 - advanceTimeBy(15); // 13 -> 28
590 + Scheduler.advanceTime(15); // 13 -> 28
591
592 renderer.update(
593 <React.unstable_Profiler id="test" onRender={callback}>
@@ -610,12 +617,12 @@ describe('Profiler', () => {
617 const callback = jest.fn();
618
619 const Yield = ({renderTime}) => {
613 - advanceTimeBy(renderTime);
614 - ReactTestRenderer.unstable_yield('Yield:' + renderTime);
620 + Scheduler.advanceTime(renderTime);
621 + Scheduler.yieldValue('Yield:' + renderTime);
622 return null;
623 };
624
618 - advanceTimeBy(5); // 0 -> 5
625 + Scheduler.advanceTime(5); // 0 -> 5
626
627 // Render partially, but run out of time before completing.
628 const renderer = ReactTestRenderer.create(
@@ -644,12 +651,12 @@ describe('Profiler', () => {
651 const callback = jest.fn();
652
653 const Yield = ({renderTime}) => {
647 - advanceTimeBy(renderTime);
648 - ReactTestRenderer.unstable_yield('Yield:' + renderTime);
654 + Scheduler.advanceTime(renderTime);
655 + Scheduler.yieldValue('Yield:' + renderTime);
656 return null;
657 };
658
652 - advanceTimeBy(5); // 0 -> 5
659 + Scheduler.advanceTime(5); // 0 -> 5
660
661 // Render partially, but don't finish.
662 // This partial render should take 5ms of simulated time.
@@ -667,7 +674,7 @@ describe('Profiler', () => {
674 expect(callback).toHaveBeenCalledTimes(0);
675
676 // Simulate time moving forward while frame is paused.
670 - advanceTimeBy(50); // 10 -> 60
677 + Scheduler.advanceTime(50); // 10 -> 60
678
679 // Flush the remaining work,
680 // Which should take an additional 10ms of simulated time.
@@ -694,12 +701,12 @@ describe('Profiler', () => {
701 const callback = jest.fn();
702
703 const Yield = ({renderTime}) => {
697 - advanceTimeBy(renderTime);
698 - ReactTestRenderer.unstable_yield('Yield:' + renderTime);
704 + Scheduler.advanceTime(renderTime);
705 + Scheduler.yieldValue('Yield:' + renderTime);
706 return null;
707 };
708
702 - advanceTimeBy(5); // 0 -> 5
709 + Scheduler.advanceTime(5); // 0 -> 5
710
711 // Render a partially update, but don't finish.
712 // This partial render should take 10ms of simulated time.
@@ -714,7 +721,7 @@ describe('Profiler', () => {
721 expect(callback).toHaveBeenCalledTimes(0);
722
723 // Simulate time moving forward while frame is paused.
717 - advanceTimeBy(100); // 15 -> 115
724 + Scheduler.advanceTime(100); // 15 -> 115
725
726 // Interrupt with higher priority work.
727 // The interrupted work simulates an additional 5ms of time.
@@ -747,12 +754,12 @@ describe('Profiler', () => {
754 const callback = jest.fn();
755
756 const Yield = ({renderTime}) => {
750 - advanceTimeBy(renderTime);
751 - ReactTestRenderer.unstable_yield('Yield:' + renderTime);
757 + Scheduler.advanceTime(renderTime);
758 + Scheduler.yieldValue('Yield:' + renderTime);
759 return null;
760 };
761
755 - advanceTimeBy(5); // 0 -> 5
762 + Scheduler.advanceTime(5); // 0 -> 5
763
764 const renderer = ReactTestRenderer.create(
765 <React.unstable_Profiler id="test" onRender={callback}>
@@ -774,7 +781,7 @@ describe('Profiler', () => {
781
782 callback.mockReset();
783
777 - advanceTimeBy(30); // 26 -> 56
784 + Scheduler.advanceTime(30); // 26 -> 56
785
786 // Render a partially update, but don't finish.
787 // This partial render should take 3ms of simulated time.
@@ -789,14 +796,14 @@ describe('Profiler', () => {
796 expect(callback).toHaveBeenCalledTimes(0);
797
798 // Simulate time moving forward while frame is paused.
792 - advanceTimeBy(100); // 59 -> 159
799 + Scheduler.advanceTime(100); // 59 -> 159
800
801 // Render another 5ms of simulated time.
802 expect(renderer).toFlushAndYieldThrough(['Yield:5']);
803 expect(callback).toHaveBeenCalledTimes(0);
804
805 // Simulate time moving forward while frame is paused.
799 - advanceTimeBy(100); // 164 -> 264
806 + Scheduler.advanceTime(100); // 164 -> 264
807
808 // Interrupt with higher priority work.
809 // The interrupted work simulates an additional 11ms of time.
@@ -828,8 +835,8 @@ describe('Profiler', () => {
835 const callback = jest.fn();
836
837 const Yield = ({renderTime}) => {
831 - advanceTimeBy(renderTime);
832 - ReactTestRenderer.unstable_yield('Yield:' + renderTime);
838 + Scheduler.advanceTime(renderTime);
839 + Scheduler.yieldValue('Yield:' + renderTime);
840 return null;
841 };
842
@@ -838,10 +845,8 @@ describe('Profiler', () => {
845 state = {renderTime: 1};
846 render() {
847 first = this;
841 - advanceTimeBy(this.state.renderTime);
842 - ReactTestRenderer.unstable_yield(
843 - 'FirstComponent:' + this.state.renderTime,
844 - );
848 + Scheduler.advanceTime(this.state.renderTime);
849 + Scheduler.yieldValue('FirstComponent:' + this.state.renderTime);
850 return <Yield renderTime={4} />;
851 }
852 }
@@ -850,15 +855,13 @@ describe('Profiler', () => {
855 state = {renderTime: 2};
856 render() {
857 second = this;
853 - advanceTimeBy(this.state.renderTime);
854 - ReactTestRenderer.unstable_yield(
855 - 'SecondComponent:' + this.state.renderTime,
856 - );
858 + Scheduler.advanceTime(this.state.renderTime);
859 + Scheduler.yieldValue('SecondComponent:' + this.state.renderTime);
860 return <Yield renderTime={7} />;
861 }
862 }
863
861 - advanceTimeBy(5); // 0 -> 5
864 + Scheduler.advanceTime(5); // 0 -> 5
865
866 const renderer = ReactTestRenderer.create(
867 <React.unstable_Profiler id="test" onRender={callback}>
@@ -886,7 +889,7 @@ describe('Profiler', () => {
889
890 callback.mockClear();
891
889 - advanceTimeBy(100); // 19 -> 119
892 + Scheduler.advanceTime(100); // 19 -> 119
893
894 // Render a partially update, but don't finish.
895 // This partial render will take 10ms of actual render time.
@@ -895,7 +898,7 @@ describe('Profiler', () => {
898 expect(callback).toHaveBeenCalledTimes(0);
899
900 // Simulate time moving forward while frame is paused.
898 - advanceTimeBy(100); // 129 -> 229
901 + Scheduler.advanceTime(100); // 129 -> 229
902
903 // Interrupt with higher priority work.
904 // This simulates a total of 37ms of actual render time.
@@ -919,7 +922,7 @@ describe('Profiler', () => {
922 callback.mockClear();
923
924 // Simulate time moving forward while frame is paused.
922 - advanceTimeBy(100); // 266 -> 366
925 + Scheduler.advanceTime(100); // 266 -> 366
926
927 // Resume the original low priority update, with rebased state.
928 // This simulates a total of 14ms of actual render time,
@@ -955,7 +958,7 @@ describe('Profiler', () => {
958 const callback = jest.fn();
959
960 const ThrowsError = () => {
958 - advanceTimeBy(3);
961 + Scheduler.advanceTime(3);
962 throw Error('expected error');
963 };
964
@@ -965,7 +968,7 @@ describe('Profiler', () => {
968 this.setState({error});
969 }
970 render() {
968 - advanceTimeBy(2);
971 + Scheduler.advanceTime(2);
972 return this.state.error === null ? (
973 this.props.children
974 ) : (
@@ -974,7 +977,7 @@ describe('Profiler', () => {
977 }
978 }
979
977 - advanceTimeBy(5); // 0 -> 5
980 + Scheduler.advanceTime(5); // 0 -> 5
981
982 ReactTestRenderer.create(
983 <React.unstable_Profiler id="test" onRender={callback}>
@@ -1034,7 +1037,7 @@ describe('Profiler', () => {
1037 const callback = jest.fn();
1038
1039 const ThrowsError = () => {
1037 - advanceTimeBy(10);
1040 + Scheduler.advanceTime(10);
1041 throw Error('expected error');
1042 };
1043
@@ -1044,7 +1047,7 @@ describe('Profiler', () => {
1047 return {error};
1048 }
1049 render() {
1047 - advanceTimeBy(2);
1050 + Scheduler.advanceTime(2);
1051 return this.state.error === null ? (
1052 this.props.children
1053 ) : (
@@ -1053,7 +1056,7 @@ describe('Profiler', () => {
1056 }
1057 }
1058
1056 - advanceTimeBy(5); // 0 -> 5
1059 + Scheduler.advanceTime(5); // 0 -> 5
1060
1061 ReactTestRenderer.create(
1062 <React.unstable_Profiler id="test" onRender={callback}>
@@ -1133,7 +1136,7 @@ describe('Profiler', () => {
1136 it('reflects the most recently rendered id value', () => {
1137 const callback = jest.fn();
1138
1136 - advanceTimeBy(5); // 0 -> 5
1139 + Scheduler.advanceTime(5); // 0 -> 5
1140
1141 const renderer = ReactTestRenderer.create(
1142 <React.unstable_Profiler id="one" onRender={callback}>
@@ -1143,7 +1146,7 @@ describe('Profiler', () => {
1146
1147 expect(callback).toHaveBeenCalledTimes(1);
1148
1146 - advanceTimeBy(20); // 7 -> 27
1149 + Scheduler.advanceTime(20); // 7 -> 27
1150
1151 renderer.update(
1152 <React.unstable_Profiler id="two" onRender={callback}>
@@ -1181,11 +1184,11 @@ describe('Profiler', () => {
1184
1185 class ClassComponent extends React.Component {
1186 componentDidMount() {
1184 - advanceTimeBy(5);
1187 + Scheduler.advanceTime(5);
1188 classComponentMounted = true;
1189 }
1190 render() {
1188 - advanceTimeBy(2);
1191 + Scheduler.advanceTime(2);
1192 return null;
1193 }
1194 }
@@ -1331,7 +1334,7 @@ describe('Profiler', () => {
1334 describe('error handling', () => {
1335 it('should cover errors thrown in onWorkScheduled', () => {
1336 function Component({children}) {
1334 - ReactTestRenderer.unstable_yield('Component:' + children);
1337 + Scheduler.yieldValue('Component:' + children);
1338 return children;
1339 }
1340
@@ -1340,11 +1343,15 @@ describe('Profiler', () => {
1343 // Errors that happen inside of a subscriber should throw,
1344 throwInOnWorkScheduled = true;
1345 expect(() => {
1343 - SchedulerTracing.unstable_trace('event', mockNow(), () => {
1344 - renderer = ReactTestRenderer.create(<Component>fail</Component>, {
1345 - unstable_isConcurrent: true,
1346 - });
1347 - });
1346 + SchedulerTracing.unstable_trace(
1347 + 'event',
1348 + Scheduler.unstable_now(),
1349 + () => {
1350 + renderer = ReactTestRenderer.create(<Component>fail</Component>, {
1351 + unstable_isConcurrent: true,
1352 + });
1353 + },
1354 + );
1355 }).toThrow('Expected error onWorkScheduled');
1356 throwInOnWorkScheduled = false;
1357 expect(onWorkScheduled).toHaveBeenCalled();
@@ -1361,16 +1368,20 @@ describe('Profiler', () => {
1368
1369 it('should cover errors thrown in onWorkStarted', () => {
1370 function Component({children}) {
1364 - ReactTestRenderer.unstable_yield('Component:' + children);
1371 + Scheduler.yieldValue('Component:' + children);
1372 return children;
1373 }
1374
1375 let renderer;
1369 - SchedulerTracing.unstable_trace('event', mockNow(), () => {
1370 - renderer = ReactTestRenderer.create(<Component>text</Component>, {
1371 - unstable_isConcurrent: true,
1372 - });
1373 - });
1376 + SchedulerTracing.unstable_trace(
1377 + 'event',
1378 + Scheduler.unstable_now(),
1379 + () => {
1380 + renderer = ReactTestRenderer.create(<Component>text</Component>, {
1381 + unstable_isConcurrent: true,
1382 + });
1383 + },
1384 + );
1385 onWorkStarted.mockClear();
1386
1387 // Errors that happen inside of a subscriber should throw,
@@ -1389,22 +1400,26 @@ describe('Profiler', () => {
1400
1401 it('should cover errors thrown in onWorkStopped', () => {
1402 function Component({children}) {
1392 - ReactTestRenderer.unstable_yield('Component:' + children);
1403 + Scheduler.yieldValue('Component:' + children);
1404 return children;
1405 }
1406
1407 let renderer;
1397 - SchedulerTracing.unstable_trace('event', mockNow(), () => {
1398 - renderer = ReactTestRenderer.create(<Component>text</Component>, {
1399 - unstable_isConcurrent: true,
1400 - });
1401 - });
1408 + SchedulerTracing.unstable_trace(
1409 + 'event',
1410 + Scheduler.unstable_now(),
1411 + () => {
1412 + renderer = ReactTestRenderer.create(<Component>text</Component>, {
1413 + unstable_isConcurrent: true,
1414 + });
1415 + },
1416 + );
1417 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
1418
1419 // Errors that happen in an on-stopped callback,
1420 throwInOnWorkStopped = true;
1421 expect(() => {
1407 - renderer.unstable_flushAll(['Component:text']);
1422 + expect(Scheduler).toFlushAndYield(['Component:text']);
1423 }).toThrow('Expected error onWorkStopped');
1424 throwInOnWorkStopped = false;
1425 expect(onWorkStopped).toHaveBeenCalledTimes(2);
@@ -1420,34 +1435,41 @@ describe('Profiler', () => {
1435
1436 it('should cover errors thrown in onInteractionScheduledWorkCompleted', () => {
1437 function Component({children}) {
1423 - ReactTestRenderer.unstable_yield('Component:' + children);
1438 + Scheduler.yieldValue('Component:' + children);
1439 return children;
1440 }
1441
1442 const eventOne = {
1443 id: 0,
1444 name: 'event one',
1430 - timestamp: mockNow(),
1445 + timestamp: Scheduler.unstable_now(),
1446 };
1447 const eventTwo = {
1448 id: 1,
1449 name: 'event two',
1435 - timestamp: mockNow(),
1450 + timestamp: Scheduler.unstable_now(),
1451 };
1452
1438 - let renderer;
1439 - SchedulerTracing.unstable_trace(eventOne.name, mockNow(), () => {
1440 - SchedulerTracing.unstable_trace(eventTwo.name, mockNow(), () => {
1441 - renderer = ReactTestRenderer.create(<Component>text</Component>, {
1442 - unstable_isConcurrent: true,
1443 - });
1444 - });
1445 - });
1453 + SchedulerTracing.unstable_trace(
1454 + eventOne.name,
1455 + Scheduler.unstable_now(),
1456 + () => {
1457 + SchedulerTracing.unstable_trace(
1458 + eventTwo.name,
1459 + Scheduler.unstable_now(),
1460 + () => {
1461 + ReactTestRenderer.create(<Component>text</Component>, {
1462 + unstable_isConcurrent: true,
1463 + });
1464 + },
1465 + );
1466 + },
1467 + );
1468 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
1469
1470 throwInOnInteractionScheduledWorkCompleted = true;
1471 expect(() => {
1450 - renderer.unstable_flushAll(['Component:text']);
1472 + expect(Scheduler).toFlushAndYield(['Component:text']);
1473 }).toThrow('Expected error onInteractionScheduledWorkCompleted');
1474
1475 // Even though an error is thrown for one completed interaction,
@@ -1465,7 +1487,7 @@ describe('Profiler', () => {
1487 });
1488
1489 let interaction;
1468 - SchedulerTracing.unstable_trace('event', mockNow(), () => {
1490 + SchedulerTracing.unstable_trace('event', Scheduler.unstable_now(), () => {
1491 const interactions = SchedulerTracing.unstable_getCurrent();
1492 expect(interactions.size).toBe(1);
1493 interaction = Array.from(interactions)[0];
@@ -1488,8 +1510,8 @@ describe('Profiler', () => {
1510 let instance = null;
1511
1512 const Yield = ({duration = 10, value}) => {
1491 - advanceTimeBy(duration);
1492 - ReactTestRenderer.unstable_yield(value);
1513 + Scheduler.advanceTime(duration);
1514 + Scheduler.yieldValue(value);
1515 return null;
1516 };
1517
@@ -1509,19 +1531,19 @@ describe('Profiler', () => {
1531 }
1532 }
1533
1512 - advanceTimeBy(1);
1534 + Scheduler.advanceTime(1);
1535
1536 const interactionCreation = {
1537 id: 0,
1538 name: 'creation event',
1517 - timestamp: mockNow(),
1539 + timestamp: Scheduler.unstable_now(),
1540 };
1541
1542 const onRender = jest.fn();
1543 let renderer;
1544 SchedulerTracing.unstable_trace(
1545 interactionCreation.name,
1524 - mockNow(),
1546 + Scheduler.unstable_now(),
1547 () => {
1548 renderer = ReactTestRenderer.create(
1549 <React.unstable_Profiler id="test-profiler" onRender={onRender}>
@@ -1555,11 +1577,11 @@ describe('Profiler', () => {
1577 expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
1578
1579 // Mount
1558 - renderer.unstable_flushAll(['first', 'last']);
1580 + expect(Scheduler).toFlushAndYield(['first', 'last']);
1581 expect(onRender).toHaveBeenCalledTimes(1);
1582 let call = onRender.mock.calls[0];
1583 expect(call[0]).toEqual('test-profiler');
1562 - expect(call[5]).toEqual(mockNow());
1584 + expect(call[5]).toEqual(Scheduler.unstable_now());
1585 if (ReactFeatureFlags.enableSchedulerTracing) {
1586 expect(call[6]).toMatchInteractions([interactionCreation]);
1587 }
@@ -1583,70 +1605,74 @@ describe('Profiler', () => {
1605 onWorkStarted.mockClear();
1606 onWorkStopped.mockClear();
1607
1586 - advanceTimeBy(3);
1608 + Scheduler.advanceTime(3);
1609
1610 let didRunCallback = false;
1611
1612 const interactionOne = {
1613 id: 1,
1614 name: 'initial event',
1593 - timestamp: mockNow(),
1615 + timestamp: Scheduler.unstable_now(),
1616 };
1595 - SchedulerTracing.unstable_trace(interactionOne.name, mockNow(), () => {
1596 - instance.setState({count: 1});
1597 -
1598 - // Update state again to verify our traced interaction isn't registered twice
1599 - instance.setState({count: 2});
1600 -
1601 - // The scheduler/tracing package will notify of work started for the default thread,
1602 - // But React shouldn't notify until it's been flushed.
1603 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
1604 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1605 -
1606 - // Work may have been scheduled multiple times.
1607 - // We only care that the subscriber was notified at least once.
1608 - // As for the thread ID- the actual value isn't important, only that there was one.
1609 - expect(onWorkScheduled).toHaveBeenCalled();
1610 - expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
1611 - interactionOne,
1612 - ]);
1613 - expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
1614 -
1615 - expect(renderer).toFlushAndYieldThrough(['first']);
1616 - expect(onRender).not.toHaveBeenCalled();
1617 -
1618 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1619 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1620 - interactionOne,
1621 - );
1622 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1623 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1624 - expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions(
1625 - [interactionOne],
1626 - );
1627 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1617 + SchedulerTracing.unstable_trace(
1618 + interactionOne.name,
1619 + Scheduler.unstable_now(),
1620 + () => {
1621 + instance.setState({count: 1});
1622 +
1623 + // Update state again to verify our traced interaction isn't registered twice
1624 + instance.setState({count: 2});
1625 +
1626 + // The scheduler/tracing package will notify of work started for the default thread,
1627 + // But React shouldn't notify until it's been flushed.
1628 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
1629 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1630 +
1631 + // Work may have been scheduled multiple times.
1632 + // We only care that the subscriber was notified at least once.
1633 + // As for the thread ID- the actual value isn't important, only that there was one.
1634 + expect(onWorkScheduled).toHaveBeenCalled();
1635 + expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
1636 + interactionOne,
1637 + ]);
1638 + expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
1639
1629 - renderer.unstable_flushAll(['last']);
1630 - expect(onRender).toHaveBeenCalledTimes(1);
1640 + expect(renderer).toFlushAndYieldThrough(['first']);
1641 + expect(onRender).not.toHaveBeenCalled();
1642
1632 - call = onRender.mock.calls[0];
1633 - expect(call[0]).toEqual('test-profiler');
1634 - expect(call[5]).toEqual(mockNow());
1635 - if (ReactFeatureFlags.enableSchedulerTracing) {
1636 - expect(call[6]).toMatchInteractions([interactionOne]);
1637 - }
1643 + expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1644 + expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1645 + interactionOne,
1646 + );
1647 + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1648 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1649 + expect(
1650 + getWorkForReactThreads(onWorkStarted)[0][0],
1651 + ).toMatchInteractions([interactionOne]);
1652 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1653 +
1654 + expect(Scheduler).toFlushAndYield(['last']);
1655 + expect(onRender).toHaveBeenCalledTimes(1);
1656 +
1657 + call = onRender.mock.calls[0];
1658 + expect(call[0]).toEqual('test-profiler');
1659 + expect(call[5]).toEqual(Scheduler.unstable_now());
1660 + if (ReactFeatureFlags.enableSchedulerTracing) {
1661 + expect(call[6]).toMatchInteractions([interactionOne]);
1662 + }
1663
1639 - didRunCallback = true;
1664 + didRunCallback = true;
1665
1641 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1642 - expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions(
1643 - [interactionOne],
1644 - );
1645 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
1646 - expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions(
1647 - [interactionOne],
1648 - );
1649 - });
1666 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1667 + expect(
1668 + getWorkForReactThreads(onWorkStarted)[0][0],
1669 + ).toMatchInteractions([interactionOne]);
1670 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
1671 + expect(
1672 + getWorkForReactThreads(onWorkStopped)[0][0],
1673 + ).toMatchInteractions([interactionOne]);
1674 + },
1675 + );
1676
1677 expect(didRunCallback).toBe(true);
1678
@@ -1655,16 +1681,16 @@ describe('Profiler', () => {
1681 onWorkStarted.mockClear();
1682 onWorkStopped.mockClear();
1683
1658 - advanceTimeBy(17);
1684 + Scheduler.advanceTime(17);
1685
1686 // Verify that updating state again does not re-log our interaction.
1687 instance.setState({count: 3});
1662 - renderer.unstable_flushAll(['first', 'last']);
1688 + expect(Scheduler).toFlushAndYield(['first', 'last']);
1689
1690 expect(onRender).toHaveBeenCalledTimes(1);
1691 call = onRender.mock.calls[0];
1692 expect(call[0]).toEqual('test-profiler');
1667 - expect(call[5]).toEqual(mockNow());
1693 + expect(call[5]).toEqual(Scheduler.unstable_now());
1694 if (ReactFeatureFlags.enableSchedulerTracing) {
1695 expect(call[6]).toMatchInteractions([]);
1696 }
@@ -1679,21 +1705,25 @@ describe('Profiler', () => {
1705
1706 onRender.mockClear();
1707
1682 - advanceTimeBy(3);
1708 + Scheduler.advanceTime(3);
1709
1710 // Verify that root updates are also associated with traced events.
1711 const interactionTwo = {
1712 id: 2,
1713 name: 'root update event',
1688 - timestamp: mockNow(),
1714 + timestamp: Scheduler.unstable_now(),
1715 };
1690 - SchedulerTracing.unstable_trace(interactionTwo.name, mockNow(), () => {
1691 - renderer.update(
1692 - <React.unstable_Profiler id="test-profiler" onRender={onRender}>
1693 - <Example />
1694 - </React.unstable_Profiler>,
1695 - );
1696 - });
1716 + SchedulerTracing.unstable_trace(
1717 + interactionTwo.name,
1718 + Scheduler.unstable_now(),
1719 + () => {
1720 + renderer.update(
1721 + <React.unstable_Profiler id="test-profiler" onRender={onRender}>
1722 + <Example />
1723 + </React.unstable_Profiler>,
1724 + );
1725 + },
1726 + );
1727
1728 expect(onInteractionTraced).toHaveBeenCalledTimes(3);
1729 expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
@@ -1715,12 +1745,12 @@ describe('Profiler', () => {
1745 ]);
1746 expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
1747
1718 - renderer.unstable_flushAll(['first', 'last']);
1748 + expect(Scheduler).toFlushAndYield(['first', 'last']);
1749
1750 expect(onRender).toHaveBeenCalledTimes(1);
1751 call = onRender.mock.calls[0];
1752 expect(call[0]).toEqual('test-profiler');
1723 - expect(call[5]).toEqual(mockNow());
1753 + expect(call[5]).toEqual(Scheduler.unstable_now());
1754 if (ReactFeatureFlags.enableSchedulerTracing) {
1755 expect(call[6]).toMatchInteractions([interactionTwo]);
1756 }
@@ -1748,7 +1778,7 @@ describe('Profiler', () => {
1778 state = {count: 0};
1779 render() {
1780 first = this;
1751 - ReactTestRenderer.unstable_yield('FirstComponent');
1781 + Scheduler.yieldValue('FirstComponent');
1782 return null;
1783 }
1784 }
@@ -1757,12 +1787,12 @@ describe('Profiler', () => {
1787 state = {count: 0};
1788 render() {
1789 second = this;
1760 - ReactTestRenderer.unstable_yield('SecondComponent');
1790 + Scheduler.yieldValue('SecondComponent');
1791 return null;
1792 }
1793 }
1794
1765 - advanceTimeBy(5);
1795 + Scheduler.advanceTime(5);
1796
1797 const renderer = ReactTestRenderer.create(
1798 <React.unstable_Profiler id="test" onRender={onRender}>
@@ -1773,128 +1803,132 @@ describe('Profiler', () => {
1803 );
1804
1805 // Initial mount.
1776 - renderer.unstable_flushAll(['FirstComponent', 'SecondComponent']);
1806 + expect(Scheduler).toFlushAndYield(['FirstComponent', 'SecondComponent']);
1807
1808 expect(onInteractionTraced).not.toHaveBeenCalled();
1809 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
1810
1811 onRender.mockClear();
1812
1783 - advanceTimeBy(100);
1813 + Scheduler.advanceTime(100);
1814
1815 const interactionLowPri = {
1816 id: 0,
1817 name: 'lowPri',
1788 - timestamp: mockNow(),
1818 + timestamp: Scheduler.unstable_now(),
1819 };
1820
1791 - SchedulerTracing.unstable_trace(interactionLowPri.name, mockNow(), () => {
1792 - // Render a partially update, but don't finish.
1793 - first.setState({count: 1});
1794 -
1795 - expect(onWorkScheduled).toHaveBeenCalled();
1796 - expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
1797 - interactionLowPri,
1798 - ]);
1799 -
1800 - expect(renderer).toFlushAndYieldThrough(['FirstComponent']);
1801 - expect(onRender).not.toHaveBeenCalled();
1802 -
1803 - expect(onInteractionTraced).toHaveBeenCalledTimes(1);
1804 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1805 - interactionLowPri,
1806 - );
1807 - expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
1808 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1809 - expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions(
1810 - [interactionLowPri],
1811 - );
1812 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1813 -
1814 - advanceTimeBy(100);
1815 -
1816 - const interactionHighPri = {
1817 - id: 1,
1818 - name: 'highPri',
1819 - timestamp: mockNow(),
1820 - };
1821 + SchedulerTracing.unstable_trace(
1822 + interactionLowPri.name,
1823 + Scheduler.unstable_now(),
1824 + () => {
1825 + // Render a partially update, but don't finish.
1826 + first.setState({count: 1});
1827
1822 - // Interrupt with higher priority work.
1823 - // This simulates a total of 37ms of actual render time.
1824 - renderer.unstable_flushSync(() => {
1825 - SchedulerTracing.unstable_trace(
1826 - interactionHighPri.name,
1827 - mockNow(),
1828 - () => {
1829 - second.setState({count: 1});
1828 + expect(onWorkScheduled).toHaveBeenCalled();
1829 + expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
1830 + interactionLowPri,
1831 + ]);
1832
1831 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1832 - expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1833 - interactionHighPri,
1834 - );
1835 - expect(
1836 - onInteractionScheduledWorkCompleted,
1837 - ).not.toHaveBeenCalled();
1833 + expect(renderer).toFlushAndYieldThrough(['FirstComponent']);
1834 + expect(onRender).not.toHaveBeenCalled();
1835
1839 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1840 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1841 - },
1836 + expect(onInteractionTraced).toHaveBeenCalledTimes(1);
1837 + expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1838 + interactionLowPri,
1839 );
1843 - });
1844 - expect(ReactTestRenderer).toHaveYielded(['SecondComponent']);
1845 -
1846 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1847 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1848 - expect(
1849 - onInteractionScheduledWorkCompleted,
1850 - ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri);
1840 + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
1841 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1842 + expect(
1843 + getWorkForReactThreads(onWorkStarted)[0][0],
1844 + ).toMatchInteractions([interactionLowPri]);
1845 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1846 +
1847 + Scheduler.advanceTime(100);
1848 +
1849 + const interactionHighPri = {
1850 + id: 1,
1851 + name: 'highPri',
1852 + timestamp: Scheduler.unstable_now(),
1853 + };
1854
1852 - // Verify the high priority update was associated with the high priority event.
1853 - expect(onRender).toHaveBeenCalledTimes(1);
1854 - let call = onRender.mock.calls[0];
1855 - expect(call[0]).toEqual('test');
1856 - expect(call[5]).toEqual(mockNow());
1857 - expect(call[6]).toMatchInteractions(
1858 - ReactFeatureFlags.enableSchedulerTracing
1859 - ? [interactionLowPri, interactionHighPri]
1860 - : [],
1861 - );
1855 + // Interrupt with higher priority work.
1856 + // This simulates a total of 37ms of actual render time.
1857 + renderer.unstable_flushSync(() => {
1858 + SchedulerTracing.unstable_trace(
1859 + interactionHighPri.name,
1860 + Scheduler.unstable_now(),
1861 + () => {
1862 + second.setState({count: 1});
1863 +
1864 + expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1865 + expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
1866 + interactionHighPri,
1867 + );
1868 + expect(
1869 + onInteractionScheduledWorkCompleted,
1870 + ).not.toHaveBeenCalled();
1871
1863 - onRender.mockClear();
1872 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
1873 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1874 + },
1875 + );
1876 + });
1877 + expect(ReactTestRenderer).toHaveYielded(['SecondComponent']);
1878 +
1879 + expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1880 + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1881 + expect(
1882 + onInteractionScheduledWorkCompleted,
1883 + ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri);
1884 +
1885 + // Verify the high priority update was associated with the high priority event.
1886 + expect(onRender).toHaveBeenCalledTimes(1);
1887 + let call = onRender.mock.calls[0];
1888 + expect(call[0]).toEqual('test');
1889 + expect(call[5]).toEqual(Scheduler.unstable_now());
1890 + expect(call[6]).toMatchInteractions(
1891 + ReactFeatureFlags.enableSchedulerTracing
1892 + ? [interactionLowPri, interactionHighPri]
1893 + : [],
1894 + );
1895
1865 - advanceTimeBy(100);
1896 + onRender.mockClear();
1897
1867 - // Resume the original low priority update, with rebased state.
1868 - // Verify the low priority update was retained.
1869 - renderer.unstable_flushAll(['FirstComponent']);
1870 - expect(onRender).toHaveBeenCalledTimes(1);
1871 - call = onRender.mock.calls[0];
1872 - expect(call[0]).toEqual('test');
1873 - expect(call[5]).toEqual(mockNow());
1874 - expect(call[6]).toMatchInteractions(
1875 - ReactFeatureFlags.enableSchedulerTracing ? [interactionLowPri] : [],
1876 - );
1898 + Scheduler.advanceTime(100);
1899
1878 - expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1879 - expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1900 + // Resume the original low priority update, with rebased state.
1901 + // Verify the low priority update was retained.
1902 + expect(Scheduler).toFlushAndYield(['FirstComponent']);
1903 + expect(onRender).toHaveBeenCalledTimes(1);
1904 + call = onRender.mock.calls[0];
1905 + expect(call[0]).toEqual('test');
1906 + expect(call[5]).toEqual(Scheduler.unstable_now());
1907 + expect(call[6]).toMatchInteractions(
1908 + ReactFeatureFlags.enableSchedulerTracing ? [interactionLowPri] : [],
1909 + );
1910
1881 - // Work might be started multiple times before being completed.
1882 - // This is okay; it's part of the scheduler/tracing contract.
1883 - expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
1884 - expect(getWorkForReactThreads(onWorkStarted)[1][0]).toMatchInteractions(
1885 - [interactionLowPri, interactionHighPri],
1886 - );
1887 - expect(getWorkForReactThreads(onWorkStarted)[2][0]).toMatchInteractions(
1888 - [interactionLowPri],
1889 - );
1890 - expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
1891 - expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions(
1892 - [interactionLowPri, interactionHighPri],
1893 - );
1894 - expect(getWorkForReactThreads(onWorkStopped)[1][0]).toMatchInteractions(
1895 - [interactionLowPri],
1896 - );
1897 - });
1911 + expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1912 + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
1913 +
1914 + // Work might be started multiple times before being completed.
1915 + // This is okay; it's part of the scheduler/tracing contract.
1916 + expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
1917 + expect(
1918 + getWorkForReactThreads(onWorkStarted)[1][0],
1919 + ).toMatchInteractions([interactionLowPri, interactionHighPri]);
1920 + expect(
1921 + getWorkForReactThreads(onWorkStarted)[2][0],
1922 + ).toMatchInteractions([interactionLowPri]);
1923 + expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
1924 + expect(
1925 + getWorkForReactThreads(onWorkStopped)[0][0],
1926 + ).toMatchInteractions([interactionLowPri, interactionHighPri]);
1927 + expect(
1928 + getWorkForReactThreads(onWorkStopped)[1][0],
1929 + ).toMatchInteractions([interactionLowPri]);
1930 + },
1931 + );
1932
1933 expect(onInteractionTraced).toHaveBeenCalledTimes(2);
1934 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
@@ -1912,18 +1946,18 @@ describe('Profiler', () => {
1946 count: 0,
1947 };
1948 componentDidMount() {
1915 - advanceTimeBy(10); // Advance timer to keep commits separate
1949 + Scheduler.advanceTime(10); // Advance timer to keep commits separate
1950 this.setState({count: 1}); // Intentional cascading update
1951 }
1952 componentDidUpdate(prevProps, prevState) {
1953 if (this.state.count === 2 && prevState.count === 1) {
1920 - advanceTimeBy(10); // Advance timer to keep commits separate
1954 + Scheduler.advanceTime(10); // Advance timer to keep commits separate
1955 this.setState({count: 3}); // Intentional cascading update
1956 }
1957 }
1958 render() {
1959 instance = this;
1926 - ReactTestRenderer.unstable_yield('Example:' + this.state.count);
1960 + Scheduler.yieldValue('Example:' + this.state.count);
1961 return null;
1962 }
1963 }
@@ -1931,21 +1965,24 @@ describe('Profiler', () => {
1965 const interactionOne = {
1966 id: 0,
1967 name: 'componentDidMount test',
1934 - timestamp: mockNow(),
1968 + timestamp: Scheduler.unstable_now(),
1969 };
1970
1971 // Initial mount.
1972 const onRender = jest.fn();
1939 - let firstCommitTime = mockNow();
1940 - let renderer;
1941 - SchedulerTracing.unstable_trace(interactionOne.name, mockNow(), () => {
1942 - renderer = ReactTestRenderer.create(
1943 - <React.unstable_Profiler id="test" onRender={onRender}>
1944 - <Example />
1945 - </React.unstable_Profiler>,
1946 - {unstable_isConcurrent: true},
1947 - );
1948 - });
1973 + let firstCommitTime = Scheduler.unstable_now();
1974 + SchedulerTracing.unstable_trace(
1975 + interactionOne.name,
1976 + Scheduler.unstable_now(),
1977 + () => {
1978 + ReactTestRenderer.create(
1979 + <React.unstable_Profiler id="test" onRender={onRender}>
1980 + <Example />
1981 + </React.unstable_Profiler>,
1982 + {unstable_isConcurrent: true},
1983 + );
1984 + },
1985 + );
1986
1987 expect(onInteractionTraced).toHaveBeenCalledTimes(1);
1988 expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
@@ -1955,7 +1992,7 @@ describe('Profiler', () => {
1992 expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
1993 expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
1994
1958 - renderer.unstable_flushAll(['Example:0', 'Example:1']);
1995 + expect(Scheduler).toFlushAndYield(['Example:0', 'Example:1']);
1996
1997 expect(onInteractionTraced).toHaveBeenCalledTimes(1);
1998 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
@@ -1986,7 +2023,7 @@ describe('Profiler', () => {
2023 );
2024 call = onRender.mock.calls[1];
2025 expect(call[0]).toEqual('test');
1989 - expect(call[5]).toEqual(mockNow());
2026 + expect(call[5]).toEqual(Scheduler.unstable_now());
2027 expect(call[6]).toMatchInteractions(
2028 ReactFeatureFlags.enableSchedulerTracing ? [interactionOne] : [],
2029 );
@@ -1996,13 +2033,17 @@ describe('Profiler', () => {
2033 const interactionTwo = {
2034 id: 1,
2035 name: 'componentDidUpdate test',
1999 - timestamp: mockNow(),
2036 + timestamp: Scheduler.unstable_now(),
2037 };
2038
2039 // Cause an traced, async update
2003 - SchedulerTracing.unstable_trace(interactionTwo.name, mockNow(), () => {
2004 - instance.setState({count: 2});
2005 - });
2040 + SchedulerTracing.unstable_trace(
2041 + interactionTwo.name,
2042 + Scheduler.unstable_now(),
2043 + () => {
2044 + instance.setState({count: 2});
2045 + },
2046 + );
2047 expect(onRender).not.toHaveBeenCalled();
2048 expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2049 expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
@@ -2012,12 +2053,12 @@ describe('Profiler', () => {
2053 expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(2);
2054 expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
2055
2015 - advanceTimeBy(5);
2056 + Scheduler.advanceTime(5);
2057
2058 // Flush async work (outside of traced scope)
2059 // This will cause an intentional cascading update from did-update
2019 - firstCommitTime = mockNow();
2020 - renderer.unstable_flushAll(['Example:2', 'Example:3']);
2060 + firstCommitTime = Scheduler.unstable_now();
2061 + expect(Scheduler).toFlushAndYield(['Example:2', 'Example:3']);
2062
2063 expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2064 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
@@ -2049,7 +2090,7 @@ describe('Profiler', () => {
2090 );
2091 call = onRender.mock.calls[1];
2092 expect(call[0]).toEqual('test');
2052 - expect(call[5]).toEqual(mockNow());
2093 + expect(call[5]).toEqual(Scheduler.unstable_now());
2094 expect(call[6]).toMatchInteractions(
2095 ReactFeatureFlags.enableSchedulerTracing ? [interactionTwo] : [],
2096 );
@@ -2059,16 +2100,20 @@ describe('Profiler', () => {
2100 const interactionThree = {
2101 id: 2,
2102 name: 'setState callback test',
2062 - timestamp: mockNow(),
2103 + timestamp: Scheduler.unstable_now(),
2104 };
2105
2106 // Cause a cascading update from the setState callback
2107 function callback() {
2108 instance.setState({count: 6});
2109 }
2069 - SchedulerTracing.unstable_trace(interactionThree.name, mockNow(), () => {
2070 - instance.setState({count: 5}, callback);
2071 - });
2110 + SchedulerTracing.unstable_trace(
2111 + interactionThree.name,
2112 + Scheduler.unstable_now(),
2113 + () => {
2114 + instance.setState({count: 5}, callback);
2115 + },
2116 + );
2117 expect(onRender).not.toHaveBeenCalled();
2118
2119 expect(onInteractionTraced).toHaveBeenCalledTimes(3);
@@ -2081,8 +2126,8 @@ describe('Profiler', () => {
2126
2127 // Flush async work (outside of traced scope)
2128 // This will cause an intentional cascading update from the setState callback
2084 - firstCommitTime = mockNow();
2085 - renderer.unstable_flushAll(['Example:5', 'Example:6']);
2129 + firstCommitTime = Scheduler.unstable_now();
2130 + expect(Scheduler).toFlushAndYield(['Example:5', 'Example:6']);
2131
2132 expect(onInteractionTraced).toHaveBeenCalledTimes(3);
2133 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(3);
@@ -2114,7 +2159,7 @@ describe('Profiler', () => {
2159 );
2160 call = onRender.mock.calls[1];
2161 expect(call[0]).toEqual('test');
2117 - expect(call[5]).toEqual(mockNow());
2162 + expect(call[5]).toEqual(Scheduler.unstable_now());
2163 expect(call[6]).toMatchInteractions(
2164 ReactFeatureFlags.enableSchedulerTracing ? [interactionThree] : [],
2165 );
@@ -2126,7 +2171,7 @@ describe('Profiler', () => {
2171
2172 class Child extends React.Component {
2173 render() {
2129 - ReactTestRenderer.unstable_yield('Child:', this.props.count);
2174 + Scheduler.yieldValue('Child:' + this.props.count);
2175 return null;
2176 }
2177 }
@@ -2145,23 +2190,27 @@ describe('Profiler', () => {
2190 }
2191 }
2192
2148 - advanceTimeBy(1);
2193 + Scheduler.advanceTime(1);
2194
2150 - const renderer = ReactTestRenderer.create(<Parent />, {
2195 + ReactTestRenderer.create(<Parent />, {
2196 unstable_isConcurrent: true,
2197 });
2153 - renderer.unstable_flushAll(['Child:0']);
2198 + expect(Scheduler).toFlushAndYield(['Child:0']);
2199 onRender.mockClear();
2200
2201 const interaction = {
2202 id: 0,
2203 name: 'parent interaction',
2159 - timestamp: mockNow(),
2204 + timestamp: Scheduler.unstable_now(),
2205 };
2206
2162 - SchedulerTracing.unstable_trace(interaction.name, mockNow(), () => {
2163 - parentInstance.setState({count: 1});
2164 - });
2207 + SchedulerTracing.unstable_trace(
2208 + interaction.name,
2209 + Scheduler.unstable_now(),
2210 + () => {
2211 + parentInstance.setState({count: 1});
2212 + },
2213 + );
2214
2215 expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2216 expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
@@ -2172,7 +2221,7 @@ describe('Profiler', () => {
2221 expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2222
2223 expect(onRender).not.toHaveBeenCalled();
2175 - renderer.unstable_flushAll(['Child:1']);
2224 + expect(Scheduler).toFlushAndYield(['Child:1']);
2225 expect(onRender).toHaveBeenCalledTimes(1);
2226 let call = onRender.mock.calls[0];
2227 expect(call[0]).toEqual('test-profiler');
@@ -2209,7 +2258,7 @@ describe('Profiler', () => {
2258 const interaction = {
2259 id: 0,
2260 name: 'initial render',
2212 - timestamp: mockNow(),
2261 + timestamp: Scheduler.unstable_now(),
2262 };
2263
2264 const monkey = React.createRef();
@@ -2221,17 +2270,21 @@ describe('Profiler', () => {
2270 }
2271
2272 const onRender = jest.fn();
2224 - SchedulerTracing.unstable_trace(interaction.name, mockNow(), () => {
2225 - ReactNoop.render(
2226 - <React.unstable_Profiler id="test-profiler" onRender={onRender}>
2227 - <React.Suspense fallback={<Text text="Loading..." />}>
2228 - <AsyncText text="Async" ms={20000} />
2229 - </React.Suspense>
2230 - <Text text="Sync" />
2231 - <Monkey ref={monkey} />
2232 - </React.unstable_Profiler>,
2233 - );
2234 - });
2273 + SchedulerTracing.unstable_trace(
2274 + interaction.name,
2275 + Scheduler.unstable_now(),
2276 + () => {
2277 + ReactNoop.render(
2278 + <React.unstable_Profiler id="test-profiler" onRender={onRender}>
2279 + <React.Suspense fallback={<Text text="Loading..." />}>
2280 + <AsyncText text="Async" ms={20000} />
2281 + </React.Suspense>
2282 + <Text text="Sync" />
2283 + <Monkey ref={monkey} />
2284 + </React.unstable_Profiler>,
2285 + );
2286 + },
2287 + );
2288
2289 expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2290 expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
@@ -2300,7 +2353,7 @@ describe('Profiler', () => {
2353 const interaction = {
2354 id: 0,
2355 name: 'initial render',
2303 - timestamp: mockNow(),
2356 + timestamp: Scheduler.unstable_now(),
2357 };
2358
2359 const onRender = jest.fn();
@@ -2354,7 +2407,7 @@ describe('Profiler', () => {
2407 const interaction = {
2408 id: 0,
2409 name: 'initial render',
2357 - timestamp: mockNow(),
2410 + timestamp: Scheduler.unstable_now(),
2411 };
2412
2413 const onRender = jest.fn();
@@ -2393,7 +2446,7 @@ describe('Profiler', () => {
2446 const interaction = {
2447 id: 0,
2448 name: 'initial render',
2396 - timestamp: mockNow(),
2449 + timestamp: Scheduler.unstable_now(),
2450 };
2451
2452 const onRender = jest.fn();
@@ -2417,7 +2470,7 @@ describe('Profiler', () => {
2470 },
2471 );
2472
2420 - advanceTimeBy(1500);
2473 + Scheduler.advanceTime(1500);
2474 await awaitableAdvanceTimers(1500);
2475
2476 expect(renderer).toFlushAndYield([
@@ -2426,7 +2479,7 @@ describe('Profiler', () => {
2479 ]);
2480 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2481
2429 - advanceTimeBy(2500);
2482 + Scheduler.advanceTime(2500);
2483 await awaitableAdvanceTimers(2500);
2484
2485 expect(ReactTestRenderer).toHaveYielded(['Promise resolved [loaded]']);
@@ -2441,16 +2494,15 @@ describe('Profiler', () => {
2494 const interaction = {
2495 id: 0,
2496 name: 'initial render',
2444 - timestamp: mockNow(),
2497 + timestamp: Scheduler.unstable_now(),
2498 };
2499
2500 const onRender = jest.fn();
2448 - let renderer;
2501 SchedulerTracing.unstable_trace(
2502 interaction.name,
2503 interaction.timestamp,
2504 () => {
2453 - renderer = ReactTestRenderer.create(
2505 + ReactTestRenderer.create(
2506 <React.unstable_Profiler id="app" onRender={onRender}>
2507 <React.Suspense
2508 maxDuration={2000}
@@ -2462,13 +2514,17 @@ describe('Profiler', () => {
2514 );
2515 },
2516 );
2465 - renderer.unstable_flushAll();
2517 + expect(Scheduler).toFlushAndYield([
2518 + 'Suspend [loaded]',
2519 + 'Text [loading]',
2520 + ]);
2521
2522 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2523
2524 jest.advanceTimersByTime(1000);
2525 await resourcePromise;
2471 - renderer.unstable_flushAll();
2526 + expect(Scheduler).toHaveYielded(['Promise resolved [loaded]']);
2527 + expect(Scheduler).toFlushAndYield(['AsyncText [loaded]']);
2528
2529 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
2530 expect(
@@ -2480,7 +2536,7 @@ describe('Profiler', () => {
2536 const initialRenderInteraction = {
2537 id: 0,
2538 name: 'initial render',
2483 - timestamp: mockNow(),
2539 + timestamp: Scheduler.unstable_now(),
2540 };
2541
2542 const onRender = jest.fn();
@@ -2513,7 +2569,7 @@ describe('Profiler', () => {
2569 const highPriUpdateInteraction = {
2570 id: 1,
2571 name: 'hiPriUpdate',
2516 - timestamp: mockNow(),
2572 + timestamp: Scheduler.unstable_now(),
2573 };
2574
2575 const originalPromise = resourcePromise;
@@ -2546,7 +2602,7 @@ describe('Profiler', () => {
2602
2603 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2604
2549 - advanceTimeBy(1000);
2605 + Scheduler.advanceTime(1000);
2606 jest.advanceTimersByTime(1000);
2607 await originalPromise;
2608 expect(renderer.toJSON()).toEqual(['loaded', 'updated']);
@@ -2566,7 +2622,7 @@ describe('Profiler', () => {
2622 const initialRenderInteraction = {
2623 id: 0,
2624 name: 'initial render',
2569 - timestamp: mockNow(),
2625 + timestamp: Scheduler.unstable_now(),
2626 };
2627
2628 const onRender = jest.fn();
@@ -2597,13 +2653,13 @@ describe('Profiler', () => {
2653 expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2654 expect(onRender).not.toHaveBeenCalled();
2655
2600 - advanceTimeBy(500);
2656 + Scheduler.advanceTime(500);
2657 jest.advanceTimersByTime(500);
2658
2659 const highPriUpdateInteraction = {
2660 id: 1,
2661 name: 'hiPriUpdate',
2606 - timestamp: mockNow(),
2662 + timestamp: Scheduler.unstable_now(),
2663 };
2664
2665 const originalPromise = resourcePromise;
@@ -2641,7 +2697,7 @@ describe('Profiler', () => {
2697
2698 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);
2699
2644 - advanceTimeBy(500);
2700 + Scheduler.advanceTime(500);
2701 jest.advanceTimersByTime(500);
2702 await originalPromise;
2703 expect(ReactTestRenderer).toHaveYielded(['Promise resolved [loaded]']);
packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js
+9 -20
@@ -14,11 +14,10 @@ describe('ReactProfiler DevTools integration', () => {
14 let React;
15 let ReactFeatureFlags;
16 let ReactTestRenderer;
17 + let Scheduler;
18 let SchedulerTracing;
19 let AdvanceTime;
19 - let advanceTimeBy;
20 let hook;
21 - let mockNow;
21
22 beforeEach(() => {
23 global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook = {
@@ -30,24 +29,14 @@ describe('ReactProfiler DevTools integration', () => {
29
30 jest.resetModules();
31
33 - let currentTime = 0;
34 -
35 - mockNow = jest.fn().mockImplementation(() => currentTime);
36 -
37 - global.Date.now = mockNow;
38 -
32 ReactFeatureFlags = require('shared/ReactFeatureFlags');
33 ReactFeatureFlags.enableProfilerTimer = true;
34 ReactFeatureFlags.enableSchedulerTracing = true;
35 + Scheduler = require('scheduler');
36 SchedulerTracing = require('scheduler/tracing');
37 React = require('react');
38 ReactTestRenderer = require('react-test-renderer');
39
46 - ReactTestRenderer.unstable_setNowImplementation(mockNow);
47 - advanceTimeBy = amount => {
48 - currentTime += amount;
49 - };
50 -
40 AdvanceTime = class extends React.Component {
41 static defaultProps = {
42 byAmount: 10,
@@ -58,7 +47,7 @@ describe('ReactProfiler DevTools integration', () => {
47 }
48 render() {
49 // Simulate time passing when this component is rendered
61 - advanceTimeBy(this.props.byAmount);
50 + Scheduler.advanceTime(this.props.byAmount);
51 return this.props.children || null;
52 }
53 };
@@ -66,7 +55,7 @@ describe('ReactProfiler DevTools integration', () => {
55
56 it('should auto-Profile all fibers if the DevTools hook is detected', () => {
57 const App = ({multiplier}) => {
69 - advanceTimeBy(2);
58 + Scheduler.advanceTime(2);
59 return (
60 <React.unstable_Profiler id="Profiler" onRender={onRender}>
61 <AdvanceTime byAmount={3 * multiplier} shouldComponentUpdate={true} />
@@ -132,7 +121,7 @@ describe('ReactProfiler DevTools integration', () => {
121 });
122
123 it('should reset the fiber stack correctly after an error when profiling host roots', () => {
135 - advanceTimeBy(20);
124 + Scheduler.advanceTime(20);
125
126 const rendered = ReactTestRenderer.create(
127 <div>
@@ -140,7 +129,7 @@ describe('ReactProfiler DevTools integration', () => {
129 </div>,
130 );
131
143 - advanceTimeBy(20);
132 + Scheduler.advanceTime(20);
133
134 expect(() => {
135 rendered.update(
@@ -150,7 +139,7 @@ describe('ReactProfiler DevTools integration', () => {
139 );
140 }).toThrow();
141
153 - advanceTimeBy(20);
142 + Scheduler.advanceTime(20);
143
144 // But this should render correctly, if the profiler's fiber stack has been reset.
145 rendered.update(
@@ -175,9 +164,9 @@ describe('ReactProfiler DevTools integration', () => {
164 const root = rendered.root._currentFiber().return;
165 expect(root.stateNode.memoizedInteractions).toContainNoInteractions();
166
178 - advanceTimeBy(10);
167 + Scheduler.advanceTime(10);
168
180 - const eventTime = mockNow();
169 + const eventTime = Scheduler.unstable_now();
170
171 // Render with an interaction
172 SchedulerTracing.unstable_trace('some event', eventTime, () => {
scripts/jest/matchers/reactTestMatchers.js
+2 -20
@@ -23,10 +23,7 @@ function resolveScheduler(obj) {
23 if (obj._Scheduler !== undefined) {
24 return obj._Scheduler;
25 }
26 - if (typeof obj.unstable_scheduleCallback === 'function') {
27 - return obj;
28 - }
29 - return null;
26 + return obj;
27 }
28
29 function assertYieldsWereCleared(Scheduler) {
@@ -34,48 +31,33 @@ function assertYieldsWereCleared(Scheduler) {
31 if (actualYields.length !== 0) {
32 throw new Error(
33 'Log of yielded values is not empty. ' +
37 - 'Call expect(ReactNoop).toHaveYielded(...) first.'
34 + 'Call expect(Scheduler).toHaveYielded(...) first.'
35 );
36 }
37 }
38
39 function toFlushAndYield(ReactNoop, expectedYields) {
40 const Scheduler = resolveScheduler(ReactNoop);
44 - if (Scheduler === null) {
45 - return JestReact.unstable_toFlushAndYield(ReactNoop, expectedYields);
46 - }
41 return SchedulerMatchers.toFlushAndYield(Scheduler, expectedYields);
42 }
43
44 function toFlushAndYieldThrough(ReactNoop, expectedYields) {
45 const Scheduler = resolveScheduler(ReactNoop);
52 - if (Scheduler === null) {
53 - return JestReact.unstable_toFlushAndYieldThrough(ReactNoop, expectedYields);
54 - }
46 return SchedulerMatchers.toFlushAndYieldThrough(Scheduler, expectedYields);
47 }
48
49 function toFlushWithoutYielding(ReactNoop) {
50 const Scheduler = resolveScheduler(ReactNoop);
60 - if (Scheduler === null) {
61 - return JestReact.unstable_toFlushWithoutYielding(ReactNoop);
62 - }
51 return SchedulerMatchers.toFlushWithoutYielding(Scheduler);
52 }
53
54 function toHaveYielded(ReactNoop, expectedYields) {
55 const Scheduler = resolveScheduler(ReactNoop);
68 - if (Scheduler === null) {
69 - return JestReact.unstable_toHaveYielded(ReactNoop, expectedYields);
70 - }
56 return SchedulerMatchers.toHaveYielded(Scheduler, expectedYields);
57 }
58
59 function toFlushAndThrow(ReactNoop, ...rest) {
60 const Scheduler = resolveScheduler(ReactNoop);
76 - if (Scheduler === null) {
77 - return JestReact.unstable_toFlushAndThrow(ReactNoop, ...rest);
78 - }
61 return SchedulerMatchers.toFlushAndThrow(Scheduler, ...rest);
62 }
63
scripts/rollup/forks.js
+2 -1
@@ -172,7 +172,8 @@ const forks = Object.freeze({
172 if (
173 entry === 'scheduler/unstable_mock' ||
174 entry === 'react-noop-renderer' ||
175 - entry === 'react-noop-renderer/persistent'
175 + entry === 'react-noop-renderer/persistent' ||
176 + entry === 'react-test-renderer'
177 ) {
178 return 'scheduler/src/forks/SchedulerHostConfig.mock';
179 }