.',
{withoutStack: true},
);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'0:a:one',
'1:b:two',
// TODO: Before onRecoverableError, this error was never surfaced to the
diff --git a/packages/react-reconciler/src/__tests__/useRef-test.internal.js b/packages/react-reconciler/src/__tests__/useRef-test.internal.js
index 9db0879dfd325..5b792d1439a55 100644
--- a/packages/react-reconciler/src/__tests__/useRef-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/useRef-test.internal.js
@@ -22,6 +22,8 @@ describe('useRef', () => {
let useLayoutEffect;
let useRef;
let useState;
+ let waitForAll;
+ let assertLog;
beforeEach(() => {
React = require('react');
@@ -37,6 +39,10 @@ describe('useRef', () => {
useLayoutEffect = React.useLayoutEffect;
useRef = React.useRef;
useState = React.useState;
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ assertLog = InternalTestUtils.assertLog;
});
function Text(props) {
@@ -79,17 +85,17 @@ describe('useRef', () => {
act(() => {
ReactNoop.render(
);
});
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
ping(1);
ping(2);
ping(3);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
jest.advanceTimersByTime(100);
- expect(Scheduler).toHaveYielded(['ping: 3']);
+ assertLog(['ping: 3']);
ping(4);
jest.advanceTimersByTime(20);
@@ -97,13 +103,13 @@ describe('useRef', () => {
ping(6);
jest.advanceTimersByTime(80);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
jest.advanceTimersByTime(20);
- expect(Scheduler).toHaveYielded(['ping: 6']);
+ assertLog(['ping: 6']);
});
- it('should return the same ref during re-renders', () => {
+ it('should return the same ref during re-renders', async () => {
function Counter() {
const ref = useRef('val');
const [count, setCount] = useState(0);
@@ -121,10 +127,10 @@ describe('useRef', () => {
}
ReactNoop.render(
);
- expect(Scheduler).toFlushAndYield([3]);
+ await waitForAll([3]);
ReactNoop.render(
);
- expect(Scheduler).toFlushAndYield([3]);
+ await waitForAll([3]);
});
if (__DEV__) {
diff --git a/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js b/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js
index 804e40ca21afc..fb94d0fc48721 100644
--- a/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js
+++ b/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js
@@ -20,6 +20,9 @@ let useImperativeHandle;
let useRef;
let useState;
let startTransition;
+let waitFor;
+let waitForAll;
+let assertLog;
// This tests the native useSyncExternalStore implementation, not the shim.
// Tests that apply to both the native implementation and the shim should go
@@ -41,6 +44,11 @@ describe('useSyncExternalStore', () => {
useSyncExternalStore = React.useSyncExternalStore;
startTransition = React.startTransition;
+ const InternalTestUtils = require('internal-test-utils');
+ waitFor = InternalTestUtils.waitFor;
+ waitForAll = InternalTestUtils.waitForAll;
+ assertLog = InternalTestUtils.assertLog;
+
act = require('jest-react').act;
});
@@ -122,13 +130,13 @@ describe('useSyncExternalStore', () => {
root.render(
);
});
- expect(Scheduler).toFlushAndYieldThrough(['A0', 'B0']);
+ await waitFor(['A0', 'B0']);
// During an interleaved event, the store is mutated.
store1.set(1);
// Then we continue rendering.
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
// C reads a newer value from the store than A or B, which means they
// are inconsistent.
'C1',
@@ -152,13 +160,13 @@ describe('useSyncExternalStore', () => {
});
// Start a concurrent render that reads from the store, then yield.
- expect(Scheduler).toFlushAndYieldThrough(['A0', 'B0']);
+ await waitFor(['A0', 'B0']);
// During an interleaved event, the store is mutated.
store2.set(1);
// Then we continue rendering.
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
// C reads a newer value from the store than A or B, which means they
// are inconsistent.
'C1',
@@ -191,17 +199,17 @@ describe('useSyncExternalStore', () => {
// Start a render that reads from the store and yields value
root.render(
);
});
- expect(Scheduler).toHaveYielded(['value:initial']);
+ assertLog(['value:initial']);
await act(() => {
store.set('value:changed');
});
- expect(Scheduler).toHaveYielded(['value:changed']);
+ assertLog(['value:changed']);
// If cached value was updated, we expect a re-render
await act(() => {
store.set('value:initial');
});
- expect(Scheduler).toHaveYielded(['value:initial']);
+ assertLog(['value:initial']);
});
});
diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js
index e44efc2bc11b0..3ae00e284190e 100644
--- a/packages/react-refresh/src/__tests__/ReactFresh-test.js
+++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js
@@ -18,6 +18,8 @@ let ReactFreshRuntime;
let Scheduler;
let act;
let createReactClass;
+let waitFor;
+let assertLog;
describe('ReactFresh', () => {
let container;
@@ -32,6 +34,11 @@ describe('ReactFresh', () => {
ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
act = require('jest-react').act;
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitFor = InternalTestUtils.waitFor;
+ assertLog = InternalTestUtils.assertLog;
+
createReactClass = require('create-react-class/factory')(
React.Component,
React.isValidElement,
@@ -2441,7 +2448,7 @@ describe('ReactFresh', () => {
const root = ReactDOMClient.createRoot(container);
root.render(
);
- expect(Scheduler).toFlushAndYieldThrough(['App#layout']);
+ await waitFor(['App#layout']);
const el = container.firstChild;
expect(el.hidden).toBe(true);
expect(el.firstChild).toBe(null); // Offscreen content not flushed yet.
@@ -2468,7 +2475,7 @@ describe('ReactFresh', () => {
expect(el.firstChild).toBe(null);
// Process the offscreen updates.
- expect(Scheduler).toFlushAndYieldThrough(['Hello#layout']);
+ await waitFor(['Hello#layout']);
expect(container.firstChild).toBe(el);
expect(el.firstChild.textContent).toBe('0');
expect(el.firstChild.style.color).toBe('red');
@@ -2481,7 +2488,7 @@ describe('ReactFresh', () => {
);
});
- expect(Scheduler).toHaveYielded(['Hello#layout']);
+ assertLog(['Hello#layout']);
expect(el.firstChild.textContent).toBe('1');
expect(el.firstChild.style.color).toBe('red');
@@ -2507,7 +2514,7 @@ describe('ReactFresh', () => {
expect(el.firstChild.style.color).toBe('red');
// Process the offscreen updates.
- expect(Scheduler).toFlushAndYieldThrough(['Hello#layout']);
+ await waitFor(['Hello#layout']);
expect(container.firstChild).toBe(el);
expect(el.firstChild.textContent).toBe('1');
expect(el.firstChild.style.color).toBe('orange');
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
index 62f8dbab9528c..c8095b8239627 100644
--- a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
+++ b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
@@ -19,7 +19,9 @@ const {format: prettyFormat} = require('pretty-format');
// Isolate noop renderer
jest.resetModules();
const ReactNoop = require('react-noop-renderer');
-const Scheduler = require('scheduler');
+
+const InternalTestUtils = require('internal-test-utils');
+const waitForAll = InternalTestUtils.waitForAll;
// Kind of hacky, but we nullify all the instances to test the tree structure
// with jasmine's deep equality function, and test the instances separate. We
@@ -1015,7 +1017,7 @@ describe('ReactTestRenderer', () => {
);
});
- it('can concurrently render context with a "primary" renderer', () => {
+ it('can concurrently render context with a "primary" renderer', async () => {
const Context = React.createContext(null);
const Indirection = React.Fragment;
const App = () => (
@@ -1026,7 +1028,7 @@ describe('ReactTestRenderer', () => {
);
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
ReactTestRenderer.create(
);
});
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRendererAct-test.js b/packages/react-test-renderer/src/__tests__/ReactTestRendererAct-test.js
index 4fe1afea6d62f..be940dd63b9c7 100644
--- a/packages/react-test-renderer/src/__tests__/ReactTestRendererAct-test.js
+++ b/packages/react-test-renderer/src/__tests__/ReactTestRendererAct-test.js
@@ -4,6 +4,7 @@ let React;
let ReactTestRenderer;
let Scheduler;
let act;
+let assertLog;
describe('ReactTestRenderer.act()', () => {
beforeEach(() => {
@@ -12,6 +13,9 @@ describe('ReactTestRenderer.act()', () => {
ReactTestRenderer = require('react-test-renderer');
Scheduler = require('scheduler');
act = ReactTestRenderer.act;
+
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
});
// @gate __DEV__
@@ -91,7 +95,7 @@ describe('ReactTestRenderer.act()', () => {
await act(async () => {
root.update(
);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
// Should not flush effects without also flushing microtasks
// First render:
'Effect',
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js b/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
index de0fe8371431f..6f9633c6fea0f 100644
--- a/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
+++ b/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
@@ -13,6 +13,9 @@
let React;
let ReactTestRenderer;
let Scheduler;
+let waitForAll;
+let waitFor;
+let assertLog;
describe('ReactTestRendererAsync', () => {
beforeEach(() => {
@@ -21,9 +24,14 @@ describe('ReactTestRendererAsync', () => {
React = require('react');
ReactTestRenderer = require('react-test-renderer');
Scheduler = require('scheduler');
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ waitFor = InternalTestUtils.waitFor;
+ assertLog = InternalTestUtils.assertLog;
});
- it('flushAll flushes all work', () => {
+ it('flushAll flushes all work', async () => {
function Foo(props) {
return props.children;
}
@@ -35,7 +43,7 @@ describe('ReactTestRendererAsync', () => {
expect(renderer.toJSON()).toEqual(null);
// Flush initial mount.
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(renderer.toJSON()).toEqual('Hi');
// Update
@@ -43,11 +51,11 @@ describe('ReactTestRendererAsync', () => {
// Not yet updated.
expect(renderer.toJSON()).toEqual('Hi');
// Flush update.
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(renderer.toJSON()).toEqual('Bye');
});
- it('flushAll returns array of yielded values', () => {
+ it('flushAll returns array of yielded values', async () => {
function Child(props) {
Scheduler.unstable_yieldValue(props.children);
return props.children;
@@ -65,15 +73,15 @@ describe('ReactTestRendererAsync', () => {
unstable_isConcurrent: true,
});
- expect(Scheduler).toFlushAndYield(['A:1', 'B:1', 'C:1']);
+ await waitForAll(['A:1', 'B:1', 'C:1']);
expect(renderer.toJSON()).toEqual(['A:1', 'B:1', 'C:1']);
renderer.update(
);
- expect(Scheduler).toFlushAndYield(['A:2', 'B:2', 'C:2']);
+ await waitForAll(['A:2', 'B:2', 'C:2']);
expect(renderer.toJSON()).toEqual(['A:2', 'B:2', 'C:2']);
});
- it('flushThrough flushes until the expected values is yielded', () => {
+ it('flushThrough flushes until the expected values is yielded', async () => {
function Child(props) {
Scheduler.unstable_yieldValue(props.children);
return props.children;
@@ -96,16 +104,16 @@ describe('ReactTestRendererAsync', () => {
});
// Flush the first two siblings
- expect(Scheduler).toFlushAndYieldThrough(['A:1', 'B:1']);
+ await waitFor(['A:1', 'B:1']);
// Did not commit yet.
expect(renderer.toJSON()).toEqual(null);
// Flush the remaining work
- expect(Scheduler).toFlushAndYield(['C:1']);
+ await waitForAll(['C:1']);
expect(renderer.toJSON()).toEqual(['A:1', 'B:1', 'C:1']);
});
- it('supports high priority interruptions', () => {
+ it('supports high priority interruptions', async () => {
function Child(props) {
Scheduler.unstable_yieldValue(props.children);
return props.children;
@@ -136,7 +144,7 @@ describe('ReactTestRendererAsync', () => {
});
// Flush the some of the changes, but don't commit
- expect(Scheduler).toFlushAndYieldThrough(['A:1']);
+ await waitFor(['A:1']);
expect(renderer.toJSON()).toEqual(null);
// Interrupt with higher priority properties
@@ -232,12 +240,12 @@ describe('ReactTestRendererAsync', () => {
});
expect(Scheduler).toFlushAndThrow('Oh no!');
- expect(Scheduler).toHaveYielded(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
+ assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
renderer.update(
);
expect(Scheduler).toFlushAndThrow('Oh no!');
- expect(Scheduler).toHaveYielded(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
+ assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
renderer.update(
);
expect(Scheduler).toFlushAndThrow('Oh no!');
diff --git a/packages/scheduler/src/__tests__/SchedulerMock-test.js b/packages/scheduler/src/__tests__/SchedulerMock-test.js
index c52e30e9b1fb7..a7f3ec7f208e5 100644
--- a/packages/scheduler/src/__tests__/SchedulerMock-test.js
+++ b/packages/scheduler/src/__tests__/SchedulerMock-test.js
@@ -21,6 +21,10 @@ let cancelCallback;
let wrapCallback;
let getCurrentPriorityLevel;
let shouldYield;
+let waitForAll;
+let assertLog;
+let waitFor;
+let waitForPaint;
describe('Scheduler', () => {
beforeEach(() => {
@@ -40,20 +44,26 @@ describe('Scheduler', () => {
wrapCallback = Scheduler.unstable_wrapCallback;
getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel;
shouldYield = Scheduler.unstable_shouldYield;
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ assertLog = InternalTestUtils.assertLog;
+ waitFor = InternalTestUtils.waitFor;
+ waitForPaint = InternalTestUtils.waitForPaint;
});
- it('flushes work incrementally', () => {
+ it('flushes work incrementally', async () => {
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('A'));
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('B'));
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('C'));
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('D'));
- expect(Scheduler).toFlushAndYieldThrough(['A', 'B']);
- expect(Scheduler).toFlushAndYieldThrough(['C']);
- expect(Scheduler).toFlushAndYield(['D']);
+ await waitFor(['A', 'B']);
+ await waitFor(['C']);
+ await waitForAll(['D']);
});
- it('cancels work', () => {
+ it('cancels work', async () => {
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('A'));
const callbackHandleB = scheduleCallback(NormalPriority, () =>
Scheduler.unstable_yieldValue('B'),
@@ -62,19 +72,19 @@ describe('Scheduler', () => {
cancelCallback(callbackHandleB);
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'A',
// B should have been cancelled
'C',
]);
});
- it('executes the highest priority callbacks first', () => {
+ it('executes the highest priority callbacks first', async () => {
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('A'));
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('B'));
// Yield before B is flushed
- expect(Scheduler).toFlushAndYieldThrough(['A']);
+ await waitFor(['A']);
scheduleCallback(UserBlockingPriority, () =>
Scheduler.unstable_yieldValue('C'),
@@ -84,10 +94,10 @@ describe('Scheduler', () => {
);
// C and D should come first, because they are higher priority
- expect(Scheduler).toFlushAndYield(['C', 'D', 'B']);
+ await waitForAll(['C', 'D', 'B']);
});
- it('expires work', () => {
+ it('expires work', async () => {
scheduleCallback(NormalPriority, didTimeout => {
Scheduler.unstable_advanceTime(100);
Scheduler.unstable_yieldValue(`A (did timeout: ${didTimeout})`);
@@ -103,7 +113,7 @@ describe('Scheduler', () => {
// Advance time, but not by enough to expire any work
Scheduler.unstable_advanceTime(249);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Schedule a few more callbacks
scheduleCallback(NormalPriority, didTimeout => {
@@ -117,33 +127,27 @@ describe('Scheduler', () => {
// Advance by just a bit more to expire the user blocking callbacks
Scheduler.unstable_advanceTime(1);
- expect(Scheduler).toFlushAndYieldThrough([
- 'B (did timeout: true)',
- 'C (did timeout: true)',
- ]);
+ await waitFor(['B (did timeout: true)', 'C (did timeout: true)']);
// Expire A
Scheduler.unstable_advanceTime(4600);
- expect(Scheduler).toFlushAndYieldThrough(['A (did timeout: true)']);
+ await waitFor(['A (did timeout: true)']);
// Flush the rest without expiring
- expect(Scheduler).toFlushAndYield([
- 'D (did timeout: false)',
- 'E (did timeout: true)',
- ]);
+ await waitForAll(['D (did timeout: false)', 'E (did timeout: true)']);
});
it('has a default expiration of ~5 seconds', () => {
scheduleCallback(NormalPriority, () => Scheduler.unstable_yieldValue('A'));
Scheduler.unstable_advanceTime(4999);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
Scheduler.unstable_advanceTime(1);
expect(Scheduler).toFlushExpired(['A']);
});
- it('continues working on same task after yielding', () => {
+ it('continues working on same task after yielding', async () => {
scheduleCallback(NormalPriority, () => {
Scheduler.unstable_advanceTime(100);
Scheduler.unstable_yieldValue('A');
@@ -184,14 +188,14 @@ describe('Scheduler', () => {
// Flush, then yield while in the middle of C.
expect(didYield).toBe(false);
- expect(Scheduler).toFlushAndYieldThrough(['A', 'B', 'C1']);
+ await waitFor(['A', 'B', 'C1']);
expect(didYield).toBe(true);
// When we resume, we should continue working on C.
- expect(Scheduler).toFlushAndYield(['C2', 'C3', 'D', 'E']);
+ await waitForAll(['C2', 'C3', 'D', 'E']);
});
- it('continuation callbacks inherit the expiration of the previous callback', () => {
+ it('continuation callbacks inherit the expiration of the previous callback', async () => {
const tasks = [
['A', 125],
['B', 124],
@@ -213,14 +217,14 @@ describe('Scheduler', () => {
scheduleCallback(UserBlockingPriority, work);
// Flush until just before the expiration time
- expect(Scheduler).toFlushAndYieldThrough(['A', 'B']);
+ await waitFor(['A', 'B']);
// Advance time by just a bit more. This should expire all the remaining work.
Scheduler.unstable_advanceTime(1);
expect(Scheduler).toFlushExpired(['C', 'D']);
});
- it('continuations are interrupted by higher priority work', () => {
+ it('continuations are interrupted by higher priority work', async () => {
const tasks = [
['A', 100],
['B', 100],
@@ -238,20 +242,20 @@ describe('Scheduler', () => {
}
};
scheduleCallback(NormalPriority, work);
- expect(Scheduler).toFlushAndYieldThrough(['A']);
+ await waitFor(['A']);
scheduleCallback(UserBlockingPriority, () => {
Scheduler.unstable_advanceTime(100);
Scheduler.unstable_yieldValue('High pri');
});
- expect(Scheduler).toFlushAndYield(['High pri', 'B', 'C', 'D']);
+ await waitForAll(['High pri', 'B', 'C', 'D']);
});
it(
'continuations do not block higher priority work scheduled ' +
'inside an executing callback',
- () => {
+ async () => {
const tasks = [
['A', 100],
['B', 100],
@@ -279,7 +283,7 @@ describe('Scheduler', () => {
}
};
scheduleCallback(NormalPriority, work);
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'A',
'B',
'Schedule high pri',
@@ -293,7 +297,7 @@ describe('Scheduler', () => {
},
);
- it('cancelling a continuation', () => {
+ it('cancelling a continuation', async () => {
const task = scheduleCallback(NormalPriority, () => {
Scheduler.unstable_yieldValue('Yield');
return () => {
@@ -301,9 +305,9 @@ describe('Scheduler', () => {
};
});
- expect(Scheduler).toFlushAndYieldThrough(['Yield']);
+ await waitFor(['Yield']);
cancelCallback(task);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
});
it('top-level immediate callbacks fire in a subsequent task', () => {
@@ -320,7 +324,7 @@ describe('Scheduler', () => {
Scheduler.unstable_yieldValue('D'),
);
// Immediate callback hasn't fired, yet.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// They all flush immediately within the subsequent task.
expect(Scheduler).toFlushExpired(['A', 'B', 'C', 'D']);
});
@@ -339,7 +343,7 @@ describe('Scheduler', () => {
scheduleCallback(ImmediatePriority, () =>
Scheduler.unstable_yieldValue('D'),
);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// C should flush at the end
expect(Scheduler).toFlushExpired(['A', 'B', 'D', 'C']);
});
@@ -365,10 +369,10 @@ describe('Scheduler', () => {
);
wrappedCallback();
- expect(Scheduler).toHaveYielded([NormalPriority]);
+ assertLog([NormalPriority]);
wrappedUserBlockingCallback();
- expect(Scheduler).toHaveYielded([UserBlockingPriority]);
+ assertLog([UserBlockingPriority]);
});
it('wrapped callbacks inherit the current priority even when nested', () => {
@@ -387,10 +391,10 @@ describe('Scheduler', () => {
});
wrappedCallback();
- expect(Scheduler).toHaveYielded([NormalPriority]);
+ assertLog([NormalPriority]);
wrappedUserBlockingCallback();
- expect(Scheduler).toHaveYielded([UserBlockingPriority]);
+ assertLog([UserBlockingPriority]);
});
it("immediate callbacks fire even if there's an error", () => {
@@ -407,12 +411,12 @@ describe('Scheduler', () => {
});
expect(() => expect(Scheduler).toFlushExpired()).toThrow('Oops A');
- expect(Scheduler).toHaveYielded(['A']);
+ assertLog(['A']);
// B and C flush in a subsequent event. That way, the second error is not
// swallowed.
expect(() => expect(Scheduler).toFlushExpired()).toThrow('Oops C');
- expect(Scheduler).toHaveYielded(['B', 'C']);
+ assertLog(['B', 'C']);
});
it('multiple immediate callbacks can throw and there will be an error for each one', () => {
@@ -440,7 +444,7 @@ describe('Scheduler', () => {
Scheduler.unstable_yieldValue(getCurrentPriorityLevel());
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
NormalPriority,
ImmediatePriority,
NormalPriority,
@@ -454,7 +458,7 @@ describe('Scheduler', () => {
// priority if you have sourcemaps.
// TODO: Feature temporarily disabled while we investigate a bug in one of
// our minifiers.
- it.skip('adds extra function to the JS stack whose name includes the priority level', () => {
+ it.skip('adds extra function to the JS stack whose name includes the priority level', async () => {
function inferPriorityFromCallstack() {
try {
throw Error();
@@ -508,7 +512,7 @@ describe('Scheduler', () => {
Scheduler.unstable_yieldValue('Idle: ' + inferPriorityFromCallstack()),
);
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'Immediate: ' + ImmediatePriority,
'UserBlocking: ' + UserBlockingPriority,
'Normal: ' + NormalPriority,
@@ -519,7 +523,7 @@ describe('Scheduler', () => {
}
describe('delayed tasks', () => {
- it('schedules a delayed task', () => {
+ it('schedules a delayed task', async () => {
scheduleCallback(
NormalPriority,
() => Scheduler.unstable_yieldValue('A'),
@@ -529,21 +533,21 @@ describe('Scheduler', () => {
);
// Should flush nothing, because delay hasn't elapsed
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Advance time until right before the threshold
Scheduler.unstable_advanceTime(999);
// Still nothing
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Advance time past the threshold
Scheduler.unstable_advanceTime(1);
// Now it should flush like normal
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
});
- it('schedules multiple delayed tasks', () => {
+ it('schedules multiple delayed tasks', async () => {
scheduleCallback(
NormalPriority,
() => Scheduler.unstable_yieldValue('C'),
@@ -577,20 +581,20 @@ describe('Scheduler', () => {
);
// Should flush nothing, because delay hasn't elapsed
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Advance some time.
Scheduler.unstable_advanceTime(200);
// Both A and B are no longer delayed. They can now flush incrementally.
- expect(Scheduler).toFlushAndYieldThrough(['A']);
- expect(Scheduler).toFlushAndYield(['B']);
+ await waitFor(['A']);
+ await waitForAll(['B']);
// Advance the rest
Scheduler.unstable_advanceTime(200);
- expect(Scheduler).toFlushAndYield(['C', 'D']);
+ await waitForAll(['C', 'D']);
});
- it('interleaves normal tasks and delayed tasks', () => {
+ it('interleaves normal tasks and delayed tasks', async () => {
// Schedule some high priority callbacks with a delay. When their delay
// elapses, they will be the most important callback in the queue.
scheduleCallback(
@@ -624,17 +628,10 @@ describe('Scheduler', () => {
// Flush all the work. The timers should be interleaved with the
// other tasks.
- expect(Scheduler).toFlushAndYield([
- 'A',
- 'Timer 1',
- 'B',
- 'C',
- 'Timer 2',
- 'D',
- ]);
+ await waitForAll(['A', 'Timer 1', 'B', 'C', 'Timer 2', 'D']);
});
- it('interleaves delayed tasks with time-sliced tasks', () => {
+ it('interleaves delayed tasks with time-sliced tasks', async () => {
// Schedule some high priority callbacks with a delay. When their delay
// elapses, they will be the most important callback in the queue.
scheduleCallback(
@@ -670,17 +667,10 @@ describe('Scheduler', () => {
// Flush all the work. The timers should be interleaved with the
// other tasks.
- expect(Scheduler).toFlushAndYield([
- 'A',
- 'Timer 1',
- 'B',
- 'C',
- 'Timer 2',
- 'D',
- ]);
+ await waitForAll(['A', 'Timer 1', 'B', 'C', 'Timer 2', 'D']);
});
- it('cancels a delayed task', () => {
+ it('cancels a delayed task', async () => {
// Schedule several tasks with the same delay
const options = {delay: 100};
@@ -701,7 +691,7 @@ describe('Scheduler', () => {
);
// Cancel B before its delay has elapsed
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
cancelCallback(taskB);
// Cancel C after its delay has elapsed
@@ -709,24 +699,24 @@ describe('Scheduler', () => {
cancelCallback(taskC);
// Only A should flush
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
});
- it('gracefully handles scheduled tasks that are not a function', () => {
+ it('gracefully handles scheduled tasks that are not a function', async () => {
scheduleCallback(ImmediatePriority, null);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
scheduleCallback(ImmediatePriority, undefined);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
scheduleCallback(ImmediatePriority, {});
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
scheduleCallback(ImmediatePriority, 42);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
});
- it('toFlushUntilNextPaint stops if a continuation is returned', () => {
+ it('toFlushUntilNextPaint stops if a continuation is returned', async () => {
scheduleCallback(NormalPriority, () => {
Scheduler.unstable_yieldValue('Original Task');
Scheduler.unstable_yieldValue('shouldYield: ' + shouldYield());
@@ -736,7 +726,7 @@ describe('Scheduler', () => {
};
});
- expect(Scheduler).toFlushUntilNextPaint([
+ await waitForPaint([
'Original Task',
// Immediately before returning a continuation, `shouldYield` returns
// false, which means there must be time remaining in the frame.
@@ -750,10 +740,10 @@ describe('Scheduler', () => {
expect(Scheduler.unstable_now()).toBe(0);
// Continue the task
- expect(Scheduler).toFlushAndYield(['Continuation Task']);
+ await waitForAll(['Continuation Task']);
});
- it("toFlushAndYield keeps flushing even if there's a continuation", () => {
+ it("toFlushAndYield keeps flushing even if there's a continuation", async () => {
scheduleCallback(NormalPriority, () => {
Scheduler.unstable_yieldValue('Original Task');
Scheduler.unstable_yieldValue('shouldYield: ' + shouldYield());
@@ -763,7 +753,7 @@ describe('Scheduler', () => {
};
});
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'Original Task',
// Immediately before returning a continuation, `shouldYield` returns
// false, which means there must be time remaining in the frame.
diff --git a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js b/packages/scheduler/src/__tests__/SchedulerProfiling-test.js
index b8eb5d1594c0c..2a7dc3099d234 100644
--- a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js
+++ b/packages/scheduler/src/__tests__/SchedulerProfiling-test.js
@@ -24,6 +24,8 @@ let cancelCallback;
// let wrapCallback;
// let getCurrentPriorityLevel;
// let shouldYield;
+let waitForAll;
+let waitFor;
function priorityLevelToString(priorityLevel) {
switch (priorityLevel) {
@@ -69,6 +71,10 @@ describe('Scheduler', () => {
// wrapCallback = Scheduler.unstable_wrapCallback;
// getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel;
// shouldYield = Scheduler.unstable_shouldYield;
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ waitFor = InternalTestUtils.waitFor;
});
const TaskStartEvent = 1;
@@ -254,7 +260,7 @@ describe('Scheduler', () => {
return '\n' + result;
}
- it('creates a basic flamegraph', () => {
+ it('creates a basic flamegraph', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
Scheduler.unstable_advanceTime(100);
@@ -280,9 +286,9 @@ describe('Scheduler', () => {
},
{label: 'Foo'},
);
- expect(Scheduler).toFlushAndYieldThrough(['Yield 1', 'Yield 3']);
+ await waitFor(['Yield 1', 'Yield 3']);
Scheduler.unstable_advanceTime(100);
- expect(Scheduler).toFlushAndYield(['Yield 2', 'Yield 4']);
+ await waitForAll(['Yield 2', 'Yield 4']);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
@@ -293,7 +299,7 @@ Task 1 [Normal] │ ████████░░░░░░░
);
});
- it('marks when a task is canceled', () => {
+ it('marks when a task is canceled', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
const task = scheduleCallback(NormalPriority, () => {
@@ -306,13 +312,13 @@ Task 1 [Normal] │ ████████░░░░░░░
};
});
- expect(Scheduler).toFlushAndYieldThrough(['Yield 1', 'Yield 2']);
+ await waitFor(['Yield 1', 'Yield 2']);
Scheduler.unstable_advanceTime(100);
cancelCallback(task);
Scheduler.unstable_advanceTime(1000);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
!!! Main thread │░░░░░░██████████████████████
@@ -321,7 +327,7 @@ Task 1 [Normal] │██████░░🡐 canceled
);
});
- it('marks when a task errors', () => {
+ it('marks when a task errors', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
scheduleCallback(NormalPriority, () => {
@@ -333,7 +339,7 @@ Task 1 [Normal] │██████░░🡐 canceled
Scheduler.unstable_advanceTime(100);
Scheduler.unstable_advanceTime(1000);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
!!! Main thread │░░░░░░██████████████████████
@@ -342,7 +348,7 @@ Task 1 [Normal] │██████🡐 errored
);
});
- it('marks when multiple tasks are canceled', () => {
+ it('marks when multiple tasks are canceled', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
const task1 = scheduleCallback(NormalPriority, () => {
@@ -364,7 +370,7 @@ Task 1 [Normal] │██████🡐 errored
};
});
- expect(Scheduler).toFlushAndYieldThrough(['Yield 1', 'Yield 2']);
+ await waitFor(['Yield 1', 'Yield 2']);
Scheduler.unstable_advanceTime(100);
cancelCallback(task1);
@@ -373,7 +379,7 @@ Task 1 [Normal] │██████🡐 errored
// Advance more time. This should not affect the size of the main
// thread row, since the Scheduler queue is empty.
Scheduler.unstable_advanceTime(1000);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
// The main thread row should end when the callback is cancelled.
expect(stopProfilingAndPrintFlamegraph()).toEqual(
@@ -385,14 +391,14 @@ Task 2 [Normal] │░░░░░░░░🡐 canceled
);
});
- it('handles cancelling a task that already finished', () => {
+ it('handles cancelling a task that already finished', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
const task = scheduleCallback(NormalPriority, () => {
Scheduler.unstable_yieldValue('A');
Scheduler.unstable_advanceTime(1000);
});
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
cancelCallback(task);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
@@ -402,7 +408,7 @@ Task 1 [Normal] │████████████████
);
});
- it('handles cancelling a task multiple times', () => {
+ it('handles cancelling a task multiple times', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
scheduleCallback(
@@ -426,7 +432,7 @@ Task 1 [Normal] │████████████████
cancelCallback(task);
cancelCallback(task);
cancelCallback(task);
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
!!! Main thread │████████████░░░░░░░░░░░░░░░░░░░░
@@ -436,7 +442,7 @@ Task 2 [Normal] │ ░░░░░░░░🡐 canceled
);
});
- it('handles delayed tasks', () => {
+ it('handles delayed tasks', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
scheduleCallback(
NormalPriority,
@@ -448,11 +454,11 @@ Task 2 [Normal] │ ░░░░░░░░🡐 canceled
delay: 1000,
},
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
Scheduler.unstable_advanceTime(1000);
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
@@ -462,7 +468,7 @@ Task 1 [Normal] │ █████████
);
});
- it('handles cancelling a delayed task', () => {
+ it('handles cancelling a delayed task', async () => {
Scheduler.unstable_Profiling.startLoggingProfilingEvents();
const task = scheduleCallback(
NormalPriority,
@@ -470,7 +476,7 @@ Task 1 [Normal] │ █████████
{delay: 1000},
);
cancelCallback(task);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(stopProfilingAndPrintFlamegraph()).toEqual(
`
!!! Main thread │
@@ -492,7 +498,7 @@ Task 1 [Normal] │ █████████
taskId++;
const task = scheduleCallback(NormalPriority, () => {});
cancelCallback(task);
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
}
expect(console.error).toHaveBeenCalledTimes(1);
@@ -509,7 +515,7 @@ Task 1 [Normal] │ █████████
scheduleCallback(NormalPriority, () => {
Scheduler.unstable_advanceTime(1000);
});
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Note: The exact task id is not super important. That just how many tasks
// it happens to take before the array is resized.
diff --git a/packages/use-subscription/src/__tests__/useSubscription-test.js b/packages/use-subscription/src/__tests__/useSubscription-test.js
index aefef75ff9fd7..a4b73eaa819d3 100644
--- a/packages/use-subscription/src/__tests__/useSubscription-test.js
+++ b/packages/use-subscription/src/__tests__/useSubscription-test.js
@@ -16,6 +16,9 @@ let React;
let ReactTestRenderer;
let Scheduler;
let ReplaySubject;
+let assertLog;
+let waitForAll;
+let waitFor;
describe('useSubscription', () => {
beforeEach(() => {
@@ -31,6 +34,11 @@ describe('useSubscription', () => {
BehaviorSubject = require('rxjs').BehaviorSubject;
ReplaySubject = require('rxjs').ReplaySubject;
+
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ assertLog = InternalTestUtils.assertLog;
+ waitFor = InternalTestUtils.waitFor;
});
function createBehaviorSubject(initialValue) {
@@ -49,7 +57,7 @@ describe('useSubscription', () => {
return replaySubject;
}
- it('supports basic subscription pattern', () => {
+ it('supports basic subscription pattern', async () => {
function Child({value = 'default'}) {
Scheduler.unstable_yieldValue(value);
return null;
@@ -79,21 +87,21 @@ describe('useSubscription', () => {
{unstable_isConcurrent: true},
);
});
- expect(Scheduler).toHaveYielded(['default']);
+ assertLog(['default']);
// Updates while subscribed should re-render the child component
act(() => observable.next(123));
- expect(Scheduler).toHaveYielded([123]);
+ assertLog([123]);
act(() => observable.next('abc'));
- expect(Scheduler).toHaveYielded(['abc']);
+ assertLog(['abc']);
// Unmounting the subscriber should remove listeners
act(() => renderer.update(
));
act(() => observable.next(456));
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
});
- it('should support observable types like RxJS ReplaySubject', () => {
+ it('should support observable types like RxJS ReplaySubject', async () => {
function Child({value = 'default'}) {
Scheduler.unstable_yieldValue(value);
return null;
@@ -131,19 +139,19 @@ describe('useSubscription', () => {
{unstable_isConcurrent: true},
);
});
- expect(Scheduler).toHaveYielded(['initial']);
+ assertLog(['initial']);
act(() => observable.next('updated'));
- expect(Scheduler).toHaveYielded(['updated']);
+ assertLog(['updated']);
- Scheduler.unstable_flushAll();
+ await waitForAll([]);
// Unsetting the subscriber prop should reset subscribed values
observable = createReplaySubject(undefined);
act(() => renderer.update(
));
- expect(Scheduler).toHaveYielded(['default']);
+ assertLog(['default']);
});
- it('should unsubscribe from old sources and subscribe to new sources when memoized props change', () => {
+ it('should unsubscribe from old sources and subscribe to new sources when memoized props change', async () => {
function Child({value = 'default'}) {
Scheduler.unstable_yieldValue(value);
return null;
@@ -182,29 +190,29 @@ describe('useSubscription', () => {
});
// Updates while subscribed should re-render the child component
- expect(Scheduler).toHaveYielded(['a-0']);
+ assertLog(['a-0']);
expect(subscriptions).toHaveLength(1);
expect(subscriptions[0]).toBe(observableA);
// Unsetting the subscriber prop should reset subscribed values
act(() => renderer.update(
));
- expect(Scheduler).toHaveYielded(['b-0']);
+ assertLog(['b-0']);
expect(subscriptions).toHaveLength(2);
expect(subscriptions[1]).toBe(observableB);
// Updates to the old subscribable should not re-render the child component
act(() => observableA.next('a-1'));
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Updates to the bew subscribable should re-render the child component
act(() => observableB.next('b-1'));
- expect(Scheduler).toHaveYielded(['b-1']);
+ assertLog(['b-1']);
expect(subscriptions).toHaveLength(2);
});
- it('should unsubscribe from old sources and subscribe to new sources when useCallback functions change', () => {
+ it('should unsubscribe from old sources and subscribe to new sources when useCallback functions change', async () => {
function Child({value = 'default'}) {
Scheduler.unstable_yieldValue(value);
return null;
@@ -241,28 +249,28 @@ describe('useSubscription', () => {
});
// Updates while subscribed should re-render the child component
- expect(Scheduler).toHaveYielded(['a-0']);
+ assertLog(['a-0']);
expect(subscriptions).toHaveLength(1);
expect(subscriptions[0]).toBe(observableA);
// Unsetting the subscriber prop should reset subscribed values
act(() => renderer.update(
));
- expect(Scheduler).toHaveYielded(['b-0']);
+ assertLog(['b-0']);
expect(subscriptions).toHaveLength(2);
expect(subscriptions[1]).toBe(observableB);
// Updates to the old subscribable should not re-render the child component
act(() => observableA.next('a-1'));
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
// Updates to the bew subscribable should re-render the child component
act(() => observableB.next('b-1'));
- expect(Scheduler).toHaveYielded(['b-1']);
+ assertLog(['b-1']);
expect(subscriptions).toHaveLength(2);
});
- it('should ignore values emitted by a new subscribable until the commit phase', () => {
+ it('should ignore values emitted by a new subscribable until the commit phase', async () => {
const log = [];
function Grandchild({value}) {
@@ -326,16 +334,16 @@ describe('useSubscription', () => {
unstable_isConcurrent: true,
});
});
- expect(Scheduler).toHaveYielded(['Child: a-0', 'Grandchild: a-0']);
+ assertLog(['Child: a-0', 'Grandchild: a-0']);
expect(log).toEqual(['Parent.componentDidMount']);
// Start React update, but don't finish
- act(() => {
+ await act(async () => {
React.startTransition(() => {
renderer.update(
);
});
- expect(Scheduler).toFlushAndYieldThrough(['Child: b-0']);
+ await waitFor(['Child: b-0']);
expect(log).toEqual(['Parent.componentDidMount']);
// Emit some updates from the uncommitted subscribable
@@ -351,7 +359,7 @@ describe('useSubscription', () => {
// We expect the last emitted update to be rendered (because of the commit phase value check)
// But the intermediate ones should be ignored,
// And the final rendered output should be the higher-priority observable.
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Grandchild: b-0',
'Child: b-3',
'Grandchild: b-3',
@@ -365,7 +373,7 @@ describe('useSubscription', () => {
]);
});
- it('should not drop values emitted between updates', () => {
+ it('should not drop values emitted between updates', async () => {
const log = [];
function Grandchild({value}) {
@@ -429,16 +437,16 @@ describe('useSubscription', () => {
unstable_isConcurrent: true,
});
});
- expect(Scheduler).toHaveYielded(['Child: a-0', 'Grandchild: a-0']);
+ assertLog(['Child: a-0', 'Grandchild: a-0']);
expect(log).toEqual(['Parent.componentDidMount:a-0']);
log.splice(0);
// Start React update, but don't finish
- act(() => {
+ await act(async () => {
React.startTransition(() => {
renderer.update(
);
});
- expect(Scheduler).toFlushAndYieldThrough(['Child: b-0']);
+ await waitFor(['Child: b-0']);
expect(log).toEqual([]);
// Emit some updates from the old subscribable
@@ -455,7 +463,7 @@ describe('useSubscription', () => {
}
// Flush everything and ensure that the correct subscribable is used
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'Child: a-2',
'Grandchild: a-2',
'Child: a-2',
@@ -467,7 +475,7 @@ describe('useSubscription', () => {
// Updates from the new subscribable should be ignored.
log.splice(0);
act(() => observableB.next('b-1'));
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
expect(log).toEqual([]);
});
@@ -514,10 +522,9 @@ describe('useSubscription', () => {
},
};
- eventHandler.subscribe(value => {
+ eventHandler.subscribe(async value => {
if (value === false) {
renderer.unmount();
- expect(Scheduler).toFlushAndYield([]);
}
});
@@ -528,13 +535,13 @@ describe('useSubscription', () => {
{unstable_isConcurrent: true},
);
});
- expect(Scheduler).toHaveYielded([true]);
+ assertLog([true]);
// This event should unmount
eventHandler.change(false);
});
- it('does not return a value from the previous subscription if the source is updated', () => {
+ it('does not return a value from the previous subscription if the source is updated', async () => {
const subscription1 = {
getCurrentValue: () => 'one',
subscribe: () => () => {},
@@ -562,13 +569,13 @@ describe('useSubscription', () => {
{unstable_isConcurrent: true},
);
});
- Scheduler.unstable_flushAll();
+ await waitForAll([]);
act(() => renderer.update(
));
- Scheduler.unstable_flushAll();
+ await waitForAll([]);
});
- it('should not tear if a mutation occurs during a concurrent update', () => {
+ it('should not tear if a mutation occurs during a concurrent update', async () => {
const input = document.createElement('input');
const mutate = value => {
@@ -590,7 +597,7 @@ describe('useSubscription', () => {
return value;
};
- act(() => {
+ await act(async () => {
// Initial render of "A"
mutate('A');
ReactTestRenderer.create(
@@ -600,13 +607,13 @@ describe('useSubscription', () => {
,
{unstable_isConcurrent: true},
);
- expect(Scheduler).toFlushAndYield(['render:first:A', 'render:second:A']);
+ await waitForAll(['render:first:A', 'render:second:A']);
// Update state "A" -> "B"
// This update will be eagerly evaluated,
// so the tearing case this test is guarding against would not happen.
mutate('B');
- expect(Scheduler).toFlushAndYield(['render:first:B', 'render:second:B']);
+ await waitForAll(['render:first:B', 'render:second:B']);
// No more pending updates
jest.runAllTimers();
@@ -618,14 +625,11 @@ describe('useSubscription', () => {
React.startTransition(() => {
mutate('C');
});
- expect(Scheduler).toFlushAndYieldThrough([
- 'render:first:C',
- 'render:second:C',
- ]);
+ await waitFor(['render:first:C', 'render:second:C']);
React.startTransition(() => {
mutate('D');
});
- expect(Scheduler).toFlushAndYield(['render:first:D', 'render:second:D']);
+ await waitForAll(['render:first:D', 'render:second:D']);
// No more pending updates
jest.runAllTimers();
diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js
index cd033bfda3dea..88665188342fe 100644
--- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js
+++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js
@@ -17,6 +17,7 @@ let Scheduler;
let useSyncExternalStore;
let useSyncExternalStoreWithSelector;
let act;
+let assertLog;
// This tests the userspace shim of `useSyncExternalStore` in a server-rendering
// (Node) environment
@@ -50,6 +51,9 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+
if (gate(flags => flags.source)) {
// The `shim/with-selector` module composes the main
// `use-sync-external-store` entrypoint. In the compiled artifacts, this
@@ -116,7 +120,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
await act(() => {
root.render(
);
});
- expect(Scheduler).toHaveYielded(['client']);
+ assertLog(['client']);
expect(root).toMatchRenderedOutput('client');
});
@@ -159,7 +163,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
const root = ReactNoop.createRoot();
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['A0', 'B0']);
+ assertLog(['A0', 'B0']);
expect(root).toMatchRenderedOutput('A0B0');
// Update b but not a
@@ -167,7 +171,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
store.set({a: 0, b: 1});
});
// Only b re-renders
- expect(Scheduler).toHaveYielded(['B1']);
+ assertLog(['B1']);
expect(root).toMatchRenderedOutput('A0B1');
// Update a but not b
@@ -175,7 +179,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
store.set({a: 1, b: 1});
});
// Only a re-renders
- expect(Scheduler).toHaveYielded(['A1']);
+ assertLog(['A1']);
expect(root).toMatchRenderedOutput('A1B1');
});
});
diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js
index 93f022870d01f..7abe12bd5e65f 100644
--- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js
+++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js
@@ -20,6 +20,7 @@ let act;
let useState;
let useEffect;
let useLayoutEffect;
+let assertLog;
// This tests shared behavior between the built-in and shim implementations of
// of useSyncExternalStore.
@@ -55,6 +56,9 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
useEffect = React.useEffect;
useLayoutEffect = React.useLayoutEffect;
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+
const internalAct = require('jest-react').act;
// The internal act implementation doesn't batch updates by default, since
@@ -140,13 +144,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
await act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['Initial']);
+ assertLog(['Initial']);
expect(container.textContent).toEqual('Initial');
await act(() => {
store.set('Updated');
});
- expect(Scheduler).toHaveYielded(['Updated']);
+ assertLog(['Updated']);
expect(container.textContent).toEqual('Updated');
});
@@ -162,7 +166,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['Initial']);
+ assertLog(['Initial']);
expect(container.textContent).toEqual('Initial');
// Update to the same value
@@ -170,7 +174,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set('Initial');
});
// Should not re-render
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
expect(container.textContent).toEqual('Initial');
});
@@ -190,13 +194,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
await act(() => root.render(
));
- expect(Scheduler).toHaveYielded([0]);
+ assertLog([0]);
expect(container.textContent).toEqual('0');
await act(() => {
storeA.set(1);
});
- expect(Scheduler).toHaveYielded([1]);
+ assertLog([1]);
expect(container.textContent).toEqual('1');
// Switch stores and update in the same batch
@@ -208,7 +212,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
});
});
// Now reading from B instead of A
- expect(Scheduler).toHaveYielded([0]);
+ assertLog([0]);
expect(container.textContent).toEqual('0');
// Update A
@@ -216,14 +220,14 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
storeA.set(3);
});
// Nothing happened, because we're no longer subscribed to A
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
expect(container.textContent).toEqual('0');
// Update B
await act(() => {
storeB.set(1);
});
- expect(Scheduler).toHaveYielded([1]);
+ assertLog([1]);
expect(container.textContent).toEqual('1');
});
@@ -252,7 +256,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['A0', 'B0']);
+ assertLog(['A0', 'B0']);
expect(container.textContent).toEqual('A0B0');
// Update b but not a
@@ -260,7 +264,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set({a: 0, b: 1});
});
// Only b re-renders
- expect(Scheduler).toHaveYielded(['B1']);
+ assertLog(['B1']);
expect(container.textContent).toEqual('A0B1');
// Update a but not b
@@ -268,7 +272,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set({a: 1, b: 1});
});
// Only a re-renders
- expect(Scheduler).toHaveYielded(['A1']);
+ assertLog(['A1']);
expect(container.textContent).toEqual('A1B1');
});
@@ -292,13 +296,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const container = document.createElement('div');
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded([0, 'Passive effect: 0']);
+ assertLog([0, 'Passive effect: 0']);
// Schedule an update. We'll intentionally not use `act` so that we can
// insert a mutation before React subscribes to the store in a
// passive effect.
store.set(1);
- expect(Scheduler).toHaveYielded([
+ assertLog([
1,
// Passive effect hasn't fired yet
]);
@@ -306,7 +310,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
// Flip the store state back to the previous value.
store.set(0);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Passive effect: 1',
// Re-render. If the current state were tracked by updating a ref in a
// passive effect, then this would break because the previous render's
@@ -362,14 +366,14 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const container = document.createElement('div');
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['A1']);
+ assertLog(['A1']);
expect(container.textContent).toEqual('A1');
act(() => {
// Change getSnapshot and update the store in the same batch
setStep(1);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
'B1',
'Update B in commit phase',
// If Child2 had used the old getSnapshot to bail out, then it would have
@@ -420,7 +424,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const container = document.createElement('div');
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['A1']);
+ assertLog(['A1']);
expect(container.textContent).toEqual('A1');
// This will cause a layout effect, and in the layout effect we'll update
@@ -428,7 +432,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
act(() => {
setStep(1);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
'A1',
// This updates B, but since Child2 doesn't subscribe to B, it doesn't
// need to re-render.
@@ -467,13 +471,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
>,
),
);
- expect(Scheduler).toHaveYielded([0, 0]);
+ assertLog([0, 0]);
expect(container.textContent).toEqual('00');
await act(() => {
store.set(1);
});
- expect(Scheduler).toHaveYielded([1, 1, 'Reset back to 0', 0, 0]);
+ assertLog([1, 1, 'Reset back to 0', 0, 0]);
expect(container.textContent).toEqual('00');
});
@@ -494,7 +498,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const container = document.createElement('div');
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded([0]);
+ assertLog([0]);
// Update the store and getSnapshot at the same time
act(() => {
@@ -504,7 +508,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
});
});
// It should read from B instead of A
- expect(Scheduler).toHaveYielded([2]);
+ assertLog([2]);
expect(container.textContent).toEqual('2');
});
@@ -549,7 +553,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
,
),
);
- expect(Scheduler).toHaveYielded([0]);
+ assertLog([0]);
expect(container.textContent).toEqual('0');
// Update that throws in a getSnapshot. We can catch it with an error boundary.
@@ -557,14 +561,14 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set({value: 1, throwInGetSnapshot: true, throwInIsEqual: false});
});
if (gate(flags => !flags.enableUseSyncExternalStoreShim)) {
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Error in getSnapshot',
// In a concurrent root, React renders a second time to attempt to
// recover from the error.
'Error in getSnapshot',
]);
} else {
- expect(Scheduler).toHaveYielded(['Error in getSnapshot']);
+ assertLog(['Error in getSnapshot']);
}
expect(container.textContent).toEqual('Error in getSnapshot');
});
@@ -644,14 +648,14 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['App', 'Selector', 'A0']);
+ assertLog(['App', 'Selector', 'A0']);
expect(container.textContent).toEqual('A0');
// Update the store
await act(() => {
store.set({a: 1, b: 0});
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
// The selector runs before React starts rendering
'Selector',
'App',
@@ -703,7 +707,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['A0', 'B0']);
+ assertLog(['A0', 'B0']);
expect(container.textContent).toEqual('A0B0');
// Update b but not a
@@ -711,7 +715,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set({a: 0, b: 1});
});
// Only b re-renders
- expect(Scheduler).toHaveYielded(['B1']);
+ assertLog(['B1']);
expect(container.textContent).toEqual('A0B1');
// Update a but not b
@@ -719,7 +723,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
store.set({a: 1, b: 1});
});
// Only a re-renders
- expect(Scheduler).toHaveYielded(['A1']);
+ assertLog(['A1']);
expect(container.textContent).toEqual('A1B1');
});
@@ -751,7 +755,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
act(() => {
ReactDOMClient.hydrateRoot(container,
);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
// First it hydrates the server rendered HTML
'server',
'Passive effect: server',
@@ -769,7 +773,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
'Text content did not match',
);
});
- expect(Scheduler).toHaveYielded(['client', 'Passive effect: client']);
+ assertLog(['client', 'Passive effect: client']);
}
expect(container.textContent).toEqual('client');
expect(ref.current).toEqual(serverRenderedDiv);
@@ -793,13 +797,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
const root = createRoot(container);
await act(() => root.render(
));
- expect(Scheduler).toHaveYielded(['INITIAL']);
+ assertLog(['INITIAL']);
expect(container.textContent).toEqual('INITIAL');
await act(() => {
store.set('Updated');
});
- expect(Scheduler).toHaveYielded(['UPDATED']);
+ assertLog(['UPDATED']);
expect(container.textContent).toEqual('UPDATED');
});
@@ -857,18 +861,12 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
await act(() => {
root.render(
);
});
- expect(Scheduler).toHaveYielded([
- 'Inline selector',
- 'A',
- 'B',
- 'C',
- 'Sibling: 0',
- ]);
+ assertLog(['Inline selector', 'A', 'B', 'C', 'Sibling: 0']);
await act(() => {
root.render(
);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
// We had to call the selector again because it's not memoized
'Inline selector',
diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js
index a0af5e70455ef..f5202852c68fc 100644
--- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js
+++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShimServer-test.js
@@ -16,6 +16,7 @@ let React;
let ReactDOM;
let ReactDOMServer;
let Scheduler;
+let assertLog;
// This tests the userspace shim of `useSyncExternalStore` in a server-rendering
// (Node) environment
@@ -45,6 +46,9 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
ReactDOMServer = require('react-dom/server');
Scheduler = require('scheduler');
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+
useSyncExternalStore =
require('use-sync-external-store/shim').useSyncExternalStore;
});
@@ -92,7 +96,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
const html = ReactDOMServer.renderToString(
);
// We don't call getServerSnapshot in the shim
- expect(Scheduler).toHaveYielded(['client']);
+ assertLog(['client']);
expect(html).toEqual('client');
});
});