11
let ReactDOM;
12
let ReactTestUtils;
13
let SchedulerTracing;
14
+let Scheduler;
15
let act;
16
let container;
17
26
}
27
28
describe('ReactTestUtils.act()', () => {
28
- beforeEach(() => {
29
- jest.resetModules();
30
- React = require('react');
31
- ReactDOM = require('react-dom');
32
- ReactTestUtils = require('react-dom/test-utils');
33
- SchedulerTracing = require('scheduler/tracing');
34
- act = ReactTestUtils.act;
35
- container = document.createElement('div');
36
- document.body.appendChild(container);
37
- });
38
- afterEach(() => {
39
- ReactDOM.unmountComponentAtNode(container);
40
- document.body.removeChild(container);
41
- });
42
-
43
- describe('sync', () => {
44
- it('can use act to flush effects', () => {
45
- function App(props) {
46
- React.useEffect(props.callback);
47
- return null;
48
- }
49
-
50
- let calledLog = [];
51
- act(() => {
52
- ReactDOM.render(
53
- <App
54
- callback={() => {
55
- calledLog.push(calledLog.length);
56
- }}
57
- />,
58
- document.createElement('div'),
59
- );
60
- });
29
+ // first we run all the tests with concurrent mode
30
+ let concurrentRoot;
31
+ function renderConcurrent(el, dom) {
32
+ concurrentRoot = ReactDOM.unstable_createRoot(dom);
33
+ concurrentRoot.render(el);
34
+ }
35
+ function unmountConcurrent(_dom) {
36
+ if (concurrentRoot !== null) {
37
+ concurrentRoot.unmount();
38
+ concurrentRoot = null;
39
+ }
40
+ }
41
+ runActTests('concurrent mode', renderConcurrent, unmountConcurrent);
42
+
43
+ // and then in sync mode
44
+ function renderSync(el, dom) {
45
+ ReactDOM.render(el, dom);
46
+ }
47
+ function unmountSync(dom) {
48
+ ReactDOM.unmountComponentAtNode(dom);
49
+ }
50
+ runActTests('legacy sync mode', renderSync, unmountSync);
51
+});
52
62
- expect(calledLog).toEqual([0]);
53
+function runActTests(label, render, unmount) {
54
+ describe(label, () => {
55
+ beforeEach(() => {
56
+ jest.resetModules();
57
+ React = require('react');
58
+ ReactDOM = require('react-dom');
59
+ ReactTestUtils = require('react-dom/test-utils');
60
+ SchedulerTracing = require('scheduler/tracing');
61
+ Scheduler = require('scheduler');
62
+ act = ReactTestUtils.act;
63
+ container = document.createElement('div');
64
+ document.body.appendChild(container);
65
});
64
-
65
- it('flushes effects on every call', () => {
66
- function App(props) {
67
- let [ctr, setCtr] = React.useState(0);
68
- React.useEffect(() => {
69
- props.callback(ctr);
70
- });
71
- return (
72
- <button id="button" onClick={() => setCtr(x => x + 1)}>
73
- {ctr}
74
- </button>
75
- );
76
- }
77
-
78
- let calledCounter = 0;
79
- act(() => {
80
- ReactDOM.render(
81
- <App
82
- callback={val => {
83
- calledCounter = val;
84
- }}
85
- />,
86
- container,
87
- );
88
- });
89
- const button = document.getElementById('button');
90
- function click() {
91
- button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
92
- }
93
-
94
- act(() => {
95
- click();
96
- click();
97
- click();
98
- });
99
- expect(calledCounter).toBe(3);
100
- act(click);
101
- expect(calledCounter).toBe(4);
102
- act(click);
103
- expect(calledCounter).toBe(5);
104
- expect(button.innerHTML).toBe('5');
66
+ afterEach(() => {
67
+ unmount(container);
68
+ document.body.removeChild(container);
69
});
70
+ describe('sync', () => {
71
+ it('can use act to flush effects', () => {
72
+ function App() {
73
+ React.useEffect(() => {
74
+ Scheduler.yieldValue(100);
75
+ });
76
+ return null;
77
+ }
78
107
- it('should flush effects recursively', () => {
108
- function App() {
109
- let [ctr, setCtr] = React.useState(0);
110
- React.useEffect(() => {
111
- if (ctr < 5) {
112
- setCtr(x => x + 1);
113
- }
79
+ act(() => {
80
+ render(<App />, container);
81
});
115
- return ctr;
116
- }
82
118
- act(() => {
119
- ReactDOM.render(<App />, container);
83
+ expect(Scheduler).toHaveYielded([100]);
84
});
85
122
- expect(container.innerHTML).toBe('5');
123
- });
124
-
125
- it('detects setState being called outside of act(...)', () => {
126
- let setValue = null;
127
- function App() {
128
- let [value, _setValue] = React.useState(0);
129
- setValue = _setValue;
130
- return (
131
- <button id="button" onClick={() => setValue(2)}>
132
- {value}
133
- </button>
134
- );
135
- }
136
- let button;
137
- act(() => {
138
- ReactDOM.render(<App />, container);
139
- button = container.querySelector('#button');
140
- button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
141
- });
142
- expect(button.innerHTML).toBe('2');
143
- expect(() => setValue(1)).toWarnDev([
144
- 'An update to App inside a test was not wrapped in act(...).',
145
- ]);
146
- });
147
- describe('fake timers', () => {
148
- beforeEach(() => {
149
- jest.useFakeTimers();
150
- });
151
- afterEach(() => {
152
- jest.useRealTimers();
153
- });
154
- it('lets a ticker update', () => {
86
+ it('flushes effects on every call', () => {
87
function App() {
156
- let [toggle, setToggle] = React.useState(0);
88
+ let [ctr, setCtr] = React.useState(0);
89
React.useEffect(() => {
158
- let timeout = setTimeout(() => {
159
- setToggle(1);
160
- }, 200);
161
- return () => clearTimeout(timeout);
162
- }, []);
163
- return toggle;
90
+ Scheduler.yieldValue(ctr);
91
+ });
92
+ return (
93
+ <button id="button" onClick={() => setCtr(x => x + 1)}>
94
+ {ctr}
95
+ </button>
96
+ );
97
}
98
99
act(() => {
167
- ReactDOM.render(<App />, container);
100
+ render(<App />, container);
101
});
102
+ expect(Scheduler).toHaveYielded([0]);
103
+ const button = container.querySelector('#button');
104
+ function click() {
105
+ button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
106
+ }
107
+
108
act(() => {
170
- jest.runAllTimers();
109
+ click();
110
+ click();
111
+ click();
112
});
172
-
173
- expect(container.innerHTML).toBe('1');
113
+ // it consolidates the 3 updates, then fires the effect
114
+ expect(Scheduler).toHaveYielded([3]);
115
+ act(click);
116
+ expect(Scheduler).toHaveYielded([4]);
117
+ act(click);
118
+ expect(Scheduler).toHaveYielded([5]);
119
+ expect(button.innerHTML).toBe('5');
120
});
175
- it('can use the async version to catch microtasks', async () => {
121
+
122
+ it("should keep flushing effects until the're done", () => {
123
function App() {
177
- let [toggle, setToggle] = React.useState(0);
124
+ let [ctr, setCtr] = React.useState(0);
125
React.useEffect(() => {
179
- // just like the previous test, except we
180
- // use a promise and schedule the update
181
- // after it resolves
182
- sleep(200).then(() => setToggle(1));
183
- }, []);
184
- return toggle;
126
+ if (ctr < 5) {
127
+ setCtr(x => x + 1);
128
+ }
129
+ });
130
+ return ctr;
131
}
132
133
act(() => {
188
- ReactDOM.render(<App />, container);
189
- });
190
- await act(async () => {
191
- jest.runAllTimers();
134
+ render(<App />, container);
135
});
136
194
- expect(container.innerHTML).toBe('1');
137
+ expect(container.innerHTML).toBe('5');
138
});
196
- it('can handle cascading promises with fake timers', async () => {
197
- // this component triggers an effect, that waits a tick,
198
- // then sets state. repeats this 5 times.
139
+
140
+ it('warns if a setState is called outside of act(...)', () => {
141
+ let setValue = null;
142
function App() {
200
- let [state, setState] = React.useState(0);
201
- async function ticker() {
202
- await null;
203
- setState(x => x + 1);
204
- }
205
- React.useEffect(
206
- () => {
207
- ticker();
208
- },
209
- [Math.min(state, 4)],
210
- );
211
- return state;
143
+ let [value, _setValue] = React.useState(0);
144
+ setValue = _setValue;
145
+ return value;
146
}
213
- const el = document.createElement('div');
214
- await act(async () => {
215
- ReactDOM.render(<App />, el);
147
+
148
+ act(() => {
149
+ render(<App />, container);
150
});
151
218
- // all 5 ticks present and accounted for
219
- expect(el.innerHTML).toBe('5');
152
+ expect(() => setValue(1)).toWarnDev([
153
+ 'An update to App inside a test was not wrapped in act(...).',
154
+ ]);
155
});
221
- it('flushes immediate re-renders with act', () => {
222
- function App() {
223
- let [ctr, setCtr] = React.useState(0);
224
- React.useEffect(() => {
225
- if (ctr === 0) {
226
- setCtr(1);
156
+ describe('fake timers', () => {
157
+ beforeEach(() => {
158
+ jest.useFakeTimers();
159
+ });
160
+ afterEach(() => {
161
+ jest.useRealTimers();
162
+ });
163
+ it('lets a ticker update', () => {
164
+ function App() {
165
+ let [toggle, setToggle] = React.useState(0);
166
+ React.useEffect(() => {
167
+ let timeout = setTimeout(() => {
168
+ setToggle(1);
169
+ }, 200);
170
+ return () => clearTimeout(timeout);
171
+ }, []);
172
+ return toggle;
173
+ }
174
+
175
+ act(() => {
176
+ render(<App />, container);
177
+ });
178
+ act(() => {
179
+ jest.runAllTimers();
180
+ });
181
+
182
+ expect(container.innerHTML).toBe('1');
183
+ });
184
+ it('can use the async version to catch microtasks', async () => {
185
+ function App() {
186
+ let [toggle, setToggle] = React.useState(0);
187
+ React.useEffect(() => {
188
+ // just like the previous test, except we
189
+ // use a promise and schedule the update
190
+ // after it resolves
191
+ sleep(200).then(() => setToggle(1));
192
+ }, []);
193
+ return toggle;
194
+ }
195
+
196
+ act(() => {
197
+ render(<App />, container);
198
+ });
199
+ await act(async () => {
200
+ jest.runAllTimers();
201
+ });
202
+
203
+ expect(container.innerHTML).toBe('1');
204
+ });
205
+ it('can handle cascading promises with fake timers', async () => {
206
+ // this component triggers an effect, that waits a tick,
207
+ // then sets state. repeats this 5 times.
208
+ function App() {
209
+ let [state, setState] = React.useState(0);
210
+ async function ticker() {
211
+ await null;
212
+ setState(x => x + 1);
213
}
228
- const timeout = setTimeout(() => setCtr(2), 1000);
229
- return () => clearTimeout(timeout);
214
+ React.useEffect(
215
+ () => {
216
+ ticker();
217
+ },
218
+ [Math.min(state, 4)],
219
+ );
220
+ return state;
221
+ }
222
+
223
+ await act(async () => {
224
+ render(<App />, container);
225
});
231
- return ctr;
232
- }
226
234
- act(() => {
235
- ReactDOM.render(<App />, container);
236
- // Since the effects won't be flushed yet, this does not advance the timer
237
- jest.runAllTimers();
227
+ // all 5 ticks present and accounted for
228
+ expect(container.innerHTML).toBe('5');
229
});
230
+ it('flushes immediate re-renders with act', () => {
231
+ function App() {
232
+ let [ctr, setCtr] = React.useState(0);
233
+ React.useEffect(() => {
234
+ if (ctr === 0) {
235
+ setCtr(1);
236
+ }
237
+ const timeout = setTimeout(() => setCtr(2), 1000);
238
+ return () => clearTimeout(timeout);
239
+ });
240
+ return ctr;
241
+ }
242
240
- expect(container.innerHTML).toBe('1');
243
+ act(() => {
244
+ render(<App />, container);
245
+ // Since effects haven't been flushed yet, this does not advance the timer
246
+ jest.runAllTimers();
247
+ });
248
242
- act(() => {
243
- jest.runAllTimers();
249
+ expect(container.innerHTML).toBe('1');
250
+
251
+ act(() => {
252
+ jest.runAllTimers();
253
+ });
254
+
255
+ expect(container.innerHTML).toBe('2');
256
});
257
+ });
258
246
- expect(container.innerHTML).toBe('2');
259
+ it('warns if you return a value inside act', () => {
260
+ expect(() => act(() => null)).toWarnDev(
261
+ [
262
+ 'The callback passed to act(...) function must return undefined, or a Promise.',
263
+ ],
264
+ {withoutStack: true},
265
+ );
266
+ expect(() => act(() => 123)).toWarnDev(
267
+ [
268
+ 'The callback passed to act(...) function must return undefined, or a Promise.',
269
+ ],
270
+ {withoutStack: true},
271
+ );
272
});
248
- });
273
250
- it('warns if you return a value inside act', () => {
251
- expect(() => act(() => null)).toWarnDev(
252
- [
253
- 'The callback passed to act(...) function must return undefined, or a Promise.',
254
- ],
255
- {withoutStack: true},
256
- );
257
- expect(() => act(() => 123)).toWarnDev(
258
- [
259
- 'The callback passed to act(...) function must return undefined, or a Promise.',
260
- ],
261
- {withoutStack: true},
262
- );
274
+ it('warns if you try to await a sync .act call', () => {
275
+ expect(() => act(() => {}).then(() => {})).toWarnDev(
276
+ [
277
+ 'Do not await the result of calling act(...) with sync logic, it is not a Promise.',
278
+ ],
279
+ {withoutStack: true},
280
+ );
281
+ });
282
});
283
+ describe('asynchronous tests', () => {
284
+ it('can handle timers', async () => {
285
+ function App() {
286
+ let [ctr, setCtr] = React.useState(0);
287
+ function doSomething() {
288
+ setTimeout(() => {
289
+ setCtr(1);
290
+ }, 50);
291
+ }
292
265
- it('warns if you try to await an .act call', () => {
266
- expect(() => act(() => {}).then(() => {})).toWarnDev(
267
- [
268
- 'Do not await the result of calling act(...) with sync logic, it is not a Promise.',
269
- ],
270
- {withoutStack: true},
271
- );
272
- });
273
- });
274
- describe('asynchronous tests', () => {
275
- it('can handle timers', async () => {
276
- function App() {
277
- let [ctr, setCtr] = React.useState(0);
278
- function doSomething() {
279
- setTimeout(() => {
280
- setCtr(1);
281
- }, 50);
293
+ React.useEffect(() => {
294
+ doSomething();
295
+ }, []);
296
+ return ctr;
297
}
283
-
284
- React.useEffect(() => {
285
- doSomething();
286
- }, []);
287
- return ctr;
288
- }
289
- const el = document.createElement('div');
290
- await act(async () => {
298
act(() => {
292
- ReactDOM.render(<App />, el);
299
+ render(<App />, container);
300
});
294
-
295
- await sleep(100);
296
- expect(el.innerHTML).toBe('1');
301
+ await act(async () => {
302
+ await sleep(100);
303
+ });
304
+ expect(container.innerHTML).toBe('1');
305
});
298
- });
306
300
- it('can handle async/await', async () => {
301
- function App() {
302
- let [ctr, setCtr] = React.useState(0);
303
- async function someAsyncFunction() {
304
- // queue a bunch of promises to be sure they all flush
305
- await null;
306
- await null;
307
- await null;
308
- setCtr(1);
307
+ it('can handle async/await', async () => {
308
+ function App() {
309
+ let [ctr, setCtr] = React.useState(0);
310
+ async function someAsyncFunction() {
311
+ // queue a bunch of promises to be sure they all flush
312
+ await null;
313
+ await null;
314
+ await null;
315
+ setCtr(1);
316
+ }
317
+ React.useEffect(() => {
318
+ someAsyncFunction();
319
+ }, []);
320
+ return ctr;
321
}
310
- React.useEffect(() => {
311
- someAsyncFunction();
312
- }, []);
313
- return ctr;
314
- }
315
- const el = document.createElement('div');
322
317
- await act(async () => {
318
- act(() => {
319
- ReactDOM.render(<App />, el);
323
+ await act(async () => {
324
+ act(() => {
325
+ render(<App />, container);
326
+ });
327
+ // pending promises will close before this ends
328
});
321
- // pending promises will close before this ends
329
+ expect(container.innerHTML).toEqual('1');
330
});
323
- expect(el.innerHTML).toEqual('1');
324
- });
331
326
- it('warns if you do not await an act call', async () => {
327
- spyOnDevAndProd(console, 'error');
328
- act(async () => {});
329
- // it's annoying that we have to wait a tick before this warning comes in
330
- await sleep(0);
331
- if (__DEV__) {
332
- expect(console.error.calls.count()).toEqual(1);
333
- expect(console.error.calls.argsFor(0)[0]).toMatch(
334
- 'You called act(async () => ...) without await.',
335
- );
336
- }
337
- });
332
+ it('warns if you do not await an act call', async () => {
333
+ spyOnDevAndProd(console, 'error');
334
+ act(async () => {});
335
+ // it's annoying that we have to wait a tick before this warning comes in
336
+ await sleep(0);
337
+ if (__DEV__) {
338
+ expect(console.error.calls.count()).toEqual(1);
339
+ expect(console.error.calls.argsFor(0)[0]).toMatch(
340
+ 'You called act(async () => ...) without await.',
341
+ );
342
+ }
343
+ });
344
+
345
+ it('warns if you try to interleave multiple act calls', async () => {
346
+ spyOnDevAndProd(console, 'error');
347
+ // let's try to cheat and spin off a 'thread' with an act call
348
+ (async () => {
349
+ await act(async () => {
350
+ await sleep(50);
351
+ });
352
+ })();
353
339
- it('warns if you try to interleave multiple act calls', async () => {
340
- spyOnDevAndProd(console, 'error');
341
- // let's try to cheat and spin off a 'thread' with an act call
342
- (async () => {
354
await act(async () => {
344
- await sleep(50);
355
+ await sleep(100);
356
});
346
- })();
357
348
- await act(async () => {
349
- await sleep(100);
358
+ await sleep(150);
359
+ if (__DEV__) {
360
+ expect(console.error).toHaveBeenCalledTimes(1);
361
+ }
362
});
363
352
- await sleep(150);
353
- if (__DEV__) {
354
- expect(console.error).toHaveBeenCalledTimes(1);
355
- }
356
- });
357
-
358
- it('commits and effects are guaranteed to be flushed', async () => {
359
- function App(props) {
360
- let [state, setState] = React.useState(0);
361
- async function something() {
362
- await null;
363
- setState(1);
364
+ it('commits and effects are guaranteed to be flushed', async () => {
365
+ function App() {
366
+ let [state, setState] = React.useState(0);
367
+ async function something() {
368
+ await null;
369
+ setState(1);
370
+ }
371
+ React.useEffect(() => {
372
+ something();
373
+ }, []);
374
+ React.useEffect(() => {
375
+ Scheduler.yieldValue(state);
376
+ });
377
+ return state;
378
}
365
- React.useEffect(() => {
366
- something();
367
- }, []);
368
- React.useEffect(() => {
369
- props.callback();
370
- });
371
- return state;
372
- }
373
- let ctr = 0;
374
- const div = document.createElement('div');
379
376
- await act(async () => {
377
- act(() => {
378
- ReactDOM.render(<App callback={() => ctr++} />, div);
380
+ await act(async () => {
381
+ act(() => {
382
+ render(<App />, container);
383
+ });
384
+ expect(container.innerHTML).toBe('0');
385
+ expect(Scheduler).toHaveYielded([0]);
386
});
380
- expect(div.innerHTML).toBe('0');
381
- expect(ctr).toBe(1);
387
+ // this may seem odd, but it matches user behaviour -
388
+ // a flash of "0" followed by "1"
389
+
390
+ expect(container.innerHTML).toBe('1');
391
+ expect(Scheduler).toHaveYielded([1]);
392
});
383
- // this may seem odd, but it matches user behaviour -
384
- // a flash of "0" followed by "1"
393
386
- expect(div.innerHTML).toBe('1');
387
- expect(ctr).toBe(2);
388
- });
394
+ it('propagates errors', async () => {
395
+ let err;
396
+ try {
397
+ await act(async () => {
398
+ await sleep(100);
399
+ throw new Error('some error');
400
+ });
401
+ } catch (_err) {
402
+ err = _err;
403
+ } finally {
404
+ expect(err instanceof Error).toBe(true);
405
+ expect(err.message).toBe('some error');
406
+ }
407
+ });
408
+ it('can handle cascading promises', async () => {
409
+ // this component triggers an effect, that waits a tick,
410
+ // then sets state. repeats this 5 times.
411
+ function App() {
412
+ let [state, setState] = React.useState(0);
413
+ async function ticker() {
414
+ await null;
415
+ setState(x => x + 1);
416
+ }
417
+ React.useEffect(
418
+ () => {
419
+ Scheduler.yieldValue(state);
420
+ ticker();
421
+ },
422
+ [Math.min(state, 4)],
423
+ );
424
+ return state;
425
+ }
426
390
- it('propagates errors', async () => {
391
- let err;
392
- try {
427
await act(async () => {
394
- throw new Error('some error');
428
+ render(<App />, container);
429
});
396
- } catch (_err) {
397
- err = _err;
398
- } finally {
399
- expect(err instanceof Error).toBe(true);
400
- expect(err.message).toBe('some error');
401
- }
402
- });
403
- it('can handle cascading promises', async () => {
404
- // this component triggers an effect, that waits a tick,
405
- // then sets state. repeats this 5 times.
406
- function App() {
407
- let [state, setState] = React.useState(0);
408
- async function ticker() {
409
- await null;
410
- setState(x => x + 1);
411
- }
412
- React.useEffect(
413
- () => {
414
- ticker();
415
- },
416
- [Math.min(state, 4)],
417
- );
418
- return state;
419
- }
420
- const el = document.createElement('div');
421
- await act(async () => {
422
- ReactDOM.render(<App />, el);
430
+ // all 5 ticks present and accounted for
431
+ expect(Scheduler).toHaveYielded([0, 1, 2, 3, 4]);
432
+ expect(container.innerHTML).toBe('5');
433
});
424
- // all 5 ticks present and accounted for
425
- expect(el.innerHTML).toBe('5');
434
});
427
- });
435
429
- describe('interaction tracing', () => {
430
- if (__DEV__) {
431
- it('should correctly trace interactions for sync roots', () => {
432
- let expectedInteraction;
436
+ describe('interaction tracing', () => {
437
+ if (__DEV__) {
438
+ it('should correctly trace interactions for sync roots', () => {
439
+ let expectedInteraction;
440
+
441
+ const Component = jest.fn(() => {
442
+ expect(expectedInteraction).toBeDefined();
443
434
- const Component = jest.fn(() => {
435
- expect(expectedInteraction).toBeDefined();
444
+ const interactions = SchedulerTracing.unstable_getCurrent();
445
+ expect(interactions.size).toBe(1);
446
+ expect(interactions).toContain(expectedInteraction);
447
437
- const interactions = SchedulerTracing.unstable_getCurrent();
438
- expect(interactions.size).toBe(1);
439
- expect(interactions).toContain(expectedInteraction);
448
+ return null;
449
+ });
450
441
- return null;
442
- });
451
+ act(() => {
452
+ SchedulerTracing.unstable_trace(
453
+ 'mount traced inside act',
454
+ performance.now(),
455
+ () => {
456
+ const interactions = SchedulerTracing.unstable_getCurrent();
457
+ expect(interactions.size).toBe(1);
458
+ expectedInteraction = Array.from(interactions)[0];
459
+
460
+ render(<Component />, container);
461
+ },
462
+ );
463
+ });
464
+
465
+ act(() => {
466
+ SchedulerTracing.unstable_trace(
467
+ 'update traced inside act',
468
+ performance.now(),
469
+ () => {
470
+ const interactions = SchedulerTracing.unstable_getCurrent();
471
+ expect(interactions.size).toBe(1);
472
+ expectedInteraction = Array.from(interactions)[0];
473
+
474
+ render(<Component />, container);
475
+ },
476
+ );
477
+ });
478
+
479
+ const secondContainer = document.createElement('div');
480
444
- act(() => {
481
SchedulerTracing.unstable_trace(
446
- 'mount traced inside act',
482
+ 'mount traced outside act',
483
performance.now(),
484
() => {
449
- const interactions = SchedulerTracing.unstable_getCurrent();
450
- expect(interactions.size).toBe(1);
451
- expectedInteraction = Array.from(interactions)[0];
485
+ act(() => {
486
+ const interactions = SchedulerTracing.unstable_getCurrent();
487
+ expect(interactions.size).toBe(1);
488
+ expectedInteraction = Array.from(interactions)[0];
489
453
- ReactDOM.render(<Component />, container);
490
+ render(<Component />, secondContainer);
491
+ });
492
},
493
);
456
- });
494
458
- act(() => {
495
SchedulerTracing.unstable_trace(
460
- 'update traced inside act',
496
+ 'update traced outside act',
497
performance.now(),
498
() => {
463
- const interactions = SchedulerTracing.unstable_getCurrent();
464
- expect(interactions.size).toBe(1);
465
- expectedInteraction = Array.from(interactions)[0];
499
+ act(() => {
500
+ const interactions = SchedulerTracing.unstable_getCurrent();
501
+ expect(interactions.size).toBe(1);
502
+ expectedInteraction = Array.from(interactions)[0];
503
467
- ReactDOM.render(<Component />, container);
504
+ render(<Component />, secondContainer);
505
+ });
506
},
507
);
470
- });
471
-
472
- const secondContainer = document.createElement('div');
473
-
474
- SchedulerTracing.unstable_trace(
475
- 'mount traced outside act',
476
- performance.now(),
477
- () => {
478
- act(() => {
479
- const interactions = SchedulerTracing.unstable_getCurrent();
480
- expect(interactions.size).toBe(1);
481
- expectedInteraction = Array.from(interactions)[0];
508
483
- ReactDOM.render(<Component />, secondContainer);
484
- });
485
- },
486
- );
487
-
488
- SchedulerTracing.unstable_trace(
489
- 'update traced outside act',
490
- performance.now(),
491
- () => {
492
- act(() => {
493
- const interactions = SchedulerTracing.unstable_getCurrent();
494
- expect(interactions.size).toBe(1);
495
- expectedInteraction = Array.from(interactions)[0];
496
-
497
- ReactDOM.render(<Component />, secondContainer);
498
- });
499
- },
500
- );
501
-
502
- expect(Component).toHaveBeenCalledTimes(4);
503
- });
504
- }
509
+ expect(Component).toHaveBeenCalledTimes(4);
510
+ unmount(secondContainer);
511
+ });
512
+ }
513
+ });
514
});
506
-});
515
+}