Skip to content

Commit 0711bb0

Browse files
committed
Improve tests that use discrete events
1 parent a6b5256 commit 0711bb0

File tree

8 files changed

+114
-99
lines changed

8 files changed

+114
-99
lines changed

packages/react-dom/src/__tests__/ReactDOMHooks-test.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
let React;
1313
let ReactDOM;
1414
let Scheduler;
15+
let act;
1516

1617
describe('ReactDOMHooks', () => {
1718
let container;
@@ -22,6 +23,7 @@ describe('ReactDOMHooks', () => {
2223
React = require('react');
2324
ReactDOM = require('react-dom');
2425
Scheduler = require('scheduler');
26+
act = require('react-dom/test-utils').unstable_concurrentAct;
2527

2628
container = document.createElement('div');
2729
document.body.appendChild(container);
@@ -106,7 +108,7 @@ describe('ReactDOMHooks', () => {
106108
});
107109

108110
// @gate experimental
109-
it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', () => {
111+
it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', async () => {
110112
const {createRef, useCallback, useState} = React;
111113

112114
const Example = ({inputRef, labelRef}) => {
@@ -132,11 +134,14 @@ describe('ReactDOMHooks', () => {
132134
Scheduler.unstable_flushAll();
133135

134136
inputRef.current.value = 'abc';
135-
inputRef.current.dispatchEvent(
136-
new Event('input', {bubbles: true, cancelable: true}),
137-
);
138-
139-
Scheduler.unstable_flushAll();
137+
await act(async () => {
138+
inputRef.current.dispatchEvent(
139+
new Event('input', {
140+
bubbles: true,
141+
cancelable: true,
142+
}),
143+
);
144+
});
140145

141146
expect(labelRef.current.innerHTML).toBe('abc');
142147
});

packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function runActTests(label, render, unmount, rerender) {
187187
expect(Scheduler).toHaveYielded([100]);
188188
});
189189

190-
it('flushes effects on every call', () => {
190+
it('flushes effects on every call', async () => {
191191
function App() {
192192
const [ctr, setCtr] = React.useState(0);
193193
React.useEffect(() => {
@@ -209,16 +209,16 @@ function runActTests(label, render, unmount, rerender) {
209209
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
210210
}
211211

212-
act(() => {
212+
await act(async () => {
213213
click();
214214
click();
215215
click();
216216
});
217217
// it consolidates the 3 updates, then fires the effect
218218
expect(Scheduler).toHaveYielded([3]);
219-
act(click);
219+
await act(async () => click());
220220
expect(Scheduler).toHaveYielded([4]);
221-
act(click);
221+
await act(async () => click());
222222
expect(Scheduler).toHaveYielded([5]);
223223
expect(button.innerHTML).toBe('5');
224224
});

packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ describe('ChangeEventPlugin', () => {
685685
});
686686

687687
// @gate experimental
688-
it('is async for non-input events', () => {
688+
it('is async for non-input events', async () => {
689689
const root = ReactDOM.unstable_createRoot(container);
690690
let input;
691691

packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('SimpleEventPlugin', function() {
1313
let React;
1414
let ReactDOM;
1515
let Scheduler;
16+
let TestUtils;
1617

1718
let onClick;
1819
let container;
@@ -39,6 +40,7 @@ describe('SimpleEventPlugin', function() {
3940
React = require('react');
4041
ReactDOM = require('react-dom');
4142
Scheduler = require('scheduler');
43+
TestUtils = require('react-dom/test-utils');
4244

4345
onClick = jest.fn();
4446
});
@@ -314,7 +316,7 @@ describe('SimpleEventPlugin', function() {
314316
});
315317

316318
// @gate experimental
317-
it('end result of many interactive updates is deterministic', () => {
319+
it('end result of many interactive updates is deterministic', async () => {
318320
container = document.createElement('div');
319321
const root = ReactDOM.unstable_createRoot(container);
320322
document.body.appendChild(container);
@@ -361,12 +363,14 @@ describe('SimpleEventPlugin', function() {
361363
expect(button.textContent).toEqual('Count: 0');
362364

363365
// Click the button many more times
364-
click();
365-
click();
366-
click();
367-
click();
368-
click();
369-
click();
366+
await TestUtils.act(async () => {
367+
click();
368+
click();
369+
click();
370+
click();
371+
click();
372+
click();
373+
});
370374

371375
// Flush the remaining work
372376
Scheduler.unstable_flushAll();
@@ -375,7 +379,7 @@ describe('SimpleEventPlugin', function() {
375379
});
376380

377381
// @gate experimental
378-
it('flushes discrete updates in order', () => {
382+
it('flushes discrete updates in order', async () => {
379383
container = document.createElement('div');
380384
document.body.appendChild(container);
381385

packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js

+27-24
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ describe('ReactIncrementalErrorHandling', () => {
232232
expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
233233
});
234234

235-
it("retries at a lower priority if there's additional pending work", () => {
235+
// @gate experimental
236+
it("retries at a lower priority if there's additional pending work", async () => {
236237
function App(props) {
237238
if (props.isBroken) {
238239
Scheduler.unstable_yieldValue('error');
@@ -252,14 +253,14 @@ describe('ReactIncrementalErrorHandling', () => {
252253
});
253254
}
254255

255-
ReactNoop.discreteUpdates(() => {
256-
ReactNoop.render(<App isBroken={true} />, onCommit);
257-
});
256+
ReactNoop.render(<App isBroken={true} />, onCommit);
258257
expect(Scheduler).toFlushAndYieldThrough(['error']);
259258
interrupt();
260259

261-
// This update is in a separate batch
262-
ReactNoop.render(<App isBroken={false} />, onCommit);
260+
React.unstable_startTransition(() => {
261+
// This update is in a separate batch
262+
ReactNoop.render(<App isBroken={false} />, onCommit);
263+
});
263264

264265
expect(Scheduler).toFlushAndYieldThrough([
265266
// The first render fails. But because there's a lower priority pending
@@ -311,16 +312,16 @@ describe('ReactIncrementalErrorHandling', () => {
311312
});
312313
}
313314

314-
ReactNoop.discreteUpdates(() => {
315-
ReactNoop.render(<App isBroken={true} />, onCommit);
316-
});
315+
ReactNoop.render(<App isBroken={true} />, onCommit);
317316
expect(Scheduler).toFlushAndYieldThrough(['error']);
318317
interrupt();
319318

320319
expect(ReactNoop).toMatchRenderedOutput(null);
321320

322-
// This update is in a separate batch
323-
ReactNoop.render(<App isBroken={false} />, onCommit);
321+
React.unstable_startTransition(() => {
322+
// This update is in a separate batch
323+
ReactNoop.render(<App isBroken={false} />, onCommit);
324+
});
324325

325326
expect(Scheduler).toFlushAndYieldThrough([
326327
// The first render fails. But because there's a lower priority pending
@@ -1780,6 +1781,7 @@ describe('ReactIncrementalErrorHandling', () => {
17801781
});
17811782
}
17821783

1784+
// @gate experimental
17831785
it('uncaught errors should be discarded if the render is aborted', async () => {
17841786
const root = ReactNoop.createRoot();
17851787

@@ -1789,22 +1791,24 @@ describe('ReactIncrementalErrorHandling', () => {
17891791
}
17901792

17911793
await ReactNoop.act(async () => {
1792-
ReactNoop.discreteUpdates(() => {
1793-
root.render(<Oops />);
1794-
});
1794+
root.render(<Oops />);
1795+
17951796
// Render past the component that throws, then yield.
17961797
expect(Scheduler).toFlushAndYieldThrough(['Oops']);
17971798
expect(root).toMatchRenderedOutput(null);
17981799
// Interleaved update. When the root completes, instead of throwing the
17991800
// error, it should try rendering again. This update will cause it to
18001801
// recover gracefully.
1801-
root.render('Everything is fine.');
1802+
React.unstable_startTransition(() => {
1803+
root.render('Everything is fine.');
1804+
});
18021805
});
18031806

18041807
// Should finish without throwing.
18051808
expect(root).toMatchRenderedOutput('Everything is fine.');
18061809
});
18071810

1811+
// @gate experimental
18081812
it('uncaught errors are discarded if the render is aborted, case 2', async () => {
18091813
const {useState} = React;
18101814
const root = ReactNoop.createRoot();
@@ -1829,21 +1833,20 @@ describe('ReactIncrementalErrorHandling', () => {
18291833
});
18301834

18311835
await ReactNoop.act(async () => {
1832-
// Schedule a high pri and a low pri update on the root.
1833-
ReactNoop.discreteUpdates(() => {
1834-
root.render(<Oops />);
1836+
// Schedule a default pri and a low pri update on the root.
1837+
root.render(<Oops />);
1838+
React.unstable_startTransition(() => {
1839+
root.render(<AllGood />);
18351840
});
1836-
root.render(<AllGood />);
1837-
// Render through just the high pri update. The low pri update remains on
1841+
1842+
// Render through just the default pri update. The low pri update remains on
18381843
// the queue.
18391844
expect(Scheduler).toFlushAndYieldThrough(['Everything is fine.']);
18401845

1841-
// Schedule a high pri update on a child that triggers an error.
1846+
// Schedule a default pri update on a child that triggers an error.
18421847
// The root should capture this error. But since there's still a pending
18431848
// update on the root, the error should be suppressed.
1844-
ReactNoop.discreteUpdates(() => {
1845-
setShouldThrow(true);
1846-
});
1849+
setShouldThrow(true);
18471850
});
18481851
// Should render the final state without throwing the error.
18491852
expect(Scheduler).toHaveYielded(['Everything is fine.']);

packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js

+44-47
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
570570
);
571571
}
572572

573-
// Schedule a high pri update and a low pri update, without rendering in
574-
// between.
575-
ReactNoop.discreteUpdates(() => {
576-
// High pri
577-
ReactNoop.render(<App />);
578-
});
573+
// Schedule a default pri update and a low pri update, without rendering in between.
574+
// Default pri
575+
ReactNoop.render(<App />);
579576
// Low pri
580-
ReactNoop.render(<App hide={true} />);
577+
React.unstable_startTransition(() => {
578+
ReactNoop.render(<App hide={true} />);
579+
});
581580

582581
expect(Scheduler).toFlushAndYield([
583582
// The first update suspends
@@ -1879,9 +1878,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
18791878
ReactNoop.render(<Foo />);
18801879
expect(Scheduler).toFlushAndYield(['Foo']);
18811880

1882-
ReactNoop.discreteUpdates(() =>
1883-
ReactNoop.render(<Foo renderContent={true} />),
1884-
);
1881+
ReactNoop.render(<Foo renderContent={true} />);
18851882
expect(Scheduler).toFlushAndYieldThrough(['Foo']);
18861883

18871884
// Advance some time.
@@ -3080,48 +3077,48 @@ describe('ReactSuspenseWithNoopRenderer', () => {
30803077
// Schedule an update inside the Suspense boundary that suspends.
30813078
setAppText('B');
30823079
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
3080+
});
30833081

3084-
// Commit the placeholder
3085-
await advanceTimers(250);
3086-
expect(root).toMatchRenderedOutput(
3087-
<>
3088-
<span hidden={true} prop="A" />
3089-
<span prop="Loading..." />
3090-
</>,
3091-
);
3082+
expect(root).toMatchRenderedOutput(
3083+
<>
3084+
<span hidden={true} prop="A" />
3085+
<span prop="Loading..." />
3086+
</>,
3087+
);
30923088

3093-
// Schedule a high pri update on the boundary, and a lower pri update
3094-
// on the fallback. We're testing to make sure the fallback can still
3095-
// update even though the primary tree is suspended.{
3096-
ReactNoop.discreteUpdates(() => {
3097-
setAppText('C');
3089+
// Schedule a default pri update on the boundary, and a lower pri update
3090+
// on the fallback. We're testing to make sure the fallback can still
3091+
// update even though the primary tree is suspended.
3092+
await ReactNoop.act(async () => {
3093+
setAppText('C');
3094+
React.unstable_startTransition(() => {
3095+
setFallbackText('Still loading...');
30983096
});
3099-
setFallbackText('Still loading...');
3097+
});
31003098

3101-
expect(Scheduler).toFlushAndYield([
3102-
// First try to render the high pri update. Still suspended.
3103-
'Suspend! [C]',
3104-
'Loading...',
3099+
expect(Scheduler).toHaveYielded([
3100+
// First try to render the high pri update. Still suspended.
3101+
'Suspend! [C]',
3102+
'Loading...',
31053103

3106-
// In the expiration times model, once the high pri update suspends,
3107-
// we can't be sure if there's additional work at a lower priority
3108-
// that might unblock the tree. We do know that there's a lower
3109-
// priority update *somehwere* in the entire root, though (the update
3110-
// to the fallback). So we try rendering one more time, just in case.
3111-
// TODO: We shouldn't need to do this with lanes, because we always
3112-
// know exactly which lanes have pending work in each tree.
3113-
'Suspend! [C]',
3114-
3115-
// Then complete the update to the fallback.
3116-
'Still loading...',
3117-
]);
3118-
expect(root).toMatchRenderedOutput(
3119-
<>
3120-
<span hidden={true} prop="A" />
3121-
<span prop="Still loading..." />
3122-
</>,
3123-
);
3124-
});
3104+
// In the expiration times model, once the high pri update suspends,
3105+
// we can't be sure if there's additional work at a lower priority
3106+
// that might unblock the tree. We do know that there's a lower
3107+
// priority update *somehwere* in the entire root, though (the update
3108+
// to the fallback). So we try rendering one more time, just in case.
3109+
// TODO: We shouldn't need to do this with lanes, because we always
3110+
// know exactly which lanes have pending work in each tree.
3111+
'Suspend! [C]',
3112+
3113+
// Then complete the update to the fallback.
3114+
'Still loading...',
3115+
]);
3116+
expect(root).toMatchRenderedOutput(
3117+
<>
3118+
<span hidden={true} prop="A" />
3119+
<span prop="Still loading..." />
3120+
</>,
3121+
);
31253122
},
31263123
);
31273124

packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1397,12 +1397,13 @@ describe('useMutableSource', () => {
13971397
// Now mutate A. Both hooks should update.
13981398
// This is at high priority so that it doesn't get batched with default
13991399
// priority updates that might fire during the passive effect
1400-
ReactNoop.discreteUpdates(() => {
1401-
mutateA('a1');
1400+
await ReactNoop.act(async () => {
1401+
ReactNoop.discreteUpdates(() => {
1402+
mutateA('a1');
1403+
});
14021404
});
1403-
expect(Scheduler).toFlushUntilNextPaint([]);
14041405

1405-
expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');
1406+
expect(root).toMatchRenderedOutput('first: a1, second: a1');
14061407
});
14071408

14081409
expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');

0 commit comments

Comments
 (0)