Codemod tests to waitFor pattern (7/?) (#26307)

This converts some of our test suite to use the `waitFor` test pattern,
instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of
these changes are automated with jscodeshift, with some slight manual
cleanup in certain cases.

See #26285 for full context.
This commit is contained in:
Andrew Clark 2023-03-04 18:03:24 -05:00 committed by GitHub
parent e98695db91
commit 3cb5afb82e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 635 additions and 737 deletions

View File

@ -15,6 +15,8 @@ let ReactDOMClient;
let Scheduler;
let act;
let container;
let waitForAll;
let assertLog;
describe('ReactSuspenseEffectsSemanticsDOM', () => {
beforeEach(() => {
@ -26,6 +28,10 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
container = document.createElement('div');
document.body.appendChild(container);
});
@ -139,23 +145,23 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
act(() => {
root.render(<Parent swap={false} />);
});
expect(Scheduler).toHaveYielded(['Loading...']);
assertLog(['Loading...']);
await LazyChildA;
expect(Scheduler).toFlushAndYield(['A', 'Ref mount: A']);
await waitForAll(['A', 'Ref mount: A']);
expect(container.innerHTML).toBe('<span>A</span>');
// Swap the position of A and B
ReactDOM.flushSync(() => {
root.render(<Parent swap={true} />);
});
expect(Scheduler).toHaveYielded(['Loading...', 'Ref unmount: A']);
assertLog(['Loading...', 'Ref unmount: A']);
expect(container.innerHTML).toBe(
'<span style="display: none;">A</span>Loading...',
);
await LazyChildB;
expect(Scheduler).toFlushAndYield(['B', 'Ref mount: B']);
await waitForAll(['B', 'Ref mount: B']);
expect(container.innerHTML).toBe('<span>B</span>');
});
@ -199,21 +205,21 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
act(() => {
root.render(<Parent swap={false} />);
});
expect(Scheduler).toHaveYielded(['Loading...']);
assertLog(['Loading...']);
await LazyChildA;
expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']);
await waitForAll(['A', 'Did mount: A']);
expect(container.innerHTML).toBe('A');
// Swap the position of A and B
ReactDOM.flushSync(() => {
root.render(<Parent swap={true} />);
});
expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']);
assertLog(['Loading...', 'Will unmount: A']);
expect(container.innerHTML).toBe('Loading...');
await LazyChildB;
expect(Scheduler).toFlushAndYield(['B', 'Did mount: B']);
await waitForAll(['B', 'Did mount: B']);
expect(container.innerHTML).toBe('B');
});
@ -251,24 +257,24 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
act(() => {
root.render(<Parent swap={false} />);
});
expect(Scheduler).toHaveYielded(['Loading...']);
assertLog(['Loading...']);
await LazyChildA;
expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']);
await waitForAll(['A', 'Did mount: A']);
expect(container.innerHTML).toBe('A');
// Swap the position of A and B
ReactDOM.flushSync(() => {
root.render(<Parent swap={true} />);
});
expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']);
assertLog(['Loading...', 'Will unmount: A']);
expect(container.innerHTML).toBe('Loading...');
// Destroy the whole tree, including the hidden A
ReactDOM.flushSync(() => {
root.render(<h1>Hello</h1>);
});
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(container.innerHTML).toBe('<h1>Hello</h1>');
});
@ -318,17 +324,17 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
act(() => {
root.render(<Parent swap={false} />);
});
expect(Scheduler).toHaveYielded(['Loading...']);
assertLog(['Loading...']);
await LazyChildA;
expect(Scheduler).toFlushAndYield(['A', 'Ref mount: A']);
await waitForAll(['A', 'Ref mount: A']);
expect(container.innerHTML).toBe('<span>A</span>');
// Swap the position of A and B
ReactDOM.flushSync(() => {
root.render(<Parent swap={true} />);
});
expect(Scheduler).toHaveYielded(['Loading...', 'Ref unmount: A']);
assertLog(['Loading...', 'Ref unmount: A']);
expect(container.innerHTML).toBe(
'<span style="display: none;">A</span>Loading...',
);
@ -337,7 +343,7 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
ReactDOM.flushSync(() => {
root.render(<h1>Hello</h1>);
});
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(container.innerHTML).toBe('<h1>Hello</h1>');
});
@ -381,24 +387,24 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
act(() => {
root.render(<Parent swap={false} />);
});
expect(Scheduler).toHaveYielded(['Loading...']);
assertLog(['Loading...']);
await LazyChildA;
expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']);
await waitForAll(['A', 'Did mount: A']);
expect(container.innerHTML).toBe('A');
// Swap the position of A and B
ReactDOM.flushSync(() => {
root.render(<Parent swap={true} />);
});
expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']);
assertLog(['Loading...', 'Will unmount: A']);
expect(container.innerHTML).toBe('Loading...');
// Destroy the whole tree, including the hidden A
ReactDOM.flushSync(() => {
root.render(<h1>Hello</h1>);
});
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(container.innerHTML).toBe('<h1>Hello</h1>');
});
@ -432,12 +438,12 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
// Initial render
ReactDOM.render(<App showMore={false} />, container);
expect(Scheduler).toHaveYielded(['Child', 'Mount']);
assertLog(['Child', 'Mount']);
// Update that suspends, causing the existing tree to switches it to
// a fallback.
ReactDOM.render(<App showMore={true} />, container);
expect(Scheduler).toHaveYielded([
assertLog([
'Child',
'Loading...',
@ -448,6 +454,6 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
// Delete the tree and unmount the effect
ReactDOM.render(null, container);
expect(Scheduler).toHaveYielded(['Unmount']);
assertLog(['Unmount']);
});
});

View File

@ -13,6 +13,7 @@ let Suspense;
let getCacheForType;
let caches;
let seededCache;
let waitForAll;
describe('ReactSuspenseFallback', () => {
beforeEach(() => {
@ -25,6 +26,9 @@ describe('ReactSuspenseFallback', () => {
getCacheForType = React.unstable_getCacheForType;
caches = [];
seededCache = null;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
});
function createTextCache() {
@ -128,26 +132,26 @@ describe('ReactSuspenseFallback', () => {
}
// @gate enableLegacyCache
it('suspends and shows fallback', () => {
it('suspends and shows fallback', async () => {
ReactNoop.render(
<Suspense fallback={<Text text="Loading..." />}>
<AsyncText text="A" ms={100} />
</Suspense>,
);
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
await waitForAll(['Suspend! [A]', 'Loading...']);
expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
});
// @gate enableLegacyCache
it('suspends and shows null fallback', () => {
it('suspends and shows null fallback', async () => {
ReactNoop.render(
<Suspense fallback={null}>
<AsyncText text="A" ms={100} />
</Suspense>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend! [A]',
// null
]);
@ -155,14 +159,14 @@ describe('ReactSuspenseFallback', () => {
});
// @gate enableLegacyCache
it('suspends and shows undefined fallback', () => {
it('suspends and shows undefined fallback', async () => {
ReactNoop.render(
<Suspense>
<AsyncText text="A" ms={100} />
</Suspense>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend! [A]',
// null
]);
@ -170,7 +174,7 @@ describe('ReactSuspenseFallback', () => {
});
// @gate enableLegacyCache
it('suspends and shows inner fallback', () => {
it('suspends and shows inner fallback', async () => {
ReactNoop.render(
<Suspense fallback={<Text text="Should not show..." />}>
<Suspense fallback={<Text text="Loading..." />}>
@ -179,12 +183,12 @@ describe('ReactSuspenseFallback', () => {
</Suspense>,
);
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
await waitForAll(['Suspend! [A]', 'Loading...']);
expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
});
// @gate enableLegacyCache
it('suspends and shows inner undefined fallback', () => {
it('suspends and shows inner undefined fallback', async () => {
ReactNoop.render(
<Suspense fallback={<Text text="Should not show..." />}>
<Suspense>
@ -193,7 +197,7 @@ describe('ReactSuspenseFallback', () => {
</Suspense>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend! [A]',
// null
]);
@ -201,7 +205,7 @@ describe('ReactSuspenseFallback', () => {
});
// @gate enableLegacyCache
it('suspends and shows inner null fallback', () => {
it('suspends and shows inner null fallback', async () => {
ReactNoop.render(
<Suspense fallback={<Text text="Should not show..." />}>
<Suspense fallback={null}>
@ -210,7 +214,7 @@ describe('ReactSuspenseFallback', () => {
</Suspense>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend! [A]',
// null
]);

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@ let ReactCache;
let Suspense;
let TextResource;
let textResourceShouldFail;
let waitForAll;
let assertLog;
describe('ReactSuspensePlaceholder', () => {
beforeEach(() => {
@ -34,6 +36,10 @@ describe('ReactSuspensePlaceholder', () => {
Profiler = React.Profiler;
Suspense = React.Suspense;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
TextResource = ReactCache.unstable_createResource(
([text, ms = 0]) => {
let listeners = null;
@ -106,7 +112,7 @@ describe('ReactSuspensePlaceholder', () => {
}
}
it('times out children that are already hidden', () => {
it('times out children that are already hidden', async () => {
class HiddenText extends React.PureComponent {
render() {
const text = this.props.text;
@ -132,13 +138,13 @@ describe('ReactSuspensePlaceholder', () => {
// Initial mount
ReactNoop.render(<App middleText="B" />);
expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'C', 'Loading...']);
await waitForAll(['A', 'Suspend! [B]', 'C', 'Loading...']);
expect(ReactNoop).toMatchRenderedOutput('Loading...');
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
assertLog(['Promise resolved [B]']);
expect(Scheduler).toFlushAndYield(['A', 'B', 'C']);
await waitForAll(['A', 'B', 'C']);
expect(ReactNoop).toMatchRenderedOutput(
<>
@ -150,11 +156,11 @@ describe('ReactSuspensePlaceholder', () => {
// Update
ReactNoop.render(<App middleText="B2" />);
expect(Scheduler).toFlushAndYield(['Suspend! [B2]', 'C', 'Loading...']);
await waitForAll(['Suspend! [B2]', 'C', 'Loading...']);
// Time out the update
jest.advanceTimersByTime(750);
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span hidden={true}>A</span>
@ -166,8 +172,8 @@ describe('ReactSuspensePlaceholder', () => {
// Resolve the promise
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [B2]']);
expect(Scheduler).toFlushAndYield(['B2', 'C']);
assertLog(['Promise resolved [B2]']);
await waitForAll(['B2', 'C']);
// Render the final update. A should still be hidden, because it was
// given a `hidden` prop.
@ -194,39 +200,34 @@ describe('ReactSuspensePlaceholder', () => {
// Initial mount
ReactNoop.render(<App middleText="B" />);
expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'C', 'Loading...']);
await waitForAll(['A', 'Suspend! [B]', 'C', 'Loading...']);
expect(ReactNoop).not.toMatchRenderedOutput('ABC');
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
expect(Scheduler).toFlushAndYield(['A', 'B', 'C']);
assertLog(['Promise resolved [B]']);
await waitForAll(['A', 'B', 'C']);
expect(ReactNoop).toMatchRenderedOutput('ABC');
// Update
ReactNoop.render(<App middleText="B2" />);
expect(Scheduler).toFlushAndYield([
'A',
'Suspend! [B2]',
'C',
'Loading...',
]);
await waitForAll(['A', 'Suspend! [B2]', 'C', 'Loading...']);
// Time out the update
jest.advanceTimersByTime(750);
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput('Loading...');
// Resolve the promise
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [B2]']);
expect(Scheduler).toFlushAndYield(['A', 'B2', 'C']);
assertLog(['Promise resolved [B2]']);
await waitForAll(['A', 'B2', 'C']);
// Render the final update. A should still be hidden, because it was
// given a `hidden` prop.
expect(ReactNoop).toMatchRenderedOutput('AB2C');
});
it('preserves host context for text nodes', () => {
it('preserves host context for text nodes', async () => {
function App(props) {
return (
// uppercase is a special type that causes React Noop to render child
@ -244,32 +245,27 @@ describe('ReactSuspensePlaceholder', () => {
// Initial mount
ReactNoop.render(<App middleText="b" />);
expect(Scheduler).toFlushAndYield(['a', 'Suspend! [b]', 'c', 'Loading...']);
await waitForAll(['a', 'Suspend! [b]', 'c', 'Loading...']);
expect(ReactNoop).toMatchRenderedOutput(<uppercase>LOADING...</uppercase>);
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [b]']);
expect(Scheduler).toFlushAndYield(['a', 'b', 'c']);
assertLog(['Promise resolved [b]']);
await waitForAll(['a', 'b', 'c']);
expect(ReactNoop).toMatchRenderedOutput(<uppercase>ABC</uppercase>);
// Update
ReactNoop.render(<App middleText="b2" />);
expect(Scheduler).toFlushAndYield([
'a',
'Suspend! [b2]',
'c',
'Loading...',
]);
await waitForAll(['a', 'Suspend! [b2]', 'c', 'Loading...']);
// Time out the update
jest.advanceTimersByTime(750);
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput(<uppercase>LOADING...</uppercase>);
// Resolve the promise
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [b2]']);
expect(Scheduler).toFlushAndYield(['a', 'b2', 'c']);
assertLog(['Promise resolved [b2]']);
await waitForAll(['a', 'b2', 'c']);
// Render the final update. A should still be hidden, because it was
// given a `hidden` prop.
@ -312,7 +308,7 @@ describe('ReactSuspensePlaceholder', () => {
describe('when suspending during mount', () => {
it('properly accounts for base durations when a suspended times out in a legacy tree', async () => {
ReactNoop.renderLegacySyncRoot(<App shouldSuspend={true} />);
expect(Scheduler).toHaveYielded([
assertLog([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -330,11 +326,11 @@ describe('ReactSuspensePlaceholder', () => {
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']);
assertLog(['Promise resolved [Loaded]']);
ReactNoop.flushSync();
expect(Scheduler).toHaveYielded(['Loaded']);
assertLog(['Loaded']);
expect(ReactNoop).toMatchRenderedOutput('LoadedText');
expect(onRender).toHaveBeenCalledTimes(2);
@ -345,10 +341,10 @@ describe('ReactSuspensePlaceholder', () => {
expect(onRender.mock.calls[1][3]).toBe(8);
});
it('properly accounts for base durations when a suspended times out in a concurrent tree', () => {
it('properly accounts for base durations when a suspended times out in a concurrent tree', async () => {
ReactNoop.render(<App shouldSuspend={true} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -368,8 +364,8 @@ describe('ReactSuspensePlaceholder', () => {
// Resolve the pending promise.
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']);
expect(Scheduler).toFlushAndYield(['Suspending', 'Loaded', 'Text']);
assertLog(['Promise resolved [Loaded]']);
await waitForAll(['Suspending', 'Loaded', 'Text']);
expect(ReactNoop).toMatchRenderedOutput('LoadedText');
expect(onRender).toHaveBeenCalledTimes(2);
@ -385,7 +381,7 @@ describe('ReactSuspensePlaceholder', () => {
ReactNoop.renderLegacySyncRoot(
<App shouldSuspend={false} textRenderDuration={5} />,
);
expect(Scheduler).toHaveYielded(['App', 'Text']);
assertLog(['App', 'Text']);
expect(ReactNoop).toMatchRenderedOutput('Text');
expect(onRender).toHaveBeenCalledTimes(1);
@ -395,7 +391,7 @@ describe('ReactSuspensePlaceholder', () => {
expect(onRender.mock.calls[0][3]).toBe(5);
ReactNoop.render(<App shouldSuspend={true} textRenderDuration={5} />);
expect(Scheduler).toHaveYielded([
assertLog([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -415,7 +411,7 @@ describe('ReactSuspensePlaceholder', () => {
ReactNoop.renderLegacySyncRoot(
<App shouldSuspend={true} text="New" textRenderDuration={6} />,
);
expect(Scheduler).toHaveYielded([
assertLog([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -429,11 +425,11 @@ describe('ReactSuspensePlaceholder', () => {
expect(onRender.mock.calls[1][3]).toBe(10);
jest.advanceTimersByTime(1000);
expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']);
assertLog(['Promise resolved [Loaded]']);
ReactNoop.flushSync();
expect(Scheduler).toHaveYielded(['Loaded']);
assertLog(['Loaded']);
expect(ReactNoop).toMatchRenderedOutput('LoadedNew');
expect(onRender).toHaveBeenCalledTimes(4);
@ -444,7 +440,7 @@ describe('ReactSuspensePlaceholder', () => {
expect(onRender.mock.calls[3][3]).toBe(9);
});
it('properly accounts for base durations when a suspended times out in a concurrent tree', () => {
it('properly accounts for base durations when a suspended times out in a concurrent tree', async () => {
ReactNoop.render(
<>
<App shouldSuspend={false} textRenderDuration={5} />
@ -452,7 +448,7 @@ describe('ReactSuspensePlaceholder', () => {
</>,
);
expect(Scheduler).toFlushAndYield(['App', 'Text']);
await waitForAll(['App', 'Text']);
expect(ReactNoop).toMatchRenderedOutput('Text');
expect(onRender).toHaveBeenCalledTimes(1);
@ -467,7 +463,7 @@ describe('ReactSuspensePlaceholder', () => {
<Suspense fallback={null} />
</>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -507,7 +503,7 @@ describe('ReactSuspensePlaceholder', () => {
// from timers.
Scheduler.unstable_advanceTime(100);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'App',
'Suspending',
'Suspend! [Loaded]',
@ -520,17 +516,8 @@ describe('ReactSuspensePlaceholder', () => {
// Resolve the pending promise.
jest.advanceTimersByTime(100);
expect(Scheduler).toHaveYielded([
'Promise resolved [Loaded]',
'Promise resolved [Sibling]',
]);
expect(Scheduler).toFlushAndYield([
'App',
'Suspending',
'Loaded',
'New',
'Sibling',
]);
assertLog(['Promise resolved [Loaded]', 'Promise resolved [Sibling]']);
await waitForAll(['App', 'Suspending', 'Loaded', 'New', 'Sibling']);
expect(onRender).toHaveBeenCalledTimes(3);
// When the suspending data is resolved and our final UI is rendered,

View File

@ -12,6 +12,10 @@ let Suspense;
let startTransition;
let cache;
let pendingTextRequests;
let waitFor;
let waitForPaint;
let assertLog;
let waitForAll;
describe('ReactThenable', () => {
beforeEach(() => {
@ -29,6 +33,12 @@ describe('ReactThenable', () => {
startTransition = React.startTransition;
cache = React.cache;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
waitForPaint = InternalTestUtils.waitForPaint;
waitFor = InternalTestUtils.waitFor;
pendingTextRequests = new Map();
});
@ -94,7 +104,7 @@ describe('ReactThenable', () => {
});
});
expect(Scheduler).toHaveYielded([
assertLog([
// React will yield when the async component suspends.
'Suspend!',
'Resolve in microtask',
@ -128,11 +138,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded([
'Suspend!',
'Resolve in microtask',
'Async',
]);
assertLog(['Suspend!', 'Resolve in microtask', 'Async']);
expect(root).toMatchRenderedOutput('Async');
});
@ -172,7 +178,7 @@ describe('ReactThenable', () => {
await act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['Suspend!', 'Loading...']);
assertLog(['Suspend!', 'Loading...']);
expect(root).toMatchRenderedOutput('Loading...');
});
@ -201,7 +207,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['ABC']);
assertLog(['ABC']);
expect(root).toMatchRenderedOutput('ABC');
});
@ -229,7 +235,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['ABC']);
assertLog(['ABC']);
expect(root).toMatchRenderedOutput('ABC');
});
@ -275,7 +281,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['Oops!', 'Oops!']);
assertLog(['Oops!', 'Oops!']);
});
// @gate enableUseHook
@ -308,7 +314,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['ABCD']);
assertLog(['ABCD']);
expect(root).toMatchRenderedOutput('ABCD');
});
@ -344,7 +350,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['CD', 'Loading...']);
assertLog(['CD', 'Loading...']);
expect(root).toMatchRenderedOutput('Loading...');
});
@ -397,7 +403,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded([
assertLog([
// First attempt. The uncached promise suspends.
'Suspend! [Async]',
// Because the promise already fulfilled, we're able to unwrap the value
@ -427,7 +433,7 @@ describe('ReactThenable', () => {
});
// @gate enableUseHook
test('basic use(context)', () => {
test('basic use(context)', async () => {
const ContextA = React.createContext('');
const ContextB = React.createContext('B');
@ -446,7 +452,7 @@ describe('ReactThenable', () => {
const root = ReactNoop.createRoot();
root.render(<App />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
expect(root).toMatchRenderedOutput('AB');
});
@ -482,7 +488,7 @@ describe('ReactThenable', () => {
startTransition(() => {
root.render(<App text="world" />);
});
expect(Scheduler).toFlushUntilNextPaint([]);
await waitForPaint([]);
expect(root).toMatchRenderedOutput(null);
await resolve({default: <Text key="hi" text="Hello " />});
@ -492,7 +498,7 @@ describe('ReactThenable', () => {
root.render(<App text="world!" />);
});
expect(Scheduler).toHaveYielded(['Hello ', 'world!']);
assertLog(['Hello ', 'world!']);
expect(root).toMatchRenderedOutput(<div>Hello world!</div>);
});
@ -547,7 +553,7 @@ describe('ReactThenable', () => {
</Suspense>,
);
});
expect(Scheduler).toHaveYielded(['(empty)']);
assertLog(['(empty)']);
expect(root).toMatchRenderedOutput('(empty)');
await act(async () => {
@ -559,13 +565,13 @@ describe('ReactThenable', () => {
);
});
});
expect(Scheduler).toHaveYielded(['Async text requested [Async]']);
assertLog(['Async text requested [Async]']);
expect(root).toMatchRenderedOutput('(empty)');
await act(async () => {
resolveTextRequests('Async');
});
expect(Scheduler).toHaveYielded(['Async text requested [Async]', 'Async']);
assertLog(['Async text requested [Async]', 'Async']);
expect(root).toMatchRenderedOutput('Async');
});
@ -586,10 +592,7 @@ describe('ReactThenable', () => {
});
});
// Even though the initial render was a transition, it shows a fallback.
expect(Scheduler).toHaveYielded([
'Async text requested [Async]',
'Loading...',
]);
assertLog(['Async text requested [Async]', 'Loading...']);
expect(root).toMatchRenderedOutput('Loading...');
// Resolve the original data
@ -603,7 +606,7 @@ describe('ReactThenable', () => {
// this test, how would the developer be able to imperatively flush it if it
// wasn't initiated until the current `act` call? Can't think of a better
// strategy at the moment.
expect(Scheduler).toHaveYielded(['Async text requested [Async]']);
assertLog(['Async text requested [Async]']);
expect(root).toMatchRenderedOutput('Loading...');
// Flush the second request.
@ -611,7 +614,7 @@ describe('ReactThenable', () => {
resolveTextRequests('Async');
});
// This time it finishes because it was during a retry.
expect(Scheduler).toHaveYielded(['Async text requested [Async]', 'Async']);
assertLog(['Async text requested [Async]', 'Async']);
expect(root).toMatchRenderedOutput('Async');
});
@ -635,9 +638,7 @@ describe('ReactThenable', () => {
);
});
});
expect(Scheduler).toHaveYielded([
'Async text requested [Will never resolve]',
]);
assertLog(['Async text requested [Will never resolve]']);
await act(async () => {
root.render(
@ -646,7 +647,7 @@ describe('ReactThenable', () => {
</Suspense>,
);
});
expect(Scheduler).toHaveYielded(['Something different']);
assertLog(['Something different']);
});
// @gate enableUseHook
@ -669,9 +670,7 @@ describe('ReactThenable', () => {
);
});
});
expect(Scheduler).toHaveYielded([
'Async text requested [Will never resolve]',
]);
assertLog(['Async text requested [Will never resolve]']);
// Calling a hook should error because we're oustide of a component.
expect(useState).toThrow(
@ -704,7 +703,7 @@ describe('ReactThenable', () => {
ReactNoop.flushSync(() => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['Hi']);
assertLog(['Hi']);
expect(root).toMatchRenderedOutput('Hi');
});
@ -745,26 +744,21 @@ describe('ReactThenable', () => {
await act(() => {
root.render(<Parent />);
});
expect(Scheduler).toHaveYielded([
'childShouldSuspend: false, showChild: true',
'Child',
]);
assertLog(['childShouldSuspend: false, showChild: true', 'Child']);
expect(root).toMatchRenderedOutput('Child');
await act(() => {
await act(async () => {
// Perform an update that causes the app to suspend
startTransition(() => {
setChildShouldSuspend(true);
});
expect(Scheduler).toFlushAndYieldThrough([
'childShouldSuspend: true, showChild: true',
]);
await waitFor(['childShouldSuspend: true, showChild: true']);
// While the update is in progress, schedule another update.
startTransition(() => {
setShowChild(false);
});
});
expect(Scheduler).toHaveYielded([
assertLog([
// Because the interleaved update is not higher priority than what we were
// already working on, it won't interrupt. The first update will continue,
// and will suspend.
@ -827,17 +821,14 @@ describe('ReactThenable', () => {
});
});
// Suspends while we wait for the async service to respond.
expect(Scheduler).toHaveYielded([
'Compute uppercase: Hello',
'Async text requested [HELLO!]',
]);
assertLog(['Compute uppercase: Hello', 'Async text requested [HELLO!]']);
expect(root).toMatchRenderedOutput(null);
// The data is received.
await act(async () => {
resolveTextRequests('HELLO!');
});
expect(Scheduler).toHaveYielded([
assertLog([
// We shouldn't run the uppercase computation again, because we can reuse
// the computation from the previous attempt.
// 'Compute uppercase: Hello',
@ -867,15 +858,12 @@ describe('ReactThenable', () => {
root.render(<Kitchen />);
});
});
expect(Scheduler).toHaveYielded(['Async text requested [apple]']);
assertLog(['Async text requested [apple]']);
expect(root).toMatchRenderedOutput(null);
await act(async () => {
resolveTextRequests('apple');
});
expect(Scheduler).toHaveYielded([
'Async text requested [apple]',
'apple carrot',
]);
assertLog(['Async text requested [apple]', 'apple carrot']);
expect(root).toMatchRenderedOutput('apple carrot');
// Update the state variable after the use().
@ -884,15 +872,12 @@ describe('ReactThenable', () => {
_setVegetable('dill');
});
});
expect(Scheduler).toHaveYielded(['Async text requested [apple]']);
assertLog(['Async text requested [apple]']);
expect(root).toMatchRenderedOutput('apple carrot');
await act(async () => {
resolveTextRequests('apple');
});
expect(Scheduler).toHaveYielded([
'Async text requested [apple]',
'apple dill',
]);
assertLog(['Async text requested [apple]', 'apple dill']);
expect(root).toMatchRenderedOutput('apple dill');
// Update the state variable before the use(). The second state is maintained.
@ -901,15 +886,12 @@ describe('ReactThenable', () => {
_setFruit('banana');
});
});
expect(Scheduler).toHaveYielded(['Async text requested [banana]']);
assertLog(['Async text requested [banana]']);
expect(root).toMatchRenderedOutput('apple dill');
await act(async () => {
resolveTextRequests('banana');
});
expect(Scheduler).toHaveYielded([
'Async text requested [banana]',
'banana dill',
]);
assertLog(['Async text requested [banana]', 'banana dill']);
expect(root).toMatchRenderedOutput('banana dill');
});
@ -931,15 +913,12 @@ describe('ReactThenable', () => {
root.render(<Lexicon />);
});
});
expect(Scheduler).toHaveYielded(['Async text requested [aguacate]']);
assertLog(['Async text requested [aguacate]']);
expect(root).toMatchRenderedOutput(null);
await act(async () => {
resolveTextRequests('aguacate');
});
expect(Scheduler).toHaveYielded([
'Async text requested [aguacate]',
'aguacate abogado',
]);
assertLog(['Async text requested [aguacate]', 'aguacate abogado']);
expect(root).toMatchRenderedOutput('aguacate abogado');
// Now update the state.
@ -948,15 +927,12 @@ describe('ReactThenable', () => {
_setLawyer('avocat');
});
});
expect(Scheduler).toHaveYielded(['Async text requested [aguacate]']);
assertLog(['Async text requested [aguacate]']);
expect(root).toMatchRenderedOutput('aguacate abogado');
await act(async () => {
resolveTextRequests('aguacate');
});
expect(Scheduler).toHaveYielded([
'Async text requested [aguacate]',
'aguacate avocat',
]);
assertLog(['Async text requested [aguacate]', 'aguacate avocat']);
expect(root).toMatchRenderedOutput('aguacate avocat');
});
@ -977,13 +953,13 @@ describe('ReactThenable', () => {
root.render(<App text="Hello" />);
});
});
expect(Scheduler).toHaveYielded(['Async text requested [Hello]']);
assertLog(['Async text requested [Hello]']);
expect(root).toMatchRenderedOutput(null);
await act(async () => {
resolveTextRequests('Hello');
});
expect(Scheduler).toHaveYielded([
assertLog([
// We shouldn't request async text again, because the async function
// was memoized
// 'Async text requested [Hello]'
@ -1015,7 +991,7 @@ describe('ReactThenable', () => {
</Suspense>,
);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Async text requested [A]',
'Async text requested [B]',
'Async text requested [C]',
@ -1028,19 +1004,19 @@ describe('ReactThenable', () => {
await act(async () => {
resolveTextRequests('A');
});
expect(Scheduler).toHaveYielded(['A', '(Loading C...)', '(Loading B...)']);
assertLog(['A', '(Loading C...)', '(Loading B...)']);
expect(root).toMatchRenderedOutput('A(Loading B...)');
await act(async () => {
resolveTextRequests('B');
});
expect(Scheduler).toHaveYielded(['B', '(Loading C...)']);
assertLog(['B', '(Loading C...)']);
expect(root).toMatchRenderedOutput('AB(Loading C...)');
await act(async () => {
resolveTextRequests('C');
});
expect(Scheduler).toHaveYielded(['C']);
assertLog(['C']);
expect(root).toMatchRenderedOutput('ABC');
});
@ -1068,7 +1044,7 @@ describe('ReactThenable', () => {
</Suspense>,
);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Async text requested [A]',
'Async text requested [B]',
'Async text requested [C]',
@ -1081,13 +1057,13 @@ describe('ReactThenable', () => {
await act(async () => {
resolveTextRequests('A');
});
expect(Scheduler).toHaveYielded(['Async text requested [A]']);
assertLog(['Async text requested [A]']);
expect(root).toMatchRenderedOutput('(Loading A...)');
await act(async () => {
resolveTextRequests('A');
});
expect(Scheduler).toHaveYielded([
assertLog([
// React suspends until A finishes loading.
'Async text requested [A]',
'A',
@ -1106,13 +1082,13 @@ describe('ReactThenable', () => {
await act(async () => {
resolveTextRequests('B');
});
expect(Scheduler).toHaveYielded(['Async text requested [B]']);
assertLog(['Async text requested [B]']);
expect(root).toMatchRenderedOutput('A(Loading B...)');
await act(async () => {
resolveTextRequests('B');
});
expect(Scheduler).toHaveYielded([
assertLog([
// React suspends until B finishes loading.
'Async text requested [B]',
'B',
@ -1126,13 +1102,13 @@ describe('ReactThenable', () => {
await act(async () => {
resolveTextRequests('C');
});
expect(Scheduler).toHaveYielded(['Async text requested [C]']);
assertLog(['Async text requested [C]']);
expect(root).toMatchRenderedOutput('AB(Loading C...)');
await act(async () => {
resolveTextRequests('C');
});
expect(Scheduler).toHaveYielded(['Async text requested [C]', 'C']);
assertLog(['Async text requested [C]', 'C']);
expect(root).toMatchRenderedOutput('ABC');
});
@ -1162,7 +1138,7 @@ describe('ReactThenable', () => {
root.render(<App />);
});
});
expect(Scheduler).toHaveYielded(['A1']);
assertLog(['A1']);
expect(root).toMatchRenderedOutput('A1');
});
});

View File

@ -12,7 +12,7 @@
let React;
let ReactNoop;
let Scheduler;
let waitForAll;
// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
@ -21,18 +21,20 @@ describe('ReactTopLevelFragment', function () {
jest.resetModules();
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
});
it('should render a simple fragment at the top of a component', function () {
it('should render a simple fragment at the top of a component', async function () {
function Fragment() {
return [<div key="a">Hello</div>, <div key="b">World</div>];
}
ReactNoop.render(<Fragment />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
});
it('should preserve state when switching from a single child', function () {
it('should preserve state when switching from a single child', async function () {
let instance = null;
class Stateful extends React.Component {
@ -50,21 +52,21 @@ describe('ReactTopLevelFragment', function () {
);
}
ReactNoop.render(<Fragment />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceB = instance;
expect(instanceB).toBe(instanceA);
});
it('should not preserve state when switching to a nested array', function () {
it('should not preserve state when switching to a nested array', async function () {
let instance = null;
class Stateful extends React.Component {
@ -82,21 +84,21 @@ describe('ReactTopLevelFragment', function () {
);
}
ReactNoop.render(<Fragment />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceB = instance;
expect(instanceB).not.toBe(instanceA);
});
it('preserves state if an implicit key slot switches from/to null', function () {
it('preserves state if an implicit key slot switches from/to null', async function () {
let instance = null;
class Stateful extends React.Component {
@ -112,28 +114,28 @@ describe('ReactTopLevelFragment', function () {
: [<div key="b">Hello</div>, <Stateful key="a" />];
}
ReactNoop.render(<Fragment />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceB = instance;
expect(instanceB).toBe(instanceA);
ReactNoop.render(<Fragment condition={false} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceC = instance;
expect(instanceC === instanceA).toBe(true);
});
it('should preserve state in a reorder', function () {
it('should preserve state in a reorder', async function () {
let instance = null;
class Stateful extends React.Component {
@ -149,14 +151,14 @@ describe('ReactTopLevelFragment', function () {
: [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
}
ReactNoop.render(<Fragment />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
const instanceB = instance;

View File

@ -12,7 +12,7 @@
let React;
let ReactNoop;
let Scheduler;
let waitForAll;
// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
@ -21,20 +21,22 @@ describe('ReactTopLevelText', () => {
jest.resetModules();
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
});
it('should render a component returning strings directly from render', () => {
it('should render a component returning strings directly from render', async () => {
const Text = ({value}) => value;
ReactNoop.render(<Text value="foo" />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput('foo');
});
it('should render a component returning numbers directly from render', () => {
it('should render a component returning numbers directly from render', async () => {
const Text = ({value}) => value;
ReactNoop.render(<Text value={10} />);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput('10');
});
});

View File

@ -20,6 +20,10 @@ let useTransition;
let startTransition;
let act;
let getCacheForType;
let waitForAll;
let waitFor;
let waitForPaint;
let assertLog;
let caches;
let seededCache;
@ -38,6 +42,12 @@ describe('ReactTransition', () => {
getCacheForType = React.unstable_getCacheForType;
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
waitFor = InternalTestUtils.waitFor;
waitForPaint = InternalTestUtils.waitForPaint;
assertLog = InternalTestUtils.assertLog;
caches = [];
seededCache = null;
});
@ -178,13 +188,13 @@ describe('ReactTransition', () => {
await act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['(empty)']);
assertLog(['(empty)']);
expect(root).toMatchRenderedOutput('(empty)');
await act(async () => {
start();
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Pending...',
'(empty)',
'Suspend! [Async]',
@ -195,7 +205,7 @@ describe('ReactTransition', () => {
await resolveText('Async');
});
expect(Scheduler).toHaveYielded(['Async']);
assertLog(['Async']);
expect(root).toMatchRenderedOutput('Async');
});
@ -239,7 +249,7 @@ describe('ReactTransition', () => {
seedNextTextCache('A content');
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['A label', 'A content']);
assertLog(['A label', 'A content']);
expect(root).toMatchRenderedOutput(
<>
A label<div>A content</div>
@ -250,7 +260,7 @@ describe('ReactTransition', () => {
await act(async () => {
update('B');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Commit pending state
'B label (loading...)',
'A content',
@ -271,7 +281,7 @@ describe('ReactTransition', () => {
await act(async () => {
update('C');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Commit pending state
'C label (loading...)',
'A content',
@ -292,7 +302,7 @@ describe('ReactTransition', () => {
await act(async () => {
resolveText('B content');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Attempt to render C, but it suspends
'C label',
'Suspend! [C content]',
@ -308,7 +318,7 @@ describe('ReactTransition', () => {
await act(async () => {
resolveText('C content');
});
expect(Scheduler).toHaveYielded(['C label', 'C content']);
assertLog(['C label', 'C content']);
expect(root).toMatchRenderedOutput(
<>
C label<div>C content</div>
@ -364,7 +374,7 @@ describe('ReactTransition', () => {
seedNextTextCache('A content');
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['A label', 'A content']);
assertLog(['A label', 'A content']);
expect(root).toMatchRenderedOutput(
<>
A label<div>A content</div>
@ -375,7 +385,7 @@ describe('ReactTransition', () => {
await act(async () => {
update('B');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Commit pending state
'B label (loading...)',
'A content',
@ -396,7 +406,7 @@ describe('ReactTransition', () => {
await act(async () => {
update('C');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Commit pending state
'C label (loading...)',
'A content',
@ -417,7 +427,7 @@ describe('ReactTransition', () => {
await act(async () => {
resolveText('B content');
});
expect(Scheduler).toHaveYielded([
assertLog([
// Attempt to render C, but it suspends
'C label',
'Suspend! [C content]',
@ -433,7 +443,7 @@ describe('ReactTransition', () => {
await act(async () => {
resolveText('C content');
});
expect(Scheduler).toHaveYielded(['C label', 'C content']);
assertLog(['C label', 'C content']);
expect(root).toMatchRenderedOutput(
<>
C label<div>C content</div>
@ -481,7 +491,7 @@ describe('ReactTransition', () => {
await act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded([]);
assertLog([]);
expect(root).toMatchRenderedOutput(null);
// Switch to A.
@ -490,7 +500,7 @@ describe('ReactTransition', () => {
setShowA(true);
});
});
expect(Scheduler).toHaveYielded(['Suspend! [A]', 'Loading...']);
assertLog(['Suspend! [A]', 'Loading...']);
expect(root).toMatchRenderedOutput(null);
// Before A loads, switch to B. This should entangle A with B.
@ -500,7 +510,7 @@ describe('ReactTransition', () => {
setShowB(true);
});
});
expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...']);
assertLog(['Suspend! [B]', 'Loading...']);
expect(root).toMatchRenderedOutput(null);
// Before A or B loads, switch to C. This should entangle C with B, and
@ -511,7 +521,7 @@ describe('ReactTransition', () => {
setShowC(true);
});
});
expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
assertLog(['Suspend! [C]', 'Loading...']);
expect(root).toMatchRenderedOutput(null);
// Now the data starts resolving out of order.
@ -523,7 +533,7 @@ describe('ReactTransition', () => {
resolveText('B');
});
});
expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
assertLog(['Suspend! [C]', 'Loading...']);
expect(root).toMatchRenderedOutput(null);
// Now resolve A. Again, this will attempt to render C, since everything
@ -533,7 +543,7 @@ describe('ReactTransition', () => {
resolveText('A');
});
});
expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
assertLog(['Suspend! [C]', 'Loading...']);
expect(root).toMatchRenderedOutput(null);
// Finally, resolve C. This time we can finish.
@ -542,7 +552,7 @@ describe('ReactTransition', () => {
resolveText('C');
});
});
expect(Scheduler).toHaveYielded(['C']);
assertLog(['C']);
expect(root).toMatchRenderedOutput('C');
},
);
@ -559,7 +569,7 @@ describe('ReactTransition', () => {
</>,
);
});
expect(Scheduler).toHaveYielded(['Initial']);
assertLog(['Initial']);
expect(root).toMatchRenderedOutput('Initial');
await act(async () => {
@ -577,7 +587,7 @@ describe('ReactTransition', () => {
});
// Partially render it.
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
// Once we the update suspends, we know it's a refresh transition,
// because the Suspense boundary has already mounted.
'Suspend! [Async]',
@ -598,7 +608,7 @@ describe('ReactTransition', () => {
// Because the first one is going to suspend regardless, we should
// immediately switch to rendering the new transition.
expect(Scheduler).toHaveYielded(['Updated']);
assertLog(['Updated']);
expect(root).toMatchRenderedOutput('Updated');
});
@ -643,12 +653,7 @@ describe('ReactTransition', () => {
await act(async () => {
root.render(<App />);
expect(Scheduler).toFlushAndYield([
'A',
'shouldHideInParent: false',
'B',
'C',
]);
await waitForAll(['A', 'shouldHideInParent: false', 'B', 'C']);
expect(root).toMatchRenderedOutput('ABC');
// Schedule an update
@ -660,14 +665,14 @@ describe('ReactTransition', () => {
// lane from the first one. At the time this was written, all transitions are worked on
// simultaneously, unless a transition was already in progress when a
// new one was scheduled. So, partially render the first transition.
expect(Scheduler).toFlushAndYieldThrough(['A']);
await waitFor(['A']);
// Now schedule a second transition. We won't interrupt the first one.
React.startTransition(() => {
setShouldHideInParent(true);
});
// Continue rendering the first transition.
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
'shouldHideInParent: false',
'Suspend! [Async]',
'Loading...',
@ -683,16 +688,13 @@ describe('ReactTransition', () => {
// time we re-enter the work loop (we don't interrupt immediately, we
// just wait for the next time slice), we should throw out the
// suspended first transition and try the second one.
expect(Scheduler).toFlushUntilNextPaint([
'shouldHideInParent: true',
'(empty)',
]);
await waitForPaint(['shouldHideInParent: true', '(empty)']);
expect(root).toMatchRenderedOutput('A(empty)BC');
// Since the two transitions are not entangled, we then later go back
// and finish retry the first transition. Not really relevant to this
// test but I'll assert the result anyway.
expect(Scheduler).toFlushAndYield([
await waitForAll([
'A',
'shouldHideInParent: true',
'(empty)',
@ -732,7 +734,7 @@ describe('ReactTransition', () => {
await act(async () => {
root.render(<App shouldSuspend={false} step={0} />);
});
expect(Scheduler).toHaveYielded(['A0', 'B0', 'C0']);
assertLog(['A0', 'B0', 'C0']);
expect(root).toMatchRenderedOutput('A0B0C0');
await act(async () => {
@ -741,14 +743,14 @@ describe('ReactTransition', () => {
root.render(<App shouldSuspend={true} step={1} />);
});
// Flush past the root, but stop before the async component.
expect(Scheduler).toFlushAndYieldThrough(['A1']);
await waitFor(['A1']);
// Schedule another transition on the root, which already completed.
startTransition(() => {
root.render(<App shouldSuspend={false} step={2} />);
});
// We'll keep working on the first update.
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
// Now the async component suspends
'Suspend! [Async]',
'Loading...',
@ -762,7 +764,7 @@ describe('ReactTransition', () => {
// TODO: This should work even if React does not yield to the main
// thread. Should use same mechanism as selective hydration to interrupt
// the render before the end of the current slice of work.
expect(Scheduler).toFlushAndYield(['A2', 'B2', 'C2']);
await waitForAll(['A2', 'B2', 'C2']);
expect(root).toMatchRenderedOutput('A2B2C2');
});
@ -798,11 +800,7 @@ describe('ReactTransition', () => {
});
// Initial render.
expect(Scheduler).toHaveYielded([
'Transition pri: 0',
'Normal pri: 0',
'Commit',
]);
assertLog(['Transition pri: 0', 'Normal pri: 0', 'Commit']);
expect(root).toMatchRenderedOutput('Transition pri: 0, Normal pri: 0');
await act(async () => {
@ -810,7 +808,7 @@ describe('ReactTransition', () => {
updateNormalPri();
});
expect(Scheduler).toHaveYielded([
assertLog([
// Normal update first.
'Transition pri: 0',
'Normal pri: 1',
@ -854,14 +852,14 @@ describe('ReactTransition', () => {
});
// Initial render.
expect(Scheduler).toHaveYielded(['(empty)', 'Normal pri: 0', 'Commit']);
assertLog(['(empty)', 'Normal pri: 0', 'Commit']);
expect(root).toMatchRenderedOutput('(empty), Normal pri: 0');
await act(async () => {
updateTransitionPri();
});
expect(Scheduler).toHaveYielded([
assertLog([
// Suspend.
'Suspend! [Async]',
'Normal pri: 0',
@ -874,7 +872,7 @@ describe('ReactTransition', () => {
updateNormalPri();
});
expect(Scheduler).toHaveYielded([
assertLog([
// Normal pri update.
'(empty)',
'Normal pri: 1',
@ -914,17 +912,13 @@ describe('ReactTransition', () => {
await act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded([
'Transition pri: 0',
'Normal pri: 0',
'Commit',
]);
assertLog(['Transition pri: 0', 'Normal pri: 0', 'Commit']);
expect(root).toMatchRenderedOutput('Transition pri: 0, Normal pri: 0');
await act(async () => {
updateTransitionPri();
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
// Start transition update.
'Transition pri: 1',
]);
@ -935,7 +929,7 @@ describe('ReactTransition', () => {
});
if (gate(flags => flags.enableUnifiedSyncLane)) {
expect(Scheduler).toHaveYielded([
assertLog([
'Normal pri: 0',
'Commit',
@ -945,7 +939,7 @@ describe('ReactTransition', () => {
'Commit',
]);
} else {
expect(Scheduler).toHaveYielded([
assertLog([
// Finish transition update.
'Normal pri: 0',
'Commit',

View File

@ -12,6 +12,8 @@ let React;
let ReactNoop;
let Scheduler;
let act;
let waitForAll;
let assertLog;
let getCacheForType;
let useState;
@ -43,6 +45,10 @@ describe('ReactInteractionTracing', () => {
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
useState = React.useState;
startTransition = React.startTransition;
Suspense = React.Suspense;
@ -233,7 +239,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
await act(async () => {
startTransition(() => root.render(<App navigate={true} />));
@ -242,12 +248,12 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
// Doesn't call transition or marker code
expect(Scheduler).toFlushAndYield(['Page Two']);
await waitForAll(['Page Two']);
startTransition(() => root.render(<App navigate={false} />), {
name: 'transition',
});
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page One',
'onTransitionStart(transition, 2000)',
'onTransitionComplete(transition, 2000, 2000)',
@ -299,7 +305,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
await act(async () => {
startTransition(() => navigateToPageTwo(), {name: 'page transition'});
@ -307,7 +313,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onTransitionStart(page transition, 1000)',
'onTransitionComplete(page transition, 1000, 2000)',
@ -358,7 +364,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One: hide']);
await waitForAll(['Page One: hide']);
await act(async () => {
startTransition(
@ -372,7 +378,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two: show',
'onTransitionStart(page transition, 1000)',
'onTransitionComplete(page transition, 1000, 2000)',
@ -431,7 +437,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -440,7 +446,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'onTransitionStart(page transition, 1000)',
@ -451,7 +457,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
await resolveText('Page Two');
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onTransitionProgress(page transition, 1000, 3000, [])',
'onTransitionComplete(page transition, 1000, 3000)',
@ -526,13 +532,13 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
startTransition(() => navigateToPageTwo(), {name: 'page transition'});
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'onTransitionStart(page transition, 1000)',
@ -542,14 +548,14 @@ describe('ReactInteractionTracing', () => {
await resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onTransitionProgress(page transition, 1000, 2000, [])',
'onTransitionComplete(page transition, 1000, 2000)',
]);
startTransition(() => showTextFn(), {name: 'text transition'});
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Show Text]',
'Show Text Loading...',
'Page Two',
@ -560,7 +566,7 @@ describe('ReactInteractionTracing', () => {
await resolveText('Show Text');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text',
'onTransitionProgress(text transition, 2000, 3000, [])',
'onTransitionComplete(text transition, 2000, 3000)',
@ -633,7 +639,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -641,7 +647,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'onTransitionStart(page transition, 1000)',
@ -652,7 +658,7 @@ describe('ReactInteractionTracing', () => {
await act(async () => {
startTransition(() => showTextFn(), {name: 'show text'});
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Show Text]',
'Show Text Loading...',
'Suspend [Page Two]',
@ -667,7 +673,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onTransitionProgress(page transition, 1000, 3000, [])',
'onTransitionComplete(page transition, 1000, 3000)',
@ -677,7 +683,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text',
'onTransitionProgress(show text, 2000, 4000, [])',
'onTransitionComplete(show text, 2000, 4000)',
@ -750,7 +756,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -758,7 +764,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Suspend [Show Text One]',
'Show Text One Loading...',
@ -773,7 +779,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'Suspend [Show Text One]',
'Show Text One Loading...',
@ -786,7 +792,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text One',
'onTransitionProgress(page transition, 1000, 4000, [show text two])',
]);
@ -795,7 +801,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text Two',
'onTransitionProgress(page transition, 1000, 5000, [])',
'onTransitionComplete(page transition, 1000, 5000)',
@ -881,7 +887,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -890,7 +896,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Suspend [Show Text One]',
'Show Text One Loading...',
@ -906,7 +912,7 @@ describe('ReactInteractionTracing', () => {
resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'Suspend [Show Text One]',
'Show Text One Loading...',
@ -920,7 +926,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'Suspend [Show Text One]',
'Show Text One Loading...',
@ -938,7 +944,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text',
'onTransitionProgress(navigate, 1000, 5000, [show text one])',
'onTransitionProgress(show text one, 1000, 5000, [show text one])',
@ -948,7 +954,7 @@ describe('ReactInteractionTracing', () => {
resolveText('Show Text Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text Two',
'onTransitionProgress(show text two, 3000, 6000, [])',
'onTransitionComplete(show text two, 3000, 6000)',
@ -959,7 +965,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Show Text One',
'onTransitionProgress(navigate, 1000, 7000, [])',
'onTransitionProgress(show text one, 1000, 7000, [])',
@ -1037,7 +1043,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
await act(async () => {
startTransition(() => navigateToPageTwo(), {name: 'page transition'});
@ -1045,7 +1051,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onTransitionStart(page transition, 1000)',
'onMarkerComplete(page transition, marker two, 1000, 2000)',
@ -1124,7 +1130,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -1133,7 +1139,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Suspend [Marker Text]',
'Loading...',
@ -1145,7 +1151,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
await resolveText('Page Two');
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'Suspend [Marker Text]',
'Loading...',
@ -1157,7 +1163,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
await resolveText('Marker Text');
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Marker Text',
'onMarkerProgress(page transition, async marker, 1000, 4000, [])',
'onMarkerComplete(page transition, async marker, 1000, 4000)',
@ -1244,7 +1250,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
});
await act(async () => {
@ -1253,7 +1259,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Outer Text]',
'Suspend [Inner Text One]',
'Inner One...',
@ -1267,12 +1273,12 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
await resolveText('Inner Text Two');
expect(Scheduler).toFlushAndYield([]);
await waitForAll([]);
ReactNoop.expire(1000);
await advanceTimers(1000);
await resolveText('Outer Text');
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Outer Text',
'Suspend [Inner Text One]',
'Inner One...',
@ -1284,7 +1290,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
await resolveText('Inner Text One');
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Inner Text One',
'onMarkerProgress(page transition, outer marker, 1000, 5000, [])',
'onMarkerComplete(page transition, marker one, 1000, 5000)',
@ -1367,7 +1373,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={false} markerName="marker one" />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
startTransition(
() => root.render(<App navigate={true} markerName="marker one" />),
@ -1378,7 +1384,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'onTransitionStart(transition one, 1000)',
@ -1400,7 +1406,7 @@ describe('ReactInteractionTracing', () => {
resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onMarkerProgress(transition one, marker one, 1000, 4000, [])',
'onTransitionProgress(transition one, 1000, 4000, [])',
@ -1501,7 +1507,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={false} showMarker={true} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
startTransition(
() => root.render(<App navigate={true} showMarker={true} />),
@ -1511,7 +1517,7 @@ describe('ReactInteractionTracing', () => {
);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'Suspend [Sibling Text]',
@ -1526,7 +1532,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'Suspend [Sibling Text]',
@ -1539,7 +1545,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={true} showMarker={true} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading...',
'Suspend [Sibling Text]',
@ -1550,12 +1556,12 @@ describe('ReactInteractionTracing', () => {
resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page Two']);
await waitForAll(['Page Two']);
resolveText('Sibling Text');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Sibling Text',
'onMarkerProgress(transition one, parent, 1000, 6000, [])',
'onMarkerProgress(transition one, sibling, 1000, 6000, [])',
@ -1652,7 +1658,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={false} deleteOne={false} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
startTransition(
() => root.render(<App navigate={true} deleteOne={false} />),
@ -1662,7 +1668,7 @@ describe('ReactInteractionTracing', () => {
);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page One]',
'Loading One...',
'Suspend [Page Two]',
@ -1677,7 +1683,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={true} deleteOne={true} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading Two...',
'onMarkerProgress(transition, parent, 1000, 3000, [suspense two])',
@ -1688,7 +1694,7 @@ describe('ReactInteractionTracing', () => {
await resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
// Marker progress will still get called after incomplete but not marker complete
'onMarkerProgress(transition, parent, 1000, 4000, [])',
@ -1793,7 +1799,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Page One']);
await waitForAll(['Page One']);
startTransition(
() => root.render(<App navigate={true} deleteOne={false} />),
@ -1803,7 +1809,7 @@ describe('ReactInteractionTracing', () => {
);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page One]',
'Suspend [Child]',
'Loading Child...',
@ -1820,7 +1826,7 @@ describe('ReactInteractionTracing', () => {
await resolveText('Page One');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page One',
'Suspend [Child]',
'Loading Child...',
@ -1833,7 +1839,7 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={true} deleteOne={true} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Page Two]',
'Loading Two...',
// "suspense one" has unsuspended so shouldn't be included
@ -1848,7 +1854,7 @@ describe('ReactInteractionTracing', () => {
await resolveText('Page Two');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Page Two',
'onMarkerProgress(transition, parent, 1000, 5000, [])',
'onMarkerProgress(transition, two, 1000, 5000, [])',
@ -1933,7 +1939,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Suspend [Child]',
'onTransitionStart(transition, 0)',
'onMarkerProgress(transition, parent, 0, 1000, [child])',
@ -1945,23 +1951,20 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
// This appended child isn't part of the transition so we
// don't call any callback
expect(Scheduler).toFlushAndYield([
'Suspend [Appended child]',
'Suspend [Child]',
]);
await waitForAll(['Suspend [Appended child]', 'Suspend [Child]']);
// This deleted child isn't part of the transition so we
// don't call any callbacks
root.render(<App show={false} />);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield(['Suspend [Child]']);
await waitForAll(['Suspend [Child]']);
await resolveText('Child');
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Child',
'onMarkerProgress(transition, parent, 0, 4000, [])',
'onMarkerComplete(transition, parent, 0, 4000)',
@ -2056,7 +2059,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Child]',
'onTransitionStart(transition one, 0)',
'onMarkerProgress(transition one, parent, 0, 1000, [child])',
@ -2075,7 +2078,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Appended child]',
'Suspend [Child]',
'onTransitionStart(transition two, 1000)',
@ -2089,7 +2092,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Child]',
'onMarkerProgress(transition two, appended child, 1000, 3000, [])',
'onMarkerIncomplete(transition two, appended child, 1000, [{endTime: 3000, name: appended child, type: suspense}])',
@ -2101,7 +2104,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Child',
'onMarkerProgress(transition one, parent, 0, 4000, [])',
'onMarkerComplete(transition one, parent, 0, 4000)',
@ -2161,7 +2164,7 @@ describe('ReactInteractionTracing', () => {
);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'one',
'onTransitionStart(transition one, 0)',
'onMarkerComplete(transition one, one, 0, 1000)',
@ -2175,10 +2178,10 @@ describe('ReactInteractionTracing', () => {
);
ReactNoop.expire(1000);
await advanceTimers(1000);
expect(() => {
await expect(async () => {
// onMarkerComplete shouldn't be called for transitions with
// new keys
expect(Scheduler).toFlushAndYield([
await waitForAll([
'two',
'onTransitionStart(transition two, 1000)',
'onTransitionComplete(transition two, 1000, 2000)',
@ -2195,7 +2198,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
await advanceTimers(1000);
// This should not warn and onMarkerComplete should be called
expect(Scheduler).toFlushAndYield([
await waitForAll([
'three',
'onTransitionStart(transition three, 2000)',
'onMarkerComplete(transition three, three, 2000, 3000)',
@ -2247,7 +2250,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Text]',
'Loading...',
'Suspend [Hidden Text]',
@ -2260,7 +2263,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Text',
'onMarkerComplete(transition, marker, 0, 2000)',
'onTransitionComplete(transition, 0, 2000)',
@ -2271,7 +2274,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded(['Hidden Text']);
assertLog(['Hidden Text']);
});
// @gate enableTransitionTracing
@ -2317,7 +2320,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Page Two]',
'Loading...',
'onTransitionStart(page transition, 0)',
@ -2329,7 +2332,7 @@ describe('ReactInteractionTracing', () => {
await advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Page Two',
'onTransitionProgress(page transition, 0, 2000, [])',
'onTransitionComplete(page transition, 0, 2000)',
@ -2387,7 +2390,7 @@ describe('ReactInteractionTracing', () => {
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Text]',
'Loading...',
'Suspend [Text Two]',
@ -2404,7 +2407,7 @@ describe('ReactInteractionTracing', () => {
ReactNoop.expire(1000);
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Text Two',
'onTransitionProgress(transition, 0, 2000, [])',
'onTransitionComplete(transition, 0, 2000)',
@ -2465,7 +2468,7 @@ describe('ReactInteractionTracing', () => {
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Suspend [Text one]',
'Loading one...',
'Suspend [Text two]',
@ -2482,7 +2485,7 @@ describe('ReactInteractionTracing', () => {
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Text one',
'onTransitionProgress(transition one, 0, 2000, []) /root one/',
'onTransitionComplete(transition one, 0, 2000) /root one/',
@ -2494,7 +2497,7 @@ describe('ReactInteractionTracing', () => {
advanceTimers(1000);
});
expect(Scheduler).toHaveYielded([
assertLog([
'Text two',
'onTransitionProgress(transition two, 0, 3000, []) /root two/',
'onTransitionComplete(transition two, 0, 3000) /root two/',

View File

@ -6,6 +6,9 @@ let startTransition;
let useState;
let useEffect;
let act;
let waitFor;
let waitForPaint;
let assertLog;
describe('ReactUpdatePriority', () => {
beforeEach(() => {
@ -20,6 +23,11 @@ describe('ReactUpdatePriority', () => {
startTransition = React.startTransition;
useState = React.useState;
useEffect = React.useEffect;
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
waitForPaint = InternalTestUtils.waitForPaint;
assertLog = InternalTestUtils.assertLog;
});
function Text({text}) {
@ -43,9 +51,9 @@ describe('ReactUpdatePriority', () => {
root.render(<App />);
});
// Should not have flushed the effect update yet
expect(Scheduler).toHaveYielded([1]);
assertLog([1]);
});
expect(Scheduler).toHaveYielded([2]);
assertLog([2]);
});
test('setState inside passive effect triggered by idle update should have idle priority', async () => {
@ -68,13 +76,13 @@ describe('ReactUpdatePriority', () => {
root.render(<App />);
});
// Should not have flushed the effect update yet
expect(Scheduler).toFlushUntilNextPaint(['Idle: 1, Default: 1']);
await waitForPaint(['Idle: 1, Default: 1']);
// Schedule another update at default priority
setDefaultState(2);
// The default update flushes first, because
expect(Scheduler).toFlushUntilNextPaint([
await waitForPaint([
// Idle update is scheduled
'Idle update',
@ -83,7 +91,7 @@ describe('ReactUpdatePriority', () => {
]);
});
// Now the idle update has flushed
expect(Scheduler).toHaveYielded(['Idle: 2, Default: 2']);
assertLog(['Idle: 2, Default: 2']);
});
test('continuous updates should interrupt transitions', async () => {
@ -111,19 +119,19 @@ describe('ReactUpdatePriority', () => {
await act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['A1', 'B1', 'C1']);
assertLog(['A1', 'B1', 'C1']);
expect(root).toMatchRenderedOutput('A1B1C1');
await act(async () => {
startTransition(() => {
setCounter(2);
});
expect(Scheduler).toFlushAndYieldThrough(['A2']);
await waitFor(['A2']);
ReactNoop.unstable_runWithPriority(ContinuousEventPriority, () => {
setIsHidden(true);
});
});
expect(Scheduler).toHaveYielded([
assertLog([
// Because the hide update has continuous priority, it should interrupt the
// in-progress transition
'(hidden)',

View File

@ -19,6 +19,9 @@ let allSchedulerTags;
let allSchedulerTypes;
let onCommitRootShouldYield;
let act;
let waitFor;
let waitForAll;
let assertLog;
describe('updaters', () => {
beforeEach(() => {
@ -91,6 +94,11 @@ describe('updaters', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
});
it('should report the (host) root as the scheduler for root-level render', async () => {
@ -210,10 +218,7 @@ describe('updaters', () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(async () => {
root.render(<Parent />);
expect(Scheduler).toFlushAndYieldThrough([
'CascadingChild 0',
'onCommitRoot',
]);
await waitFor(['CascadingChild 0', 'onCommitRoot']);
});
expect(triggerActiveCascade).not.toBeNull();
expect(triggerPassiveCascade).not.toBeNull();
@ -221,7 +226,7 @@ describe('updaters', () => {
await act(async () => {
triggerActiveCascade();
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
'CascadingChild 0',
'onCommitRoot',
'CascadingChild 1',
@ -236,7 +241,7 @@ describe('updaters', () => {
await act(async () => {
triggerPassiveCascade();
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
'CascadingChild 1',
'onCommitRoot',
'CascadingChild 2',
@ -252,7 +257,7 @@ describe('updaters', () => {
]);
// Verify no outstanding flushes
Scheduler.unstable_flushAll();
await waitForAll([]);
});
it('should cover suspense pings', async () => {
@ -291,7 +296,7 @@ describe('updaters', () => {
await act(async () => {
ReactDOM.render(<Parent />, document.createElement('div'));
expect(Scheduler).toHaveYielded(['onCommitRoot']);
assertLog(['onCommitRoot']);
});
expect(setShouldSuspend).not.toBeNull();
expect(allSchedulerTypes).toEqual([[null]]);
@ -299,7 +304,7 @@ describe('updaters', () => {
await act(async () => {
setShouldSuspend(true);
});
expect(Scheduler).toHaveYielded(['onCommitRoot']);
assertLog(['onCommitRoot']);
expect(allSchedulerTypes).toEqual([[null], [Suspender]]);
expect(resolver).not.toBeNull();
@ -307,11 +312,11 @@ describe('updaters', () => {
resolver('abc');
return promise;
});
expect(Scheduler).toHaveYielded(['onCommitRoot']);
assertLog(['onCommitRoot']);
expect(allSchedulerTypes).toEqual([[null], [Suspender], [Suspender]]);
// Verify no outstanding flushes
Scheduler.unstable_flushAll();
await waitForAll([]);
});
it('should cover error handling', async () => {
@ -354,7 +359,7 @@ describe('updaters', () => {
await act(async () => {
root.render(<Parent shouldError={false} />);
});
expect(Scheduler).toHaveYielded(['initial', 'onCommitRoot']);
assertLog(['initial', 'onCommitRoot']);
expect(triggerError).not.toBeNull();
allSchedulerTypes.splice(0);
@ -363,11 +368,11 @@ describe('updaters', () => {
await act(async () => {
triggerError();
});
expect(Scheduler).toHaveYielded(['onCommitRoot', 'error', 'onCommitRoot']);
assertLog(['onCommitRoot', 'error', 'onCommitRoot']);
expect(allSchedulerTypes).toEqual([[Parent], [ErrorBoundary]]);
// Verify no outstanding flushes
Scheduler.unstable_flushAll();
await waitForAll([]);
});
it('should distinguish between updaters in the case of interleaved work', async () => {
@ -409,7 +414,7 @@ describe('updaters', () => {
);
// Render everything initially.
expect(Scheduler).toFlushAndYield([
await waitForAll([
'SyncPriorityUpdater 0',
'Yield HighPriority 0',
'LowPriorityUpdater 0',
@ -421,14 +426,14 @@ describe('updaters', () => {
expect(allSchedulerTags).toEqual([[HostRoot]]);
// Render a partial update, but don't finish.
act(() => {
await act(async () => {
triggerLowPriorityUpdate();
expect(Scheduler).toFlushAndYieldThrough(['LowPriorityUpdater 1']);
await waitFor(['LowPriorityUpdater 1']);
expect(allSchedulerTags).toEqual([[HostRoot]]);
// Interrupt with higher priority work.
ReactDOM.flushSync(triggerSyncPriorityUpdate);
expect(Scheduler).toHaveYielded([
assertLog([
'SyncPriorityUpdater 1',
'Yield HighPriority 1',
'onCommitRoot',
@ -437,7 +442,7 @@ describe('updaters', () => {
// Finish the initial partial update
triggerLowPriorityUpdate();
expect(Scheduler).toFlushAndYield([
await waitForAll([
'LowPriorityUpdater 2',
'Yield LowPriority 2',
'onCommitRoot',
@ -455,6 +460,6 @@ describe('updaters', () => {
]);
// Verify no outstanding flushes
Scheduler.unstable_flushAll();
await waitForAll([]);
});
});

View File

@ -13,6 +13,7 @@ let React;
let ReactTestRenderer;
let Scheduler;
let act;
let assertLog;
describe('StrictEffectsMode', () => {
beforeEach(() => {
@ -21,6 +22,9 @@ describe('StrictEffectsMode', () => {
ReactTestRenderer = require('react-test-renderer');
Scheduler = require('scheduler');
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
assertLog = InternalTestUtils.assertLog;
});
function supportsDoubleInvokeEffects() {
@ -51,10 +55,7 @@ describe('StrictEffectsMode', () => {
ReactTestRenderer.create(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
});
it('double invoking for effects works properly', () => {
@ -80,7 +81,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect mount',
'useEffect mount',
'useLayoutEffect unmount',
@ -89,17 +90,14 @@ describe('StrictEffectsMode', () => {
'useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
}
act(() => {
renderer.update(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
@ -110,10 +108,7 @@ describe('StrictEffectsMode', () => {
renderer.unmount();
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect unmount',
'useEffect unmount',
]);
assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
});
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
@ -139,7 +134,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'useEffect One mount',
'useEffect Two mount',
'useEffect One unmount',
@ -148,17 +143,14 @@ describe('StrictEffectsMode', () => {
'useEffect Two mount',
]);
} else {
expect(Scheduler).toHaveYielded([
'useEffect One mount',
'useEffect Two mount',
]);
assertLog(['useEffect One mount', 'useEffect Two mount']);
}
act(() => {
renderer.update(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useEffect One unmount',
'useEffect Two unmount',
'useEffect One mount',
@ -169,10 +161,7 @@ describe('StrictEffectsMode', () => {
renderer.unmount(null);
});
expect(Scheduler).toHaveYielded([
'useEffect One unmount',
'useEffect Two unmount',
]);
assertLog(['useEffect One unmount', 'useEffect Two unmount']);
});
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
@ -200,7 +189,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect One mount',
'useLayoutEffect Two mount',
'useLayoutEffect One unmount',
@ -209,17 +198,14 @@ describe('StrictEffectsMode', () => {
'useLayoutEffect Two mount',
]);
} else {
expect(Scheduler).toHaveYielded([
'useLayoutEffect One mount',
'useLayoutEffect Two mount',
]);
assertLog(['useLayoutEffect One mount', 'useLayoutEffect Two mount']);
}
act(() => {
renderer.update(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect One unmount',
'useLayoutEffect Two unmount',
'useLayoutEffect One mount',
@ -230,10 +216,7 @@ describe('StrictEffectsMode', () => {
renderer.unmount();
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect One unmount',
'useLayoutEffect Two unmount',
]);
assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
});
it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {
@ -257,33 +240,27 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect mount',
'useEffect mount',
'useLayoutEffect mount',
'useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
}
act(() => {
renderer.update(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
act(() => {
renderer.unmount();
});
expect(Scheduler).toHaveYielded([]);
assertLog([]);
});
it('passes the right context to class component lifecycles', () => {
@ -315,13 +292,13 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'componentWillUnmount',
'componentDidMount',
]);
} else {
expect(Scheduler).toHaveYielded(['componentDidMount']);
assertLog(['componentDidMount']);
}
});
@ -352,26 +329,26 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'componentWillUnmount',
'componentDidMount',
]);
} else {
expect(Scheduler).toHaveYielded(['componentDidMount']);
assertLog(['componentDidMount']);
}
act(() => {
renderer.update(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded(['componentDidUpdate']);
assertLog(['componentDidUpdate']);
act(() => {
renderer.unmount();
});
expect(Scheduler).toHaveYielded(['componentWillUnmount']);
assertLog(['componentWillUnmount']);
});
it('should not double invoke class lifecycles in legacy mode', () => {
@ -397,7 +374,7 @@ describe('StrictEffectsMode', () => {
ReactTestRenderer.create(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded(['componentDidMount']);
assertLog(['componentDidMount']);
});
it('double flushing passive effects only results in one double invoke', () => {
@ -427,7 +404,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'mount',
'useLayoutEffect mount',
'useEffect mount',
@ -442,7 +419,7 @@ describe('StrictEffectsMode', () => {
'useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
assertLog([
'mount',
'useLayoutEffect mount',
'useEffect mount',
@ -492,7 +469,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'App useLayoutEffect mount',
'App useEffect mount',
'App useLayoutEffect unmount',
@ -501,10 +478,7 @@ describe('StrictEffectsMode', () => {
'App useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
'App useLayoutEffect mount',
'App useEffect mount',
]);
assertLog(['App useLayoutEffect mount', 'App useEffect mount']);
}
act(() => {
@ -512,7 +486,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'App useLayoutEffect unmount',
'Child useLayoutEffect mount',
'App useLayoutEffect mount',
@ -525,7 +499,7 @@ describe('StrictEffectsMode', () => {
'Child useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
assertLog([
'App useLayoutEffect unmount',
'Child useLayoutEffect mount',
'App useLayoutEffect mount',
@ -580,7 +554,7 @@ describe('StrictEffectsMode', () => {
});
if (supportsDoubleInvokeEffects()) {
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'useLayoutEffect mount',
'useEffect mount',
@ -592,7 +566,7 @@ describe('StrictEffectsMode', () => {
'useEffect mount',
]);
} else {
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'useLayoutEffect mount',
'useEffect mount',
@ -603,7 +577,7 @@ describe('StrictEffectsMode', () => {
renderer.update(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
@ -614,7 +588,7 @@ describe('StrictEffectsMode', () => {
renderer.unmount();
});
expect(Scheduler).toHaveYielded([
assertLog([
'componentWillUnmount',
'useLayoutEffect unmount',
'useEffect unmount',

View File

@ -13,6 +13,10 @@ let React;
let ReactNoop;
let Scheduler;
let act;
let assertLog;
let waitFor;
let waitForAll;
let waitForPaint;
describe('StrictEffectsMode defaults', () => {
beforeEach(() => {
@ -23,6 +27,12 @@ describe('StrictEffectsMode defaults', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
waitForAll = InternalTestUtils.waitForAll;
waitForPaint = InternalTestUtils.waitForPaint;
assertLog = InternalTestUtils.assertLog;
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__;
});
@ -46,10 +56,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
});
it('should not double invoke class lifecycles in legacy mode', () => {
@ -75,11 +82,11 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded(['componentDidMount']);
assertLog(['componentDidMount']);
});
if (__DEV__) {
it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => {
it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', async () => {
function ComponentWithEffects({label}) {
React.useLayoutEffect(() => {
Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`);
@ -90,21 +97,21 @@ describe('StrictEffectsMode defaults', () => {
return label;
}
act(() => {
await act(async () => {
ReactNoop.render(
<>
<ComponentWithEffects label={'one'} />
</>,
);
expect(Scheduler).toFlushUntilNextPaint([
await waitForPaint([
'useLayoutEffect mount "one"',
'useLayoutEffect unmount "one"',
'useLayoutEffect mount "one"',
]);
});
act(() => {
await act(async () => {
ReactNoop.render(
<>
<ComponentWithEffects label={'one'} />
@ -112,8 +119,8 @@ describe('StrictEffectsMode defaults', () => {
</>,
);
expect(Scheduler).toHaveYielded([]);
expect(Scheduler).toFlushUntilNextPaint([
assertLog([]);
await waitForPaint([
// Cleanup and re-run "one" (and "two") since there is no dependencies array.
'useLayoutEffect unmount "one"',
'useLayoutEffect mount "one"',
@ -128,7 +135,7 @@ describe('StrictEffectsMode defaults', () => {
// This test also verifies that double-invoked effects flush synchronously
// within the same frame as passive effects.
it('should double invoke effects only for newly mounted components', () => {
it('should double invoke effects only for newly mounted components', async () => {
function ComponentWithEffects({label}) {
React.useEffect(() => {
Scheduler.unstable_yieldValue(`useEffect mount "${label}"`);
@ -145,14 +152,14 @@ describe('StrictEffectsMode defaults', () => {
return label;
}
act(() => {
await act(async () => {
ReactNoop.render(
<>
<ComponentWithEffects label={'one'} />
</>,
);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'useLayoutEffect mount "one"',
'useEffect mount "one"',
'useLayoutEffect unmount "one"',
@ -162,7 +169,7 @@ describe('StrictEffectsMode defaults', () => {
]);
});
act(() => {
await act(async () => {
ReactNoop.render(
<>
<ComponentWithEffects label={'one'} />
@ -170,13 +177,13 @@ describe('StrictEffectsMode defaults', () => {
</>,
);
expect(Scheduler).toFlushAndYieldThrough([
await waitFor([
// Cleanup and re-run "one" (and "two") since there is no dependencies array.
'useLayoutEffect unmount "one"',
'useLayoutEffect mount "one"',
'useLayoutEffect mount "two"',
]);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'useEffect unmount "one"',
'useEffect mount "one"',
'useEffect mount "two"',
@ -208,7 +215,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect mount',
'useEffect mount',
'useLayoutEffect unmount',
@ -221,7 +228,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
@ -232,10 +239,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect unmount',
'useEffect unmount',
]);
assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
});
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
@ -257,7 +261,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useEffect One mount',
'useEffect Two mount',
'useEffect One unmount',
@ -270,7 +274,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useEffect One unmount',
'useEffect Two unmount',
'useEffect One mount',
@ -281,10 +285,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded([
'useEffect One unmount',
'useEffect Two unmount',
]);
assertLog(['useEffect One unmount', 'useEffect Two unmount']);
});
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
@ -308,7 +309,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect One mount',
'useLayoutEffect Two mount',
'useLayoutEffect One unmount',
@ -321,7 +322,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect One unmount',
'useLayoutEffect Two unmount',
'useLayoutEffect One mount',
@ -332,10 +333,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect One unmount',
'useLayoutEffect Two unmount',
]);
assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
});
it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {
@ -355,7 +353,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect mount',
'useEffect mount',
'useLayoutEffect mount',
@ -366,16 +364,13 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded([
'useLayoutEffect mount',
'useEffect mount',
]);
assertLog(['useLayoutEffect mount', 'useEffect mount']);
act(() => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded([]);
assertLog([]);
});
//@gate useModernStrictMode
@ -430,7 +425,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'componentWillUnmount',
'componentDidMount',
@ -460,7 +455,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'componentWillUnmount',
'componentDidMount',
@ -470,13 +465,13 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'update'} />);
});
expect(Scheduler).toHaveYielded(['componentDidUpdate']);
assertLog(['componentDidUpdate']);
act(() => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded(['componentWillUnmount']);
assertLog(['componentWillUnmount']);
});
it('double flushing passive effects only results in one double invoke', () => {
@ -503,7 +498,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'mount',
'useLayoutEffect mount',
'useEffect mount',
@ -555,7 +550,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'App useLayoutEffect mount',
'App useEffect mount',
'App useLayoutEffect unmount',
@ -568,7 +563,7 @@ describe('StrictEffectsMode defaults', () => {
_setShowChild(true);
});
expect(Scheduler).toHaveYielded([
assertLog([
'App useLayoutEffect unmount',
'Child useLayoutEffect mount',
'App useLayoutEffect mount',
@ -622,7 +617,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'componentDidMount',
'useLayoutEffect mount',
'useEffect mount',
@ -638,7 +633,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(<App text={'mount'} />);
});
expect(Scheduler).toHaveYielded([
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
@ -649,7 +644,7 @@ describe('StrictEffectsMode defaults', () => {
ReactNoop.render(null);
});
expect(Scheduler).toHaveYielded([
assertLog([
'componentWillUnmount',
'useLayoutEffect unmount',
'useEffect unmount',

View File

@ -26,6 +26,8 @@ describe('useEffectEvent', () => {
let useEffect;
let useLayoutEffect;
let useMemo;
let waitForAll;
let assertLog;
beforeEach(() => {
React = require('react');
@ -40,6 +42,10 @@ describe('useEffectEvent', () => {
useEffect = React.useEffect;
useLayoutEffect = React.useLayoutEffect;
useMemo = React.useMemo;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
assertLog = InternalTestUtils.assertLog;
});
function Text(props) {
@ -48,7 +54,7 @@ describe('useEffectEvent', () => {
}
// @gate enableUseEffectEventHook
it('memoizes basic case correctly', () => {
it('memoizes basic case correctly', async () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@ -72,7 +78,7 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Counter incrementBy={1} />);
expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
await waitForAll(['Increment', 'Count: 0']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -81,7 +87,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 1']);
assertLog(['Increment', 'Count: 1']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -90,7 +96,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Event should use the updated callback function closed over the new value.
'Count: 2',
@ -104,7 +110,7 @@ describe('useEffectEvent', () => {
// Increase the increment prop amount
ReactNoop.render(<Counter incrementBy={10} />);
expect(Scheduler).toFlushAndYield(['Increment', 'Count: 2']);
await waitForAll(['Increment', 'Count: 2']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -114,7 +120,7 @@ describe('useEffectEvent', () => {
// Event uses the new prop
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 12']);
assertLog(['Increment', 'Count: 12']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -124,7 +130,7 @@ describe('useEffectEvent', () => {
});
// @gate enableUseEffectEventHook
it('can be defined more than once', () => {
it('can be defined more than once', async () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@ -158,7 +164,7 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Counter incrementBy={5} />);
expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
await waitForAll(['Increment', 'Count: 0']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -167,7 +173,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 5']);
assertLog(['Increment', 'Count: 5']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -176,7 +182,7 @@ describe('useEffectEvent', () => {
);
act(button.current.multiply);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 25']);
assertLog(['Increment', 'Count: 25']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -186,7 +192,7 @@ describe('useEffectEvent', () => {
});
// @gate enableUseEffectEventHook
it('does not preserve `this` in event functions', () => {
it('does not preserve `this` in event functions', async () => {
class GreetButton extends React.PureComponent {
greet = () => {
this.props.onClick();
@ -217,7 +223,7 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Greeter hello={'hej'} />);
expect(Scheduler).toFlushAndYield(['Say hej', 'Greeting: Seb says hej']);
await waitForAll(['Say hej', 'Greeting: Seb says hej']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Say hej" />
@ -226,10 +232,7 @@ describe('useEffectEvent', () => {
);
act(button.current.greet);
expect(Scheduler).toHaveYielded([
'Say hej',
'Greeting: undefined says hej',
]);
assertLog(['Say hej', 'Greeting: undefined says hej']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Say hej" />
@ -272,11 +275,11 @@ describe('useEffectEvent', () => {
// If something throws, we try one more time synchronously in case the error was
// caused by a data race. See recoverFromConcurrentError
expect(Scheduler).toHaveYielded(['Count: 0', 'Count: 0']);
assertLog(['Count: 0', 'Count: 0']);
});
// @gate enableUseEffectEventHook
it("useLayoutEffect shouldn't re-fire when event handlers change", () => {
it("useLayoutEffect shouldn't re-fire when event handlers change", async () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@ -307,8 +310,8 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Counter incrementBy={1} />);
expect(Scheduler).toHaveYielded([]);
expect(Scheduler).toFlushAndYield([
assertLog([]);
await waitForAll([
'Increment',
'Count: 0',
'Effect: by 2',
@ -323,7 +326,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Effect should not re-run because the dependency hasn't changed.
'Count: 3',
@ -336,7 +339,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Event should use the updated callback function closed over the new value.
'Count: 4',
@ -350,7 +353,7 @@ describe('useEffectEvent', () => {
// Increase the increment prop amount
ReactNoop.render(<Counter incrementBy={10} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Increment',
'Count: 4',
'Effect: by 20',
@ -366,7 +369,7 @@ describe('useEffectEvent', () => {
// Event uses the new prop
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
assertLog(['Increment', 'Count: 34']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -376,7 +379,7 @@ describe('useEffectEvent', () => {
});
// @gate enableUseEffectEventHook
it("useEffect shouldn't re-fire when event handlers change", () => {
it("useEffect shouldn't re-fire when event handlers change", async () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@ -407,7 +410,7 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Counter incrementBy={1} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Increment',
'Count: 0',
'Effect: by 2',
@ -422,7 +425,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Effect should not re-run because the dependency hasn't changed.
'Count: 3',
@ -435,7 +438,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Event should use the updated callback function closed over the new value.
'Count: 4',
@ -449,7 +452,7 @@ describe('useEffectEvent', () => {
// Increase the increment prop amount
ReactNoop.render(<Counter incrementBy={10} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Increment',
'Count: 4',
'Effect: by 20',
@ -465,7 +468,7 @@ describe('useEffectEvent', () => {
// Event uses the new prop
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
assertLog(['Increment', 'Count: 34']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -475,7 +478,7 @@ describe('useEffectEvent', () => {
});
// @gate enableUseEffectEventHook
it('is stable in a custom hook', () => {
it('is stable in a custom hook', async () => {
class IncrementButton extends React.PureComponent {
increment = () => {
this.props.onClick();
@ -512,7 +515,7 @@ describe('useEffectEvent', () => {
const button = React.createRef(null);
ReactNoop.render(<Counter incrementBy={1} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Increment',
'Count: 0',
'Effect: by 2',
@ -527,7 +530,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Effect should not re-run because the dependency hasn't changed.
'Count: 3',
@ -540,7 +543,7 @@ describe('useEffectEvent', () => {
);
act(button.current.increment);
expect(Scheduler).toHaveYielded([
assertLog([
'Increment',
// Event should use the updated callback function closed over the new value.
'Count: 4',
@ -554,7 +557,7 @@ describe('useEffectEvent', () => {
// Increase the increment prop amount
ReactNoop.render(<Counter incrementBy={10} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'Increment',
'Count: 4',
'Effect: by 20',
@ -570,7 +573,7 @@ describe('useEffectEvent', () => {
// Event uses the new prop
act(button.current.increment);
expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
assertLog(['Increment', 'Count: 34']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="Increment" />
@ -580,7 +583,7 @@ describe('useEffectEvent', () => {
});
// @gate enableUseEffectEventHook
it('is mutated before all other effects', () => {
it('is mutated before all other effects', async () => {
function Counter({value}) {
useInsertionEffect(() => {
Scheduler.unstable_yieldValue('Effect value: ' + value);
@ -597,14 +600,14 @@ describe('useEffectEvent', () => {
}
ReactNoop.render(<Counter value={1} />);
expect(Scheduler).toFlushAndYield(['Effect value: 1', 'Event value: 1']);
await waitForAll(['Effect value: 1', 'Event value: 1']);
act(() => ReactNoop.render(<Counter value={2} />));
expect(Scheduler).toHaveYielded(['Effect value: 2', 'Event value: 2']);
assertLog(['Effect value: 2', 'Event value: 2']);
});
// @gate enableUseEffectEventHook
it("doesn't provide a stable identity", () => {
it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Scheduler.unstable_yieldValue(
@ -627,16 +630,16 @@ describe('useEffectEvent', () => {
}
ReactNoop.render(<Counter shouldRender={true} value={0} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'onClick, shouldRender=true, value=0',
'onClick, shouldRender=true, value=0',
]);
ReactNoop.render(<Counter shouldRender={true} value={1} />);
expect(Scheduler).toFlushAndYield(['onClick, shouldRender=true, value=1']);
await waitForAll(['onClick, shouldRender=true, value=1']);
ReactNoop.render(<Counter shouldRender={false} value={2} />);
expect(Scheduler).toFlushAndYield([
await waitForAll([
'onClick, shouldRender=false, value=2',
'onClick, shouldRender=false, value=2',
]);
@ -676,7 +679,7 @@ describe('useEffectEvent', () => {
await act(async () => {
root.render(<App value={1} />);
});
expect(Scheduler).toHaveYielded(['Commit new event handler']);
assertLog(['Commit new event handler']);
expect(root).toMatchRenderedOutput('Latest rendered value 1');
expect(committedEventHandler()).toBe('Value seen by useEffectEvent: 1');
@ -686,14 +689,14 @@ describe('useEffectEvent', () => {
});
// No new event handler should be committed, because it was omitted from
// the dependency array.
expect(Scheduler).toHaveYielded([]);
assertLog([]);
// But the event handler should still be able to see the latest value.
expect(root).toMatchRenderedOutput('Latest rendered value 2');
expect(committedEventHandler()).toBe('Value seen by useEffectEvent: 2');
});
// @gate enableUseEffectEventHook
it('integration: implements docs chat room example', () => {
it('integration: implements docs chat room example', async () => {
function createConnection() {
let connectedCallback;
let timeout;
@ -738,47 +741,47 @@ describe('useEffectEvent', () => {
}
act(() => ReactNoop.render(<ChatRoom roomId="general" theme="light" />));
expect(Scheduler).toHaveYielded(['Welcome to the general room!']);
assertLog(['Welcome to the general room!']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the general room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
expect(Scheduler).toHaveYielded(['Connected! theme: light']);
assertLog(['Connected! theme: light']);
// change roomId only
act(() => ReactNoop.render(<ChatRoom roomId="music" theme="light" />));
expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
assertLog(['Welcome to the music room!']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the music room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should trigger a reconnect
expect(Scheduler).toHaveYielded(['Connected! theme: light']);
assertLog(['Connected! theme: light']);
// change theme only
act(() => ReactNoop.render(<ChatRoom roomId="music" theme="dark" />));
expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
assertLog(['Welcome to the music room!']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the music room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should not trigger a reconnect
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
// change roomId only
act(() => ReactNoop.render(<ChatRoom roomId="travel" theme="dark" />));
expect(Scheduler).toHaveYielded(['Welcome to the travel room!']);
assertLog(['Welcome to the travel room!']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the travel room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should trigger a reconnect
expect(Scheduler).toHaveYielded(['Connected! theme: dark']);
assertLog(['Connected! theme: dark']);
});
// @gate enableUseEffectEventHook
@ -837,12 +840,9 @@ describe('useEffectEvent', () => {
</AppShell>,
),
);
expect(Scheduler).toHaveYielded([
'Add to cart',
'url: /shop/1, numberOfItems: 0',
]);
assertLog(['Add to cart', 'url: /shop/1, numberOfItems: 0']);
act(button.current.addToCart);
expect(Scheduler).toHaveYielded(['Add to cart']);
assertLog(['Add to cart']);
act(() =>
ReactNoop.render(
@ -851,9 +851,6 @@ describe('useEffectEvent', () => {
</AppShell>,
),
);
expect(Scheduler).toHaveYielded([
'Add to cart',
'url: /shop/2, numberOfItems: 1',
]);
assertLog(['Add to cart', 'url: /shop/2, numberOfItems: 1']);
});
});