@samitouri / QOS-React / commits / 53e787b45f

Replace noop's fake Scheduler implementation with mock Scheduler build (#14969)

* Replace noop's fake Scheduler implementation with mock Scheduler build The noop 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. Most of the existing noop tests were unaffected, but I did have to make some changes. The biggest one involved passive effects: previously, they were scheduled on a separate queue from the queue that handles rendering. After this change, both rendering and effects are scheduled in the Scheduler queue. I think this is a better approach because tests no longer have to worry about the difference; if you call `flushAll`, all the work is flushed, both rendering and effects. But for those few tests that do care to flush the rendering without the effects, that's still possible using the `yieldValue` API. Follow-up: Do the same for test renderer. * Fix import to scheduler/unstable_mock

Andrew Clark committed Feb 28, 2019 at 10:30 UTC 53e787b45f633468f5ccdc2012468f1ebbba9d4c
14 files changed +479 -470
packages/react-noop-renderer/src/createReactNoop.js
+19 -146
@@ -18,6 +18,7 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
18 import type {UpdateQueue} from 'react-reconciler/src/ReactUpdateQueue';
19 import type {ReactNodeList} from 'shared/ReactTypes';
20
21 +import * as Scheduler from 'scheduler/unstable_mock';
22 import {createPortal} from 'shared/ReactPortal';
23 import expect from 'expect';
24 import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
@@ -51,9 +52,6 @@ if (__DEV__) {
52 }
53
54 function createReactNoop(reconciler: Function, useMutation: boolean) {
54 - let scheduledCallback = null;
55 - let scheduledCallbackTimeout = -1;
56 - let scheduledPassiveCallback = null;
55 let instanceCounter = 0;
56 let hostDiffCounter = 0;
57 let hostUpdateCounter = 0;
@@ -218,8 +216,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
216 );
217 }
218
221 - let elapsedTimeInMs = 0;
222 -
219 const sharedHostConfig = {
220 getRootHostContext() {
221 return NO_CONTEXT;
@@ -308,66 +304,23 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
304 return inst;
305 },
306
311 - scheduleDeferredCallback(callback, options) {
312 - if (scheduledCallback) {
313 - throw new Error(
314 - 'Scheduling a callback twice is excessive. Instead, keep track of ' +
315 - 'whether the callback has already been scheduled.',
316 - );
317 - }
318 - scheduledCallback = callback;
319 - if (
320 - typeof options === 'object' &&
321 - options !== null &&
322 - typeof options.timeout === 'number'
323 - ) {
324 - const newTimeout = options.timeout;
325 - if (
326 - scheduledCallbackTimeout === -1 ||
327 - scheduledCallbackTimeout > newTimeout
328 - ) {
329 - scheduledCallbackTimeout = elapsedTimeInMs + newTimeout;
330 - }
331 - }
332 - return 0;
333 - },
334 -
335 - cancelDeferredCallback() {
336 - if (scheduledCallback === null) {
337 - throw new Error('No callback is scheduled.');
338 - }
339 - scheduledCallback = null;
340 - scheduledCallbackTimeout = -1;
341 - },
307 + scheduleDeferredCallback: Scheduler.unstable_scheduleCallback,
308 + cancelDeferredCallback: Scheduler.unstable_cancelCallback,
309
343 - shouldYield,
310 + shouldYield: Scheduler.unstable_shouldYield,
311
312 scheduleTimeout: setTimeout,
313 cancelTimeout: clearTimeout,
314 noTimeout: -1,
348 - schedulePassiveEffects(callback) {
349 - if (scheduledCallback) {
350 - throw new Error(
351 - 'Scheduling a callback twice is excessive. Instead, keep track of ' +
352 - 'whether the callback has already been scheduled.',
353 - );
354 - }
355 - scheduledPassiveCallback = callback;
356 - },
357 - cancelPassiveEffects() {
358 - if (scheduledPassiveCallback === null) {
359 - throw new Error('No passive effects callback is scheduled.');
360 - }
361 - scheduledPassiveCallback = null;
362 - },
315 +
316 + schedulePassiveEffects: Scheduler.unstable_scheduleCallback,
317 + cancelPassiveEffects: Scheduler.unstable_cancelCallback,
318
319 prepareForCommit(): void {},
320
321 resetAfterCommit(): void {},
322
368 - now(): number {
369 - return elapsedTimeInMs;
370 - },
323 + now: Scheduler.unstable_now,
324
325 isPrimaryRenderer: true,
326 supportsHydration: false,
@@ -534,71 +487,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
487 const roots = new Map();
488 const DEFAULT_ROOT_ID = '<default>';
489
537 - let yieldedValues: Array<mixed> = [];
538 - let didStop: boolean = false;
539 - let expectedNumberOfYields: number = -1;
540 -
541 - function shouldYield() {
542 - if (
543 - expectedNumberOfYields !== -1 &&
544 - yieldedValues.length >= expectedNumberOfYields &&
545 - (scheduledCallbackTimeout === -1 ||
546 - elapsedTimeInMs < scheduledCallbackTimeout)
547 - ) {
548 - // We yielded at least as many values as expected. Stop rendering.
549 - didStop = true;
550 - return true;
551 - }
552 - // Keep rendering.
553 - return false;
554 - }
555 -
556 - function flushAll(): Array<mixed> {
557 - yieldedValues = [];
558 - while (scheduledCallback !== null) {
559 - const cb = scheduledCallback;
560 - scheduledCallback = null;
561 - const didTimeout =
562 - scheduledCallbackTimeout !== -1 &&
563 - scheduledCallbackTimeout < elapsedTimeInMs;
564 - cb(didTimeout);
565 - }
566 - const values = yieldedValues;
567 - yieldedValues = [];
568 - return values;
569 - }
570 -
571 - function flushNumberOfYields(count: number): Array<mixed> {
572 - expectedNumberOfYields = count;
573 - didStop = false;
574 - yieldedValues = [];
575 - try {
576 - while (scheduledCallback !== null && !didStop) {
577 - const cb = scheduledCallback;
578 - scheduledCallback = null;
579 - const didTimeout =
580 - scheduledCallbackTimeout !== -1 &&
581 - scheduledCallbackTimeout < elapsedTimeInMs;
582 - cb(didTimeout);
583 - }
584 - return yieldedValues;
585 - } finally {
586 - expectedNumberOfYields = -1;
587 - didStop = false;
588 - yieldedValues = [];
589 - }
590 - }
591 -
592 - function yieldValue(value: mixed): void {
593 - yieldedValues.push(value);
594 - }
595 -
596 - function clearYields(): Array<mixed> {
597 - const values = yieldedValues;
598 - yieldedValues = [];
599 - return values;
600 - }
601 -
490 function childToJSX(child, text) {
491 if (text !== null) {
492 return text;
@@ -653,6 +541,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
541 }
542
543 const ReactNoop = {
544 + _Scheduler: Scheduler,
545 +
546 getChildren(rootID: string = DEFAULT_ROOT_ID) {
547 const container = rootContainers.get(rootID);
548 if (container) {
@@ -763,14 +653,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
653 return NoopRenderer.findHostInstance(component);
654 },
655
766 - // TODO: Should only be used via a Jest plugin (like we do with the
767 - // test renderer).
768 - unstable_flushWithoutYielding: flushAll,
769 - unstable_flushNumberOfYields: flushNumberOfYields,
770 - unstable_clearYields: clearYields,
771 -
656 flushNextYield(): Array<mixed> {
773 - return flushNumberOfYields(1);
657 + Scheduler.unstable_flushNumberOfYields(1);
658 + return Scheduler.unstable_clearYields();
659 },
660
661 flushWithHostCounters(
@@ -788,7 +673,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
673 hostUpdateCounter = 0;
674 hostCloneCounter = 0;
675 try {
791 - flushAll();
676 + Scheduler.flushAll();
677 return useMutation
678 ? {
679 hostDiffCounter,
@@ -805,24 +690,13 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
690 }
691 },
692
808 - expire(ms: number): Array<mixed> {
809 - ReactNoop.advanceTime(ms);
810 - return ReactNoop.flushExpired();
811 - },
812 -
813 - advanceTime(ms: number): void {
814 - elapsedTimeInMs += ms;
815 - },
693 + expire: Scheduler.advanceTime,
694
695 flushExpired(): Array<mixed> {
818 - return flushNumberOfYields(0);
696 + return Scheduler.unstable_flushExpired();
697 },
698
821 - yield: yieldValue,
822 -
823 - hasScheduledCallback() {
824 - return !!scheduledCallback;
825 - },
699 + yield: Scheduler.yieldValue,
700
701 batchedUpdates: NoopRenderer.batchedUpdates,
702
@@ -870,9 +744,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
744 },
745
746 flushSync(fn: () => mixed) {
873 - yieldedValues = [];
747 NoopRenderer.flushSync(fn);
875 - return yieldedValues;
748 },
749
750 flushPassiveEffects() {
@@ -997,12 +869,13 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
869 _next: null,
870 };
871 root.firstBatch = batch;
1000 - const actual = flushAll();
872 + Scheduler.unstable_flushWithoutYielding();
873 + const actual = Scheduler.unstable_clearYields();
874 expect(actual).toEqual(expectedFlush);
875 return (expectedCommit: Array<mixed>) => {
876 batch._defer = false;
877 NoopRenderer.flushRoot(root, expiration);
1005 - expect(yieldedValues).toEqual(expectedCommit);
878 + expect(Scheduler.unstable_clearYields()).toEqual(expectedCommit);
879 };
880 },
881
packages/react-reconciler/src/__tests__/ReactExpiration-test.internal.js
+44 -23
@@ -12,6 +12,7 @@
12 let React;
13 let ReactFeatureFlags;
14 let ReactNoop;
15 +let Scheduler;
16
17 describe('ReactExpiration', () => {
18 beforeEach(() => {
@@ -20,6 +21,7 @@ describe('ReactExpiration', () => {
21 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
22 React = require('react');
23 ReactNoop = require('react-noop-renderer');
24 + Scheduler = require('scheduler');
25 });
26
27 function span(prop) {
@@ -60,19 +62,33 @@ describe('ReactExpiration', () => {
62 }
63 }
64
65 + function interrupt() {
66 + ReactNoop.flushSync(() => {
67 + ReactNoop.renderToRootWithID(null, 'other-root');
68 + });
69 + }
70 +
71 // First, show what happens for updates in two separate events.
72 // Schedule an update.
73 ReactNoop.render(<Text text="A" />);
66 - // Advance the timer and flush any work that expired. Flushing the expired
67 - // work signals to the renderer that the event has ended.
68 - ReactNoop.advanceTime(2000);
74 + // Advance the timer.
75 + Scheduler.advanceTime(2000);
76 + // Partially flush the the first update, then interrupt it.
77 + expect(ReactNoop).toFlushAndYieldThrough(['A [render]']);
78 + interrupt();
79 +
80 // Don't advance time by enough to expire the first update.
70 - expect(ReactNoop.flushExpired()).toEqual([]);
81 + expect(Scheduler).toHaveYielded([]);
82 expect(ReactNoop.getChildren()).toEqual([]);
83 +
84 // Schedule another update.
85 ReactNoop.render(<Text text="B" />);
86 // The updates should flush in separate batches, since sufficient time
87 // passed in between them *and* they occurred in separate events.
88 + // Note: This isn't necessarily the ideal behavior. It might be better to
89 + // batch these two updates together. The fact that they aren't batched
90 + // is an implementation detail. The important part of this unit test is that
91 + // they are batched if it's possible that they happened in the same event.
92 expect(ReactNoop).toFlushAndYield([
93 'A [render]',
94 'A [commit]',
@@ -84,10 +100,7 @@ describe('ReactExpiration', () => {
100 // Now do the same thing again, except this time don't flush any work in
101 // between the two updates.
102 ReactNoop.render(<Text text="A" />);
87 - // Advance the timer, but don't flush the expired work. Because we still
88 - // haven't entered an idle callback, the scheduler must assume that we're
89 - // inside the same event.
90 - ReactNoop.advanceTime(2000);
103 + Scheduler.advanceTime(2000);
104 expect(ReactNoop).toHaveYielded([]);
105 expect(ReactNoop.getChildren()).toEqual([span('B')]);
106 // Schedule another update.
@@ -114,19 +127,33 @@ describe('ReactExpiration', () => {
127 }
128 }
129
130 + function interrupt() {
131 + ReactNoop.flushSync(() => {
132 + ReactNoop.renderToRootWithID(null, 'other-root');
133 + });
134 + }
135 +
136 // First, show what happens for updates in two separate events.
137 // Schedule an update.
138 ReactNoop.render(<Text text="A" />);
120 - // Advance the timer and flush any work that expired. Flushing the expired
121 - // work signals to the renderer that the event has ended.
122 - ReactNoop.advanceTime(2000);
139 + // Advance the timer.
140 + Scheduler.advanceTime(2000);
141 + // Partially flush the the first update, then interrupt it.
142 + expect(ReactNoop).toFlushAndYieldThrough(['A [render]']);
143 + interrupt();
144 +
145 // Don't advance time by enough to expire the first update.
124 - expect(ReactNoop.flushExpired()).toEqual([]);
146 + expect(Scheduler).toHaveYielded([]);
147 expect(ReactNoop.getChildren()).toEqual([]);
148 +
149 // Schedule another update.
150 ReactNoop.render(<Text text="B" />);
151 // The updates should flush in separate batches, since sufficient time
152 // passed in between them *and* they occurred in separate events.
153 + // Note: This isn't necessarily the ideal behavior. It might be better to
154 + // batch these two updates together. The fact that they aren't batched
155 + // is an implementation detail. The important part of this unit test is that
156 + // they are batched if it's possible that they happened in the same event.
157 expect(ReactNoop).toFlushAndYield([
158 'A [render]',
159 'A [commit]',
@@ -138,21 +165,15 @@ describe('ReactExpiration', () => {
165 // Now do the same thing again, except this time don't flush any work in
166 // between the two updates.
167 ReactNoop.render(<Text text="A" />);
141 - // Advance the timer, but don't flush the expired work. Because we still
142 - // haven't entered an idle callback, the scheduler must assume that we're
143 - // inside the same event.
144 - ReactNoop.advanceTime(2000);
168 + Scheduler.advanceTime(2000);
169 expect(ReactNoop).toHaveYielded([]);
170 expect(ReactNoop.getChildren()).toEqual([span('B')]);
171
148 - // Perform some synchronous work. Again, the scheduler must assume we're
149 - // inside the same event.
150 - ReactNoop.flushSync(() => {
151 - ReactNoop.renderToRootWithID('1', 'second-root');
152 - });
172 + // Perform some synchronous work. The scheduler must assume we're inside
173 + // the same event.
174 + interrupt();
175
154 - // Even though React flushed a sync update, it should not have updated the
155 - // current time. Schedule another update.
176 + // Schedule another update.
177 ReactNoop.render(<Text text="B" />);
178 // The updates should flush in the same batch, since as far as the scheduler
179 // knows, they may have occurred inside the same event.
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+195 -87
@@ -15,6 +15,7 @@
15 let React;
16 let ReactFeatureFlags;
17 let ReactNoop;
18 +let Scheduler;
19 let SchedulerTracing;
20 let useState;
21 let useReducer;
@@ -40,6 +41,7 @@ describe('ReactHooksWithNoopRenderer', () => {
41 ReactFeatureFlags.enableSchedulerTracing = true;
42 React = require('react');
43 ReactNoop = require('react-noop-renderer');
44 + Scheduler = require('scheduler');
45 SchedulerTracing = require('scheduler/tracing');
46 useState = React.useState;
47 useReducer = React.useReducer;
@@ -595,22 +597,25 @@ describe('ReactHooksWithNoopRenderer', () => {
597 it('simple mount and update', () => {
598 function Counter(props) {
599 useEffect(() => {
598 - ReactNoop.yield(`Did commit [${props.count}]`);
600 + ReactNoop.yield(`Passive effect [${props.count}]`);
601 });
602 return <Text text={'Count: ' + props.count} />;
603 }
602 - ReactNoop.render(<Counter count={0} />);
603 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
604 + ReactNoop.render(<Counter count={0} />, () =>
605 + ReactNoop.yield('Sync effect'),
606 + );
607 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
608 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
605 - ReactNoop.flushPassiveEffects();
606 - expect(ReactNoop).toHaveYielded(['Did commit [0]']);
609 + // Effects are deferred until after the commit
610 + expect(ReactNoop).toFlushAndYield(['Passive effect [0]']);
611
608 - ReactNoop.render(<Counter count={1} />);
609 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
612 + ReactNoop.render(<Counter count={1} />, () =>
613 + ReactNoop.yield('Sync effect'),
614 + );
615 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
616 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
617 // Effects are deferred until after the commit
612 - ReactNoop.flushPassiveEffects();
613 - expect(ReactNoop).toHaveYielded(['Did commit [1]']);
618 + expect(ReactNoop).toFlushAndYield(['Passive effect [1]']);
619 });
620
621 it('flushes passive effects even with sibling deletions', () => {
@@ -628,7 +633,11 @@ describe('ReactHooksWithNoopRenderer', () => {
633 }
634 let passive = <PassiveEffect key="p" />;
635 ReactNoop.render([<LayoutEffect key="l" />, passive]);
631 - expect(ReactNoop).toFlushAndYield(['Layout', 'Passive', 'Layout effect']);
636 + expect(ReactNoop).toFlushAndYieldThrough([
637 + 'Layout',
638 + 'Passive',
639 + 'Layout effect',
640 + ]);
641 expect(ReactNoop.getChildren()).toEqual([
642 span('Layout'),
643 span('Passive'),
@@ -733,17 +742,21 @@ describe('ReactHooksWithNoopRenderer', () => {
742 });
743 return <Text text={props.count} />;
744 }
736 - ReactNoop.render(<Counter count={0} />);
737 - expect(ReactNoop).toFlushAndYield([0]);
745 + ReactNoop.render(<Counter count={0} />, () =>
746 + ReactNoop.yield('Sync effect'),
747 + );
748 + expect(ReactNoop).toFlushAndYieldThrough([0, 'Sync effect']);
749 expect(ReactNoop.getChildren()).toEqual([span(0)]);
750
751 // Before the effects have a chance to flush, schedule another update
741 - ReactNoop.render(<Counter count={1} />);
752 + ReactNoop.render(<Counter count={1} />, () =>
753 + ReactNoop.yield('Sync effect'),
754 + );
755 expect(ReactNoop).toHaveYielded([
756 // The previous effect flushes before the reconciliation
757 'Committed state when effect was fired: 0',
758 ]);
746 - expect(ReactNoop).toFlushAndYield([1]);
759 + expect(ReactNoop).toFlushAndYieldThrough([1, 'Sync effect']);
760 expect(ReactNoop.getChildren()).toEqual([span(1)]);
761
762 ReactNoop.flushPassiveEffects();
@@ -765,15 +778,22 @@ describe('ReactHooksWithNoopRenderer', () => {
778 );
779 return <Text text={'Count: ' + count} />;
780 }
768 - ReactNoop.render(<Counter count={0} />);
769 - expect(ReactNoop).toFlushAndYield(['Count: (empty)']);
781 + ReactNoop.render(<Counter count={0} />, () =>
782 + ReactNoop.yield('Sync effect'),
783 + );
784 + expect(ReactNoop).toFlushAndYieldThrough([
785 + 'Count: (empty)',
786 + 'Sync effect',
787 + ]);
788 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
789 ReactNoop.flushPassiveEffects();
790 expect(ReactNoop).toHaveYielded(['Schedule update [0]']);
791 expect(ReactNoop).toFlushAndYield(['Count: 0']);
792
775 - ReactNoop.render(<Counter count={1} />);
776 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
793 + ReactNoop.render(<Counter count={1} />, () =>
794 + ReactNoop.yield('Sync effect'),
795 + );
796 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
797 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
798 ReactNoop.flushPassiveEffects();
799 expect(ReactNoop).toHaveYielded(['Schedule update [1]']);
@@ -792,20 +812,24 @@ describe('ReactHooksWithNoopRenderer', () => {
812 );
813 return <Text text={'Count: ' + count} />;
814 }
795 - ReactNoop.render(<Counter count={0} />);
796 - expect(ReactNoop).toFlushAndYield(['Count: (empty)']);
815 + ReactNoop.render(<Counter count={0} />, () =>
816 + ReactNoop.yield('Sync effect'),
817 + );
818 + expect(ReactNoop).toFlushAndYieldThrough([
819 + 'Count: (empty)',
820 + 'Sync effect',
821 + ]);
822 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
823
824 // Rendering again should flush the previous commit's effects
800 - ReactNoop.render(<Counter count={1} />);
825 + ReactNoop.render(<Counter count={1} />, () =>
826 + ReactNoop.yield('Sync effect'),
827 + );
828 expect(ReactNoop).toHaveYielded(['Schedule update [0]']);
829 expect(ReactNoop).toFlushAndYieldThrough(['Count: 0']);
830 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
831
805 - ReactNoop.batchedUpdates(() => {
806 - expect(ReactNoop).toFlushAndYield([]);
807 - });
808 -
832 + expect(ReactNoop).toFlushAndYieldThrough(['Sync effect']);
833 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
834
835 ReactNoop.flushPassiveEffects();
@@ -826,8 +850,10 @@ describe('ReactHooksWithNoopRenderer', () => {
850 return <Text text={'Count: ' + count} />;
851 }
852
829 - ReactNoop.render(<Counter count={0} />);
830 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
853 + ReactNoop.render(<Counter count={0} />, () =>
854 + ReactNoop.yield('Sync effect'),
855 + );
856 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
857 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
858
859 // Enqueuing this update forces the passive effect to be flushed --
@@ -869,10 +895,12 @@ describe('ReactHooksWithNoopRenderer', () => {
895 tracingEvent.name,
896 tracingEvent.timestamp,
897 () => {
872 - ReactNoop.render(<Counter count={0} />);
898 + ReactNoop.render(<Counter count={0} />, () =>
899 + ReactNoop.yield('Sync effect'),
900 + );
901 },
902 );
875 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
903 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
904 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
905
906 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);
@@ -936,8 +964,13 @@ describe('ReactHooksWithNoopRenderer', () => {
964 );
965 return <Text text={'Count: ' + count} />;
966 }
939 - ReactNoop.render(<Counter count={0} />);
940 - expect(ReactNoop).toFlushAndYield(['Count: (empty)']);
967 + ReactNoop.render(<Counter count={0} />, () =>
968 + ReactNoop.yield('Sync effect'),
969 + );
970 + expect(ReactNoop).toFlushAndYieldThrough([
971 + 'Count: (empty)',
972 + 'Sync effect',
973 + ]);
974 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
975
976 expect(() => {
@@ -955,14 +988,18 @@ describe('ReactHooksWithNoopRenderer', () => {
988 });
989 return <Text text={'Count: ' + props.count} />;
990 }
958 - ReactNoop.render(<Counter count={0} />);
959 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
991 + ReactNoop.render(<Counter count={0} />, () =>
992 + ReactNoop.yield('Sync effect'),
993 + );
994 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
995 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
996 ReactNoop.flushPassiveEffects();
997 expect(ReactNoop).toHaveYielded(['Did create [0]']);
998
964 - ReactNoop.render(<Counter count={1} />);
965 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
999 + ReactNoop.render(<Counter count={1} />, () =>
1000 + ReactNoop.yield('Sync effect'),
1001 + );
1002 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1003 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1004 ReactNoop.flushPassiveEffects();
1005 expect(ReactNoop).toHaveYielded(['Did destroy [0]', 'Did create [1]']);
@@ -978,8 +1015,10 @@ describe('ReactHooksWithNoopRenderer', () => {
1015 });
1016 return <Text text={'Count: ' + props.count} />;
1017 }
981 - ReactNoop.render(<Counter count={0} />);
982 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1018 + ReactNoop.render(<Counter count={0} />, () =>
1019 + ReactNoop.yield('Sync effect'),
1020 + );
1021 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1022 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1023 ReactNoop.flushPassiveEffects();
1024 expect(ReactNoop).toHaveYielded(['Did create [0]']);
@@ -999,14 +1038,18 @@ describe('ReactHooksWithNoopRenderer', () => {
1038 }, []);
1039 return <Text text={'Count: ' + props.count} />;
1040 }
1002 - ReactNoop.render(<Counter count={0} />);
1003 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1041 + ReactNoop.render(<Counter count={0} />, () =>
1042 + ReactNoop.yield('Sync effect'),
1043 + );
1044 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1045 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1046 ReactNoop.flushPassiveEffects();
1047 expect(ReactNoop).toHaveYielded(['Did create [0]']);
1048
1008 - ReactNoop.render(<Counter count={1} />);
1009 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1049 + ReactNoop.render(<Counter count={1} />, () =>
1050 + ReactNoop.yield('Sync effect'),
1051 + );
1052 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1053 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1054 ReactNoop.flushPassiveEffects();
1055 expect(ReactNoop).toHaveYielded([]);
@@ -1027,14 +1070,18 @@ describe('ReactHooksWithNoopRenderer', () => {
1070 useEffect(effect);
1071 return <Text text={'Count: ' + props.count} />;
1072 }
1030 - ReactNoop.render(<Counter count={0} />);
1031 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1073 + ReactNoop.render(<Counter count={0} />, () =>
1074 + ReactNoop.yield('Sync effect'),
1075 + );
1076 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1077 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1078 ReactNoop.flushPassiveEffects();
1079 expect(ReactNoop).toHaveYielded(['Did create']);
1080
1036 - ReactNoop.render(<Counter count={1} />);
1037 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1081 + ReactNoop.render(<Counter count={1} />, () =>
1082 + ReactNoop.yield('Sync effect'),
1083 + );
1084 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1085 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1086 ReactNoop.flushPassiveEffects();
1087 expect(ReactNoop).toHaveYielded(['Did destroy', 'Did create']);
@@ -1058,15 +1105,19 @@ describe('ReactHooksWithNoopRenderer', () => {
1105 );
1106 return <Text text={text} />;
1107 }
1061 - ReactNoop.render(<Counter label="Count" count={0} />);
1062 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1108 + ReactNoop.render(<Counter label="Count" count={0} />, () =>
1109 + ReactNoop.yield('Sync effect'),
1110 + );
1111 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1112 ReactNoop.flushPassiveEffects();
1113 expect(ReactNoop).toHaveYielded(['Did create [Count: 0]']);
1114 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1115
1067 - ReactNoop.render(<Counter label="Count" count={1} />);
1116 + ReactNoop.render(<Counter label="Count" count={1} />, () =>
1117 + ReactNoop.yield('Sync effect'),
1118 + );
1119 // Count changed
1069 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1120 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1121 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1122 ReactNoop.flushPassiveEffects();
1123 expect(ReactNoop).toHaveYielded([
@@ -1074,16 +1125,20 @@ describe('ReactHooksWithNoopRenderer', () => {
1125 'Did create [Count: 1]',
1126 ]);
1127
1077 - ReactNoop.render(<Counter label="Count" count={1} />);
1128 + ReactNoop.render(<Counter label="Count" count={1} />, () =>
1129 + ReactNoop.yield('Sync effect'),
1130 + );
1131 // Nothing changed, so no effect should have fired
1079 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1132 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1133 ReactNoop.flushPassiveEffects();
1134 expect(ReactNoop).toHaveYielded([]);
1135 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1136
1084 - ReactNoop.render(<Counter label="Total" count={1} />);
1137 + ReactNoop.render(<Counter label="Total" count={1} />, () =>
1138 + ReactNoop.yield('Sync effect'),
1139 + );
1140 // Label changed
1086 - expect(ReactNoop).toFlushAndYield(['Total: 1']);
1141 + expect(ReactNoop).toFlushAndYieldThrough(['Total: 1', 'Sync effect']);
1142 expect(ReactNoop.getChildren()).toEqual([span('Total: 1')]);
1143 ReactNoop.flushPassiveEffects();
1144 expect(ReactNoop).toHaveYielded([
@@ -1102,14 +1157,18 @@ describe('ReactHooksWithNoopRenderer', () => {
1157 });
1158 return <Text text={'Count: ' + props.count} />;
1159 }
1105 - ReactNoop.render(<Counter count={0} />);
1106 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1160 + ReactNoop.render(<Counter count={0} />, () =>
1161 + ReactNoop.yield('Sync effect'),
1162 + );
1163 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1164 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1165 ReactNoop.flushPassiveEffects();
1166 expect(ReactNoop).toHaveYielded(['Did commit 1 [0]', 'Did commit 2 [0]']);
1167
1111 - ReactNoop.render(<Counter count={1} />);
1112 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1168 + ReactNoop.render(<Counter count={1} />, () =>
1169 + ReactNoop.yield('Sync effect'),
1170 + );
1171 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1172 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1173 ReactNoop.flushPassiveEffects();
1174 expect(ReactNoop).toHaveYielded(['Did commit 1 [1]', 'Did commit 2 [1]']);
@@ -1131,14 +1190,18 @@ describe('ReactHooksWithNoopRenderer', () => {
1190 });
1191 return <Text text={'Count: ' + props.count} />;
1192 }
1134 - ReactNoop.render(<Counter count={0} />);
1135 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1193 + ReactNoop.render(<Counter count={0} />, () =>
1194 + ReactNoop.yield('Sync effect'),
1195 + );
1196 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1197 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1198 ReactNoop.flushPassiveEffects();
1199 expect(ReactNoop).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
1200
1140 - ReactNoop.render(<Counter count={1} />);
1141 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1201 + ReactNoop.render(<Counter count={1} />, () =>
1202 + ReactNoop.yield('Sync effect'),
1203 + );
1204 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1205 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1206 ReactNoop.flushPassiveEffects();
1207 expect(ReactNoop).toHaveYielded([
@@ -1168,8 +1231,10 @@ describe('ReactHooksWithNoopRenderer', () => {
1231 });
1232 return <Text text={'Count: ' + props.count} />;
1233 }
1171 - ReactNoop.render(<Counter count={0} />);
1172 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1234 + ReactNoop.render(<Counter count={0} />, () =>
1235 + ReactNoop.yield('Sync effect'),
1236 + );
1237 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1238 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1239 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
1240 expect(ReactNoop).toHaveYielded([
@@ -1202,15 +1267,19 @@ describe('ReactHooksWithNoopRenderer', () => {
1267 });
1268 return <Text text={'Count: ' + props.count} />;
1269 }
1205 - ReactNoop.render(<Counter count={0} />);
1206 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1270 + ReactNoop.render(<Counter count={0} />, () =>
1271 + ReactNoop.yield('Sync effect'),
1272 + );
1273 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1274 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1275 ReactNoop.flushPassiveEffects();
1276 expect(ReactNoop).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
1277
1278 // This update will trigger an errror
1212 - ReactNoop.render(<Counter count={1} />);
1213 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1279 + ReactNoop.render(<Counter count={1} />, () =>
1280 + ReactNoop.yield('Sync effect'),
1281 + );
1282 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1283 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1284 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
1285 expect(ReactNoop).toHaveYielded([
@@ -1244,15 +1313,19 @@ describe('ReactHooksWithNoopRenderer', () => {
1313 });
1314 return <Text text={'Count: ' + props.count} />;
1315 }
1247 - ReactNoop.render(<Counter count={0} />);
1248 - expect(ReactNoop).toFlushAndYield(['Count: 0']);
1316 + ReactNoop.render(<Counter count={0} />, () =>
1317 + ReactNoop.yield('Sync effect'),
1318 + );
1319 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1320 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1321 ReactNoop.flushPassiveEffects();
1322 expect(ReactNoop).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
1323
1324 // This update will trigger an errror
1254 - ReactNoop.render(<Counter count={1} />);
1255 - expect(ReactNoop).toFlushAndYield(['Count: 1']);
1325 + ReactNoop.render(<Counter count={1} />, () =>
1326 + ReactNoop.yield('Sync effect'),
1327 + );
1328 + expect(ReactNoop).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1329 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1330 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
1331 expect(ReactNoop).toHaveYielded([
@@ -1273,16 +1346,29 @@ describe('ReactHooksWithNoopRenderer', () => {
1346 }
1347 Counter = memo(Counter);
1348
1276 - ReactNoop.render(<Counter count={0} />);
1277 - expect(ReactNoop).toFlushAndYield(['Count: 0', 'Mount: 0']);
1349 + ReactNoop.render(<Counter count={0} />, () =>
1350 + ReactNoop.yield('Sync effect'),
1351 + );
1352 + expect(ReactNoop).toFlushAndYieldThrough([
1353 + 'Count: 0',
1354 + 'Mount: 0',
1355 + 'Sync effect',
1356 + ]);
1357 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1358
1280 - ReactNoop.render(<Counter count={1} />);
1281 - expect(ReactNoop).toFlushAndYield(['Count: 1', 'Unmount: 0', 'Mount: 1']);
1359 + ReactNoop.render(<Counter count={1} />, () =>
1360 + ReactNoop.yield('Sync effect'),
1361 + );
1362 + expect(ReactNoop).toFlushAndYieldThrough([
1363 + 'Count: 1',
1364 + 'Unmount: 0',
1365 + 'Mount: 1',
1366 + 'Sync effect',
1367 + ]);
1368 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1369
1370 ReactNoop.render(null);
1285 - expect(ReactNoop).toFlushAndYield(['Unmount: 1']);
1371 + expect(ReactNoop).toFlushAndYieldThrough(['Unmount: 1']);
1372 expect(ReactNoop.getChildren()).toEqual([]);
1373 });
1374 });
@@ -1290,7 +1376,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1376 describe('useLayoutEffect', () => {
1377 it('fires layout effects after the host has been mutated', () => {
1378 function getCommittedText() {
1293 - const yields = ReactNoop.unstable_clearYields();
1379 + const yields = Scheduler.unstable_clearYields();
1380 const children = ReactNoop.getChildren();
1381 ReactNoop.yield(yields);
1382 if (children === null) {
@@ -1306,12 +1392,24 @@ describe('ReactHooksWithNoopRenderer', () => {
1392 return <Text text={props.count} />;
1393 }
1394
1309 - ReactNoop.render(<Counter count={0} />);
1310 - expect(ReactNoop).toFlushAndYield([[0], 'Current: 0']);
1395 + ReactNoop.render(<Counter count={0} />, () =>
1396 + ReactNoop.yield('Sync effect'),
1397 + );
1398 + expect(ReactNoop).toFlushAndYieldThrough([
1399 + [0],
1400 + 'Current: 0',
1401 + 'Sync effect',
1402 + ]);
1403 expect(ReactNoop.getChildren()).toEqual([span(0)]);
1404
1313 - ReactNoop.render(<Counter count={1} />);
1314 - expect(ReactNoop).toFlushAndYield([[1], 'Current: 1']);
1405 + ReactNoop.render(<Counter count={1} />, () =>
1406 + ReactNoop.yield('Sync effect'),
1407 + );
1408 + expect(ReactNoop).toFlushAndYieldThrough([
1409 + [1],
1410 + 'Current: 1',
1411 + 'Sync effect',
1412 + ]);
1413 expect(ReactNoop.getChildren()).toEqual([span(1)]);
1414 });
1415
@@ -1338,15 +1436,23 @@ describe('ReactHooksWithNoopRenderer', () => {
1436 return null;
1437 }
1438
1341 - ReactNoop.render(<Counter count={0} />);
1342 - expect(ReactNoop).toFlushAndYield(['Mount layout [current: 0]']);
1439 + ReactNoop.render(<Counter count={0} />, () =>
1440 + ReactNoop.yield('Sync effect'),
1441 + );
1442 + expect(ReactNoop).toFlushAndYieldThrough([
1443 + 'Mount layout [current: 0]',
1444 + 'Sync effect',
1445 + ]);
1446 expect(committedText).toEqual('0');
1447
1345 - ReactNoop.render(<Counter count={1} />);
1448 + ReactNoop.render(<Counter count={1} />, () =>
1449 + ReactNoop.yield('Sync effect'),
1450 + );
1451 expect(ReactNoop).toHaveYielded(['Mount normal [current: 0]']);
1347 - expect(ReactNoop).toFlushAndYield([
1452 + expect(ReactNoop).toFlushAndYieldThrough([
1453 'Unmount layout [current: 0]',
1454 'Mount layout [current: 1]',
1455 + 'Sync effect',
1456 ]);
1457 expect(committedText).toEqual('1');
1458
@@ -1823,8 +1929,10 @@ describe('ReactHooksWithNoopRenderer', () => {
1929 return null;
1930 }
1931
1826 - ReactNoop.render(<App showMore={false} />);
1827 - expect(ReactNoop).toFlushAndYield([]);
1932 + ReactNoop.render(<App showMore={false} />, () =>
1933 + ReactNoop.yield('Sync effect'),
1934 + );
1935 + expect(ReactNoop).toFlushAndYieldThrough(['Sync effect']);
1936 ReactNoop.flushPassiveEffects();
1937 expect(ReactNoop).toHaveYielded(['Mount A']);
1938
packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
+1 -3
@@ -2862,9 +2862,7 @@ describe('ReactIncremental', () => {
2862 expect(ReactNoop).toFlushAndYieldThrough(['Parent: 1']);
2863
2864 // Interrupt at higher priority
2865 - expect(
2866 - ReactNoop.flushSync(() => ReactNoop.render(<Parent step={2} />)),
2867 - ).toEqual(['Parent: 2', 'Child: 2']);
2865 + ReactNoop.flushSync(() => ReactNoop.render(<Parent step={2} />));
2866 expect(ReactNoop).toHaveYielded(['Parent: 2', 'Child: 2']);
2867
2868 expect(ReactNoop).toFlushAndYield([]);
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+13 -1
@@ -14,6 +14,7 @@ let PropTypes;
14 let ReactFeatureFlags;
15 let React;
16 let ReactNoop;
17 +let Scheduler;
18
19 describe('ReactIncrementalErrorHandling', () => {
20 beforeEach(() => {
@@ -24,6 +25,7 @@ describe('ReactIncrementalErrorHandling', () => {
25 PropTypes = require('prop-types');
26 React = require('react');
27 ReactNoop = require('react-noop-renderer');
28 + Scheduler = require('scheduler');
29 });
30
31 function div(...children) {
@@ -226,8 +228,18 @@ describe('ReactIncrementalErrorHandling', () => {
228 ReactNoop.yield('commit');
229 }
230
231 + function interrupt() {
232 + ReactNoop.flushSync(() => {
233 + ReactNoop.renderToRootWithID(null, 'other-root');
234 + });
235 + }
236 +
237 ReactNoop.render(<App isBroken={true} />, onCommit);
230 - ReactNoop.expire(2000);
238 + Scheduler.advanceTime(1000);
239 + expect(ReactNoop).toFlushAndYieldThrough(['error']);
240 + interrupt();
241 +
242 + // This update is in a separate batch
243 ReactNoop.render(<App isBroken={false} />, onCommit);
244
245 expect(ReactNoop).toFlushAndYield([
packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js
-1
@@ -283,7 +283,6 @@ describe('ReactDebugFiberPerf', () => {
283 componentDidMount() {
284 ReactNoop.renderToRootWithID(<Child />, 'b');
285 addComment('Scheduling another root from componentDidMount');
286 - expect(ReactNoop).toFlushWithoutYielding();
286 }
287 render() {
288 return <div>{this.props.children}</div>;
packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.internal.js
-33
@@ -361,37 +361,4 @@ describe('ReactIncrementalScheduling', () => {
361 'componentDidUpdate: 2',
362 ]);
363 });
364 -
365 - it('updates do not schedule a new callback if already inside a callback', () => {
366 - class Foo extends React.Component {
367 - state = {foo: 'foo'};
368 - UNSAFE_componentWillReceiveProps() {
369 - ReactNoop.yield(
370 - 'has callback before setState: ' + ReactNoop.hasScheduledCallback(),
371 - );
372 - this.setState({foo: 'baz'});
373 - ReactNoop.yield(
374 - 'has callback after setState: ' + ReactNoop.hasScheduledCallback(),
375 - );
376 - }
377 - render() {
378 - return null;
379 - }
380 - }
381 -
382 - ReactNoop.render(<Foo step={1} />);
383 - expect(() => {
384 - expect(ReactNoop).toFlushWithoutYielding();
385 - }).toWarnDev(
386 - 'componentWillReceiveProps: Please update the following components ' +
387 - 'to use static getDerivedStateFromProps instead: Foo',
388 - {withoutStack: true},
389 - );
390 -
391 - ReactNoop.render(<Foo step={2} />);
392 - expect(ReactNoop).toFlushAndYield([
393 - 'has callback before setState: false',
394 - 'has callback after setState: false',
395 - ]);
396 - });
364 });
packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.internal.js
+6 -4
@@ -13,6 +13,7 @@
13 let React;
14 let ReactFeatureFlags;
15 let ReactNoop;
16 +let Scheduler;
17
18 describe('ReactIncrementalTriangle', () => {
19 beforeEach(() => {
@@ -21,6 +22,7 @@ describe('ReactIncrementalTriangle', () => {
22 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
23 React = require('react');
24 ReactNoop = require('react-noop-renderer');
25 + Scheduler = require('scheduler');
26 });
27
28 function span(prop) {
@@ -306,7 +308,7 @@ describe('ReactIncrementalTriangle', () => {
308 } else {
309 ReactNoop.render(<App remainingDepth={MAX_DEPTH} key={keyCounter++} />);
310 }
309 - ReactNoop.unstable_flushWithoutYielding();
311 + Scheduler.unstable_flushWithoutYielding();
312 assertConsistentTree();
313 return appInstance;
314 }
@@ -385,10 +387,10 @@ describe('ReactIncrementalTriangle', () => {
387 ReactNoop.flushSync(() => {
388 switch (action.type) {
389 case FLUSH:
388 - ReactNoop.unstable_flushNumberOfYields(action.unitsOfWork);
390 + Scheduler.unstable_flushNumberOfYields(action.unitsOfWork);
391 break;
392 case FLUSH_ALL:
391 - ReactNoop.unstable_flushWithoutYielding();
393 + Scheduler.unstable_flushWithoutYielding();
394 break;
395 case STEP:
396 ReactNoop.deferredUpdates(() => {
@@ -430,7 +432,7 @@ describe('ReactIncrementalTriangle', () => {
432 assertConsistentTree(activeLeafIndices);
433 }
434 // Flush remaining work
433 - ReactNoop.unstable_flushWithoutYielding();
435 + Scheduler.unstable_flushWithoutYielding();
436 assertConsistentTree(activeLeafIndices, expectedCounterAtEnd);
437 }
438
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
+142 -98
@@ -13,6 +13,7 @@
13 let React;
14 let ReactFeatureFlags;
15 let ReactNoop;
16 +let Scheduler;
17
18 describe('ReactIncrementalUpdates', () => {
19 beforeEach(() => {
@@ -21,6 +22,7 @@ describe('ReactIncrementalUpdates', () => {
22 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
23 React = require('react');
24 ReactNoop = require('react-noop-renderer');
25 + Scheduler = require('scheduler');
26 });
27
28 function span(prop) {
@@ -456,33 +458,50 @@ describe('ReactIncrementalUpdates', () => {
458 });
459
460 it('flushes all expired updates in a single batch', () => {
459 - class Foo extends React.Component {
460 - componentDidUpdate() {
461 - ReactNoop.yield('Commit: ' + this.props.prop);
462 - }
463 - componentDidMount() {
464 - ReactNoop.yield('Commit: ' + this.props.prop);
465 - }
466 - render() {
467 - ReactNoop.yield('Render: ' + this.props.prop);
468 - return <span prop={this.props.prop} />;
469 - }
461 + const {useEffect} = React;
462 +
463 + function App({label}) {
464 + ReactNoop.yield('Render: ' + label);
465 + useEffect(() => {
466 + ReactNoop.yield('Commit: ' + label);
467 + });
468 + return label;
469 + }
470 +
471 + function interrupt() {
472 + ReactNoop.flushSync(() => {
473 + ReactNoop.renderToRootWithID(null, 'other-root');
474 + });
475 }
476
477 // First, as a sanity check, assert what happens when four low pri
478 // updates in separate batches are all flushed in the same callback
474 - ReactNoop.render(<Foo prop="" />);
475 - ReactNoop.expire(1000);
476 - jest.advanceTimersByTime(1000);
477 - ReactNoop.render(<Foo prop="he" />);
478 - ReactNoop.expire(1000);
479 - jest.advanceTimersByTime(1000);
480 - ReactNoop.render(<Foo prop="hell" />);
481 - ReactNoop.expire(1000);
482 - jest.advanceTimersByTime(1000);
483 - ReactNoop.render(<Foo prop="hello" />);
484 -
485 - // There should be a separate render and commit for each update
479 + ReactNoop.render(<App label="" />);
480 + Scheduler.advanceTime(1000);
481 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
482 + interrupt();
483 +
484 + ReactNoop.render(<App label="he" />);
485 + Scheduler.advanceTime(1000);
486 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
487 + interrupt();
488 +
489 + ReactNoop.render(<App label="hell" />);
490 + Scheduler.advanceTime(1000);
491 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
492 + interrupt();
493 +
494 + ReactNoop.render(<App label="hello" />);
495 + Scheduler.advanceTime(1000);
496 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
497 + interrupt();
498 +
499 + // Each update flushes in a separate commit.
500 + // Note: This isn't necessarily the ideal behavior. It might be better to
501 + // batch all of these updates together. The fact that they don't is an
502 + // implementation detail. The important part of this unit test is what
503 + // happens when they expire, in which case they really should be batched to
504 + // avoid blocking the main thread for a long time.
505 expect(ReactNoop).toFlushAndYield([
506 'Render: ',
507 'Commit: ',
@@ -493,65 +512,86 @@ describe('ReactIncrementalUpdates', () => {
512 'Render: hello',
513 'Commit: hello',
514 ]);
496 - expect(ReactNoop.getChildren()).toEqual([span('hello')]);
497 -
498 - // Now do the same thing, except this time expire all the updates
499 - // before flushing them.
500 - ReactNoop.render(<Foo prop="" />);
501 - ReactNoop.expire(1000);
502 - jest.advanceTimersByTime(1000);
503 - ReactNoop.render(<Foo prop="go" />);
504 - ReactNoop.expire(1000);
505 - jest.advanceTimersByTime(1000);
506 - ReactNoop.render(<Foo prop="good" />);
507 - ReactNoop.expire(1000);
508 - jest.advanceTimersByTime(1000);
509 - ReactNoop.render(<Foo prop="goodbye" />);
510 -
511 - ReactNoop.advanceTime(10000);
512 - jest.advanceTimersByTime(10000);
515 +
516 + // Now do the same thing over again, but this time, expire all the updates
517 + // instead of flushing them normally.
518 + ReactNoop.render(<App label="" />);
519 + Scheduler.advanceTime(1000);
520 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
521 + interrupt();
522 +
523 + ReactNoop.render(<App label="go" />);
524 + Scheduler.advanceTime(1000);
525 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
526 + interrupt();
527 +
528 + ReactNoop.render(<App label="good" />);
529 + Scheduler.advanceTime(1000);
530 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
531 + interrupt();
532 +
533 + ReactNoop.render(<App label="goodbye" />);
534 + Scheduler.advanceTime(1000);
535 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
536 + interrupt();
537
538 // All the updates should render and commit in a single batch.
515 - expect(ReactNoop).toFlushAndYield(['Render: goodbye', 'Commit: goodbye']);
516 - expect(ReactNoop.getChildren()).toEqual([span('goodbye')]);
539 + Scheduler.advanceTime(10000);
540 + expect(ReactNoop).toHaveYielded(['Render: goodbye']);
541 + // Passive effect
542 + expect(ReactNoop).toFlushAndYield(['Commit: goodbye']);
543 });
544
545 it('flushes all expired updates in a single batch across multiple roots', () => {
546 // Same as previous test, but with two roots.
521 - class Foo extends React.Component {
522 - componentDidUpdate() {
523 - ReactNoop.yield('Commit: ' + this.props.prop);
524 - }
525 - componentDidMount() {
526 - ReactNoop.yield('Commit: ' + this.props.prop);
527 - }
528 - render() {
529 - ReactNoop.yield('Render: ' + this.props.prop);
530 - return <span prop={this.props.prop} />;
531 - }
547 + const {useEffect} = React;
548 +
549 + function App({label}) {
550 + ReactNoop.yield('Render: ' + label);
551 + useEffect(() => {
552 + ReactNoop.yield('Commit: ' + label);
553 + });
554 + return label;
555 + }
556 +
557 + function interrupt() {
558 + ReactNoop.flushSync(() => {
559 + ReactNoop.renderToRootWithID(null, 'other-root');
560 + });
561 }
562
563 // First, as a sanity check, assert what happens when four low pri
564 // updates in separate batches are all flushed in the same callback
536 - ReactNoop.renderToRootWithID(<Foo prop="" />, 'a');
537 - ReactNoop.renderToRootWithID(<Foo prop="" />, 'b');
538 -
539 - ReactNoop.expire(1000);
540 - jest.advanceTimersByTime(1000);
541 - ReactNoop.renderToRootWithID(<Foo prop="he" />, 'a');
542 - ReactNoop.renderToRootWithID(<Foo prop="he" />, 'b');
543 -
544 - ReactNoop.expire(1000);
545 - jest.advanceTimersByTime(1000);
546 - ReactNoop.renderToRootWithID(<Foo prop="hell" />, 'a');
547 - ReactNoop.renderToRootWithID(<Foo prop="hell" />, 'b');
548 -
549 - ReactNoop.expire(1000);
550 - jest.advanceTimersByTime(1000);
551 - ReactNoop.renderToRootWithID(<Foo prop="hello" />, 'a');
552 - ReactNoop.renderToRootWithID(<Foo prop="hello" />, 'b');
553 -
554 - // There should be a separate render and commit for each update
565 + ReactNoop.renderToRootWithID(<App label="" />, 'a');
566 + ReactNoop.renderToRootWithID(<App label="" />, 'b');
567 + Scheduler.advanceTime(1000);
568 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
569 + interrupt();
570 +
571 + ReactNoop.renderToRootWithID(<App label="he" />, 'a');
572 + ReactNoop.renderToRootWithID(<App label="he" />, 'b');
573 + Scheduler.advanceTime(1000);
574 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
575 + interrupt();
576 +
577 + ReactNoop.renderToRootWithID(<App label="hell" />, 'a');
578 + ReactNoop.renderToRootWithID(<App label="hell" />, 'b');
579 + Scheduler.advanceTime(1000);
580 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
581 + interrupt();
582 +
583 + ReactNoop.renderToRootWithID(<App label="hello" />, 'a');
584 + ReactNoop.renderToRootWithID(<App label="hello" />, 'b');
585 + Scheduler.advanceTime(1000);
586 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
587 + interrupt();
588 +
589 + // Each update flushes in a separate commit.
590 + // Note: This isn't necessarily the ideal behavior. It might be better to
591 + // batch all of these updates together. The fact that they don't is an
592 + // implementation detail. The important part of this unit test is what
593 + // happens when they expire, in which case they really should be batched to
594 + // avoid blocking the main thread for a long time.
595 expect(ReactNoop).toFlushAndYield([
596 'Render: ',
597 'Commit: ',
@@ -570,37 +610,41 @@ describe('ReactIncrementalUpdates', () => {
610 'Render: hello',
611 'Commit: hello',
612 ]);
573 - expect(ReactNoop.getChildren('a')).toEqual([span('hello')]);
574 - expect(ReactNoop.getChildren('b')).toEqual([span('hello')]);
575 -
576 - // Now do the same thing, except this time expire all the updates
577 - // before flushing them.
578 - ReactNoop.renderToRootWithID(<Foo prop="" />, 'a');
579 - ReactNoop.renderToRootWithID(<Foo prop="" />, 'b');
580 - ReactNoop.expire(1000);
581 - jest.advanceTimersByTime(1000);
582 - ReactNoop.renderToRootWithID(<Foo prop="go" />, 'a');
583 - ReactNoop.renderToRootWithID(<Foo prop="go" />, 'b');
584 - ReactNoop.expire(1000);
585 - jest.advanceTimersByTime(1000);
586 - ReactNoop.renderToRootWithID(<Foo prop="good" />, 'a');
587 - ReactNoop.renderToRootWithID(<Foo prop="good" />, 'b');
588 - ReactNoop.expire(1000);
589 - jest.advanceTimersByTime(1000);
590 - ReactNoop.renderToRootWithID(<Foo prop="goodbye" />, 'a');
591 - ReactNoop.renderToRootWithID(<Foo prop="goodbye" />, 'b');
592 -
593 - ReactNoop.advanceTime(10000);
594 - jest.advanceTimersByTime(10000);
613 +
614 + // Now do the same thing over again, but this time, expire all the updates
615 + // instead of flushing them normally.
616 + ReactNoop.renderToRootWithID(<App label="" />, 'a');
617 + ReactNoop.renderToRootWithID(<App label="" />, 'b');
618 + Scheduler.advanceTime(1000);
619 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
620 + interrupt();
621 +
622 + ReactNoop.renderToRootWithID(<App label="go" />, 'a');
623 + ReactNoop.renderToRootWithID(<App label="go" />, 'b');
624 + Scheduler.advanceTime(1000);
625 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
626 + interrupt();
627 +
628 + ReactNoop.renderToRootWithID(<App label="good" />, 'a');
629 + ReactNoop.renderToRootWithID(<App label="good" />, 'b');
630 + Scheduler.advanceTime(1000);
631 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
632 + interrupt();
633 +
634 + ReactNoop.renderToRootWithID(<App label="goodbye" />, 'a');
635 + ReactNoop.renderToRootWithID(<App label="goodbye" />, 'b');
636 + Scheduler.advanceTime(1000);
637 + expect(ReactNoop).toFlushAndYieldThrough(['Render: ']);
638 + interrupt();
639
640 // All the updates should render and commit in a single batch.
597 - expect(ReactNoop).toFlushAndYield([
641 + Scheduler.advanceTime(10000);
642 + expect(ReactNoop).toHaveYielded([
643 'Render: goodbye',
644 'Commit: goodbye',
645 'Render: goodbye',
601 - 'Commit: goodbye',
646 ]);
603 - expect(ReactNoop.getChildren('a')).toEqual([span('goodbye')]);
604 - expect(ReactNoop.getChildren('b')).toEqual([span('goodbye')]);
647 + // Passive effect
648 + expect(ReactNoop).toFlushAndYield(['Commit: goodbye']);
649 });
650 });
packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
+5 -3
@@ -14,6 +14,7 @@ let ReactFeatureFlags = require('shared/ReactFeatureFlags');
14 let React = require('react');
15 let useContext;
16 let ReactNoop;
17 +let Scheduler;
18 let gen;
19
20 describe('ReactNewContext', () => {
@@ -24,6 +25,7 @@ describe('ReactNewContext', () => {
25 React = require('react');
26 useContext = React.useContext;
27 ReactNoop = require('react-noop-renderer');
28 + Scheduler = require('scheduler');
29 gen = require('random-seed');
30 });
31
@@ -1769,10 +1771,10 @@ describe('ReactNewContext', () => {
1771 actions.forEach(action => {
1772 switch (action.type) {
1773 case FLUSH_ALL:
1772 - ReactNoop.unstable_flushWithoutYielding();
1774 + Scheduler.unstable_flushWithoutYielding();
1775 break;
1776 case FLUSH:
1775 - ReactNoop.unstable_flushNumberOfYields(action.unitsOfWork);
1777 + Scheduler.unstable_flushNumberOfYields(action.unitsOfWork);
1778 break;
1779 case UPDATE:
1780 finalExpectedValues = {
@@ -1787,7 +1789,7 @@ describe('ReactNewContext', () => {
1789 assertConsistentTree();
1790 });
1791
1790 - ReactNoop.unstable_flushWithoutYielding();
1792 + Scheduler.unstable_flushWithoutYielding();
1793 assertConsistentTree(finalExpectedValues);
1794 }
1795
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
+9 -6
@@ -2,6 +2,7 @@ let React;
2 let ReactFeatureFlags;
3 let Fragment;
4 let ReactNoop;
5 +let Scheduler;
6 let ReactCache;
7 let Suspense;
8 let StrictMode;
@@ -22,6 +23,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
23 React = require('react');
24 Fragment = React.Fragment;
25 ReactNoop = require('react-noop-renderer');
26 + Scheduler = require('scheduler');
27 ReactCache = require('react-cache');
28 Suspense = React.Suspense;
29 StrictMode = React.StrictMode;
@@ -449,7 +451,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
451 await advanceTimers(10000);
452 // No additional rendering work is required, since we already prepared
453 // the placeholder.
452 - expect(ReactNoop.flushExpired()).toEqual([]);
454 + expect(Scheduler).toHaveYielded([]);
455 // Should have committed the placeholder.
456 expect(ReactNoop.getChildren()).toEqual([span('Loading...'), span('Sync')]);
457
@@ -636,7 +638,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
638 );
639 expect(ReactNoop.flushNextYield()).toEqual(['Suspend! [Async]']);
640 await advanceTimers(1500);
639 - expect(ReactNoop.expire(1500)).toEqual([]);
641 + expect(Scheduler).toHaveYielded([]);
642 // Before we have a chance to flush, the promise resolves.
643 await advanceTimers(2000);
644 expect(ReactNoop).toHaveYielded(['Promise resolved [Async]']);
@@ -659,7 +661,8 @@ describe('ReactSuspenseWithNoopRenderer', () => {
661 <AsyncText text="B" ms={100} />
662 </Suspense>,
663 );
662 - expect(ReactNoop.expire(10000)).toEqual([
664 + Scheduler.advanceTime(10000);
665 + expect(Scheduler).toHaveYielded([
666 'Suspend! [A]',
667 'Suspend! [B]',
668 'Loading...',
@@ -780,17 +783,17 @@ describe('ReactSuspenseWithNoopRenderer', () => {
783 jest.advanceTimersByTime(1000);
784 ReactNoop.render(<Foo text="goodbye" />);
785
783 - ReactNoop.advanceTime(10000);
786 + Scheduler.advanceTime(10000);
787 jest.advanceTimersByTime(10000);
788
786 - expect(ReactNoop).toFlushAndYield([
789 + expect(ReactNoop).toHaveYielded([
790 'Suspend! [goodbye]',
791 'Loading...',
792 'Commit: goodbye',
793 ]);
794 expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
795
793 - ReactNoop.advanceTime(20000);
796 + Scheduler.advanceTime(20000);
797 await advanceTimers(20000);
798 expect(ReactNoop).toHaveYielded(['Promise resolved [goodbye]']);
799 expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
packages/react/src/__tests__/ReactProfiler-test.internal.js
+8 -7
@@ -13,6 +13,7 @@
13 let React;
14 let ReactFeatureFlags;
15 let ReactNoop;
16 +let Scheduler;
17 let ReactCache;
18 let ReactTestRenderer;
19 let advanceTimeBy;
@@ -44,6 +45,7 @@ function loadModules({
45 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = replayFailedUnitOfWorkWithInvokeGuardedCallback;
46
47 React = require('react');
48 + Scheduler = require('scheduler');
49 SchedulerTracing = require('scheduler/tracing');
50 ReactCache = require('react-cache');
51
@@ -1205,7 +1207,7 @@ describe('Profiler', () => {
1207 loadModules({useNoopRenderer: true});
1208
1209 const Child = ({duration, id}) => {
1208 - ReactNoop.advanceTime(duration);
1210 + Scheduler.advanceTime(duration);
1211 ReactNoop.yield(`Child:render:${id}`);
1212 return null;
1213 };
@@ -1225,7 +1227,7 @@ describe('Profiler', () => {
1227 }
1228 }
1229
1228 - ReactNoop.advanceTime(50);
1230 + Scheduler.advanceTime(50);
1231
1232 ReactNoop.renderToRootWithID(<Parent duration={3} id="one" />, 'one');
1233
@@ -1238,13 +1240,13 @@ describe('Profiler', () => {
1240
1241 expect(ReactNoop.getRoot('one').current.actualDuration).toBe(0);
1242
1241 - ReactNoop.advanceTime(100);
1243 + Scheduler.advanceTime(100);
1244
1245 // Process some async work, but yield before committing it.
1246 ReactNoop.renderToRootWithID(<Parent duration={7} id="two" />, 'two');
1247 expect(ReactNoop).toFlushAndYieldThrough(['Child:render:two']);
1248
1247 - ReactNoop.advanceTime(150);
1249 + Scheduler.advanceTime(150);
1250
1251 // Commit the previously paused, batched work.
1252 commitFirstRender(['Parent:componentDidMount:one']);
@@ -1252,9 +1254,8 @@ describe('Profiler', () => {
1254 expect(ReactNoop.getRoot('one').current.actualDuration).toBe(6);
1255 expect(ReactNoop.getRoot('two').current.actualDuration).toBe(0);
1256
1255 - ReactNoop.advanceTime(200);
1257 + Scheduler.advanceTime(200);
1258
1257 - expect(ReactNoop).toHaveYielded(['Parent:componentDidMount:one']);
1259 expect(ReactNoop).toFlushAndYield([
1260 'Child:render:two',
1261 'Parent:componentDidMount:two',
@@ -2256,7 +2257,7 @@ describe('Profiler', () => {
2257 await awaitableAdvanceTimers(10000);
2258 // No additional rendering work is required, since we already prepared
2259 // the placeholder.
2259 - expect(ReactNoop.flushExpired()).toEqual([]);
2260 + expect(Scheduler).toHaveYielded([]);
2261 // Should have committed the placeholder.
2262 expect(ReactNoop.getChildrenAsJSX()).toEqual('Loading...Sync');
2263 expect(onRender).toHaveBeenCalledTimes(1);
scripts/jest/matchers/reactTestMatchers.js
+32 -57
@@ -19,16 +19,18 @@ function captureAssertion(fn) {
19 return {pass: true};
20 }
21
22 -function isScheduler(obj) {
23 - return typeof obj.unstable_scheduleCallback === 'function';
24 -}
25 -
26 -function isReactNoop(obj) {
27 - return typeof obj.hasScheduledCallback === 'function';
22 +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;
30 }
31
30 -function assertYieldsWereCleared(ReactNoop) {
31 - const actualYields = ReactNoop.unstable_clearYields();
32 +function assertYieldsWereCleared(Scheduler) {
33 + const actualYields = Scheduler.unstable_clearYields();
34 if (actualYields.length !== 0) {
35 throw new Error(
36 'Log of yielded values is not empty. ' +
@@ -38,81 +40,54 @@ function assertYieldsWereCleared(ReactNoop) {
40 }
41
42 function toFlushAndYield(ReactNoop, expectedYields) {
41 - if (isScheduler(ReactNoop)) {
42 - return SchedulerMatchers.toFlushAndYield(ReactNoop, expectedYields);
43 - }
44 - if (!isReactNoop(ReactNoop)) {
43 + const Scheduler = resolveScheduler(ReactNoop);
44 + if (Scheduler === null) {
45 return JestReact.unstable_toFlushAndYield(ReactNoop, expectedYields);
46 }
47 - assertYieldsWereCleared(ReactNoop);
48 - const actualYields = ReactNoop.unstable_flushWithoutYielding();
49 - return captureAssertion(() => {
50 - expect(actualYields).toEqual(expectedYields);
51 - });
47 + return SchedulerMatchers.toFlushAndYield(Scheduler, expectedYields);
48 }
49
50 function toFlushAndYieldThrough(ReactNoop, expectedYields) {
55 - if (isScheduler(ReactNoop)) {
56 - return SchedulerMatchers.toFlushAndYieldThrough(ReactNoop, expectedYields);
57 - }
58 - if (!isReactNoop(ReactNoop)) {
51 + const Scheduler = resolveScheduler(ReactNoop);
52 + if (Scheduler === null) {
53 return JestReact.unstable_toFlushAndYieldThrough(ReactNoop, expectedYields);
54 }
61 - assertYieldsWereCleared(ReactNoop);
62 - const actualYields = ReactNoop.unstable_flushNumberOfYields(
63 - expectedYields.length
64 - );
65 - return captureAssertion(() => {
66 - expect(actualYields).toEqual(expectedYields);
67 - });
55 + return SchedulerMatchers.toFlushAndYieldThrough(Scheduler, expectedYields);
56 }
57
58 function toFlushWithoutYielding(ReactNoop) {
71 - if (isScheduler(ReactNoop)) {
72 - return SchedulerMatchers.toFlushWithoutYielding(ReactNoop);
73 - }
74 - if (!isReactNoop(ReactNoop)) {
59 + const Scheduler = resolveScheduler(ReactNoop);
60 + if (Scheduler === null) {
61 return JestReact.unstable_toFlushWithoutYielding(ReactNoop);
62 }
77 - return toFlushAndYield(ReactNoop, []);
63 + return SchedulerMatchers.toFlushWithoutYielding(Scheduler);
64 }
65
66 function toHaveYielded(ReactNoop, expectedYields) {
81 - if (isScheduler(ReactNoop)) {
82 - return SchedulerMatchers.toHaveYielded(ReactNoop, expectedYields);
83 - }
84 - if (!isReactNoop(ReactNoop)) {
67 + const Scheduler = resolveScheduler(ReactNoop);
68 + if (Scheduler === null) {
69 return JestReact.unstable_toHaveYielded(ReactNoop, expectedYields);
70 }
87 - return captureAssertion(() => {
88 - const actualYields = ReactNoop.unstable_clearYields();
89 - expect(actualYields).toEqual(expectedYields);
90 - });
71 + return SchedulerMatchers.toHaveYielded(Scheduler, expectedYields);
72 }
73
74 function toFlushAndThrow(ReactNoop, ...rest) {
94 - if (isScheduler(ReactNoop)) {
95 - return SchedulerMatchers.toFlushAndThrow(ReactNoop, ...rest);
96 - }
97 - if (!isReactNoop(ReactNoop)) {
75 + const Scheduler = resolveScheduler(ReactNoop);
76 + if (Scheduler === null) {
77 return JestReact.unstable_toFlushAndThrow(ReactNoop, ...rest);
78 }
100 - assertYieldsWereCleared(ReactNoop);
101 - return captureAssertion(() => {
102 - expect(() => {
103 - ReactNoop.unstable_flushWithoutYielding();
104 - }).toThrow(...rest);
105 - });
79 + return SchedulerMatchers.toFlushAndThrow(Scheduler, ...rest);
80 }
81
82 function toMatchRenderedOutput(ReactNoop, expectedJSX) {
109 - if (!isReactNoop(ReactNoop)) {
110 - return JestReact.unstable_toMatchRenderedOutput(ReactNoop, expectedJSX);
83 + if (typeof ReactNoop.getChildrenAsJSX === 'function') {
84 + const Scheduler = ReactNoop._Scheduler;
85 + assertYieldsWereCleared(Scheduler);
86 + return captureAssertion(() => {
87 + expect(ReactNoop.getChildrenAsJSX()).toEqual(expectedJSX);
88 + });
89 }
112 - assertYieldsWereCleared(ReactNoop);
113 - return captureAssertion(() => {
114 - expect(ReactNoop.getChildrenAsJSX()).toEqual(expectedJSX);
115 - });
90 + return JestReact.unstable_toMatchRenderedOutput(ReactNoop, expectedJSX);
91 }
92
93 module.exports = {
scripts/rollup/forks.js
+5 -1
@@ -169,7 +169,11 @@ const forks = Object.freeze({
169 },
170
171 'scheduler/src/SchedulerHostConfig': (bundleType, entry, dependencies) => {
172 - if (entry === 'scheduler/unstable_mock') {
172 + if (
173 + entry === 'scheduler/unstable_mock' ||
174 + entry === 'react-noop-renderer' ||
175 + entry === 'react-noop-renderer/persistent'
176 + ) {
177 return 'scheduler/src/forks/SchedulerHostConfig.mock';
178 }
179 return 'scheduler/src/forks/SchedulerHostConfig.default';