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
}