Codemod it.experimental to gate pragma (#18582)
* Codemod it.experimental to gate pragma Find-and-replace followed by Prettier * Delete it.experimental Removes the API from our test setup script
Andrew Clark committed
Apr 13, 2020 at 10:28 UTC
65237a237e15af3b3c983d46b401c6af988c5f74
20 files changed
+1194
-1246
packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
+230
-236
@@ -153,7 +153,8 @@ describe('ReactDOMFiberAsync', () => {
153
Scheduler = require('scheduler');
154
});
155
156
- it.experimental('does not perform deferred updates synchronously', () => {
156
+ // @gate experimental
157
+ it('does not perform deferred updates synchronously', () => {
158
const inputRef = React.createRef();
159
const asyncValueRef = React.createRef();
160
const syncValueRef = React.createRef();
@@ -208,7 +209,8 @@ describe('ReactDOMFiberAsync', () => {
209
expect(syncValueRef.current.textContent).toBe('hello');
210
});
211
211
- it.experimental('top-level updates are concurrent', () => {
212
+ // @gate experimental
213
+ it('top-level updates are concurrent', () => {
214
const root = ReactDOM.createRoot(container);
215
root.render(<div>Hi</div>);
216
expect(container.textContent).toEqual('');
@@ -221,7 +223,8 @@ describe('ReactDOMFiberAsync', () => {
223
expect(container.textContent).toEqual('Bye');
224
});
225
224
- it.experimental('deep updates (setState) are concurrent', () => {
226
+ // @gate experimental
227
+ it('deep updates (setState) are concurrent', () => {
228
let instance;
229
class Component extends React.Component {
230
state = {step: 0};
@@ -243,7 +246,8 @@ describe('ReactDOMFiberAsync', () => {
246
expect(container.textContent).toEqual('1');
247
});
248
246
- it.experimental('flushSync flushes updates before end of the tick', () => {
249
+ // @gate experimental
250
+ it('flushSync flushes updates before end of the tick', () => {
251
const ops = [];
252
let instance;
253
@@ -291,82 +295,79 @@ describe('ReactDOMFiberAsync', () => {
295
expect(ops).toEqual(['BC', 'ABCD']);
296
});
297
294
- it.experimental(
295
- 'flushControlled flushes updates before yielding to browser',
296
- () => {
297
- let inst;
298
- class Counter extends React.Component {
299
- state = {counter: 0};
300
- increment = () =>
301
- this.setState(state => ({counter: state.counter + 1}));
302
- render() {
303
- inst = this;
304
- return this.state.counter;
305
- }
298
+ // @gate experimental
299
+ it('flushControlled flushes updates before yielding to browser', () => {
300
+ let inst;
301
+ class Counter extends React.Component {
302
+ state = {counter: 0};
303
+ increment = () =>
304
+ this.setState(state => ({counter: state.counter + 1}));
305
+ render() {
306
+ inst = this;
307
+ return this.state.counter;
308
}
307
- const root = ReactDOM.createRoot(container);
308
- root.render(<Counter />);
309
- Scheduler.unstable_flushAll();
310
- expect(container.textContent).toEqual('0');
309
+ }
310
+ const root = ReactDOM.createRoot(container);
311
+ root.render(<Counter />);
312
+ Scheduler.unstable_flushAll();
313
+ expect(container.textContent).toEqual('0');
314
312
- // Test that a normal update is async
313
- inst.increment();
314
- expect(container.textContent).toEqual('0');
315
- Scheduler.unstable_flushAll();
316
- expect(container.textContent).toEqual('1');
315
+ // Test that a normal update is async
316
+ inst.increment();
317
+ expect(container.textContent).toEqual('0');
318
+ Scheduler.unstable_flushAll();
319
+ expect(container.textContent).toEqual('1');
320
318
- const ops = [];
321
+ const ops = [];
322
+ ReactDOM.unstable_flushControlled(() => {
323
+ inst.increment();
324
ReactDOM.unstable_flushControlled(() => {
325
inst.increment();
321
- ReactDOM.unstable_flushControlled(() => {
322
- inst.increment();
323
- ops.push('end of inner flush: ' + container.textContent);
324
- });
325
- ops.push('end of outer flush: ' + container.textContent);
326
+ ops.push('end of inner flush: ' + container.textContent);
327
});
327
- ops.push('after outer flush: ' + container.textContent);
328
- expect(ops).toEqual([
329
- 'end of inner flush: 1',
330
- 'end of outer flush: 1',
331
- 'after outer flush: 3',
332
- ]);
333
- },
334
- );
328
+ ops.push('end of outer flush: ' + container.textContent);
329
+ });
330
+ ops.push('after outer flush: ' + container.textContent);
331
+ expect(ops).toEqual([
332
+ 'end of inner flush: 1',
333
+ 'end of outer flush: 1',
334
+ 'after outer flush: 3',
335
+ ]);
336
+ });
337
336
- it.experimental(
337
- 'flushControlled does not flush until end of outermost batchedUpdates',
338
- () => {
339
- let inst;
340
- class Counter extends React.Component {
341
- state = {counter: 0};
342
- increment = () =>
343
- this.setState(state => ({counter: state.counter + 1}));
344
- render() {
345
- inst = this;
346
- return this.state.counter;
347
- }
338
+ // @gate experimental
339
+ it('flushControlled does not flush until end of outermost batchedUpdates', () => {
340
+ let inst;
341
+ class Counter extends React.Component {
342
+ state = {counter: 0};
343
+ increment = () =>
344
+ this.setState(state => ({counter: state.counter + 1}));
345
+ render() {
346
+ inst = this;
347
+ return this.state.counter;
348
}
349
- ReactDOM.render(<Counter />, container);
349
+ }
350
+ ReactDOM.render(<Counter />, container);
351
351
- const ops = [];
352
- ReactDOM.unstable_batchedUpdates(() => {
352
+ const ops = [];
353
+ ReactDOM.unstable_batchedUpdates(() => {
354
+ inst.increment();
355
+ ReactDOM.unstable_flushControlled(() => {
356
inst.increment();
354
- ReactDOM.unstable_flushControlled(() => {
355
- inst.increment();
356
- ops.push('end of flushControlled fn: ' + container.textContent);
357
- });
358
- ops.push('end of batchedUpdates fn: ' + container.textContent);
357
+ ops.push('end of flushControlled fn: ' + container.textContent);
358
});
360
- ops.push('after batchedUpdates: ' + container.textContent);
361
- expect(ops).toEqual([
362
- 'end of flushControlled fn: 0',
363
- 'end of batchedUpdates fn: 0',
364
- 'after batchedUpdates: 2',
365
- ]);
366
- },
367
- );
359
+ ops.push('end of batchedUpdates fn: ' + container.textContent);
360
+ });
361
+ ops.push('after batchedUpdates: ' + container.textContent);
362
+ expect(ops).toEqual([
363
+ 'end of flushControlled fn: 0',
364
+ 'end of batchedUpdates fn: 0',
365
+ 'after batchedUpdates: 2',
366
+ ]);
367
+ });
368
369
- it.experimental('flushControlled returns nothing', () => {
369
+ // @gate experimental
370
+ it('flushControlled returns nothing', () => {
371
// In the future, we may want to return a thenable "work" object.
372
let inst;
373
class Counter extends React.Component {
@@ -389,201 +390,193 @@ describe('ReactDOMFiberAsync', () => {
390
expect(returnValue).toBe(undefined);
391
});
392
392
- it.experimental(
393
- 'ignores discrete events on a pending removed element',
394
- () => {
395
- const disableButtonRef = React.createRef();
396
- const submitButtonRef = React.createRef();
397
-
398
- let formSubmitted = false;
399
-
400
- class Form extends React.Component {
401
- state = {active: true};
402
- disableForm = () => {
403
- this.setState({active: false});
404
- };
405
- submitForm = () => {
406
- formSubmitted = true; // This should not get invoked
407
- };
408
- render() {
409
- return (
410
- <div>
411
- <button onClick={this.disableForm} ref={disableButtonRef}>
412
- Disable
393
+ // @gate experimental
394
+ it('ignores discrete events on a pending removed element', () => {
395
+ const disableButtonRef = React.createRef();
396
+ const submitButtonRef = React.createRef();
397
+
398
+ let formSubmitted = false;
399
+
400
+ class Form extends React.Component {
401
+ state = {active: true};
402
+ disableForm = () => {
403
+ this.setState({active: false});
404
+ };
405
+ submitForm = () => {
406
+ formSubmitted = true; // This should not get invoked
407
+ };
408
+ render() {
409
+ return (
410
+ <div>
411
+ <button onClick={this.disableForm} ref={disableButtonRef}>
412
+ Disable
413
+ </button>
414
+ {this.state.active ? (
415
+ <button onClick={this.submitForm} ref={submitButtonRef}>
416
+ Submit
417
</button>
414
- {this.state.active ? (
415
- <button onClick={this.submitForm} ref={submitButtonRef}>
416
- Submit
417
- </button>
418
- ) : null}
419
- </div>
420
- );
421
- }
418
+ ) : null}
419
+ </div>
420
+ );
421
}
422
+ }
423
424
- const root = ReactDOM.createRoot(container);
425
- root.render(<Form />);
426
- // Flush
427
- Scheduler.unstable_flushAll();
424
+ const root = ReactDOM.createRoot(container);
425
+ root.render(<Form />);
426
+ // Flush
427
+ Scheduler.unstable_flushAll();
428
429
- const disableButton = disableButtonRef.current;
430
- expect(disableButton.tagName).toBe('BUTTON');
429
+ const disableButton = disableButtonRef.current;
430
+ expect(disableButton.tagName).toBe('BUTTON');
431
432
- // Dispatch a click event on the Disable-button.
433
- const firstEvent = document.createEvent('Event');
434
- firstEvent.initEvent('click', true, true);
435
- disableButton.dispatchEvent(firstEvent);
432
+ // Dispatch a click event on the Disable-button.
433
+ const firstEvent = document.createEvent('Event');
434
+ firstEvent.initEvent('click', true, true);
435
+ disableButton.dispatchEvent(firstEvent);
436
437
- // There should now be a pending update to disable the form.
437
+ // There should now be a pending update to disable the form.
438
439
- // This should not have flushed yet since it's in concurrent mode.
440
- const submitButton = submitButtonRef.current;
441
- expect(submitButton.tagName).toBe('BUTTON');
439
+ // This should not have flushed yet since it's in concurrent mode.
440
+ const submitButton = submitButtonRef.current;
441
+ expect(submitButton.tagName).toBe('BUTTON');
442
443
- // In the meantime, we can dispatch a new client event on the submit button.
444
- const secondEvent = document.createEvent('Event');
445
- secondEvent.initEvent('click', true, true);
446
- // This should force the pending update to flush which disables the submit button before the event is invoked.
447
- submitButton.dispatchEvent(secondEvent);
443
+ // In the meantime, we can dispatch a new client event on the submit button.
444
+ const secondEvent = document.createEvent('Event');
445
+ secondEvent.initEvent('click', true, true);
446
+ // This should force the pending update to flush which disables the submit button before the event is invoked.
447
+ submitButton.dispatchEvent(secondEvent);
448
449
- // Therefore the form should never have been submitted.
450
- expect(formSubmitted).toBe(false);
449
+ // Therefore the form should never have been submitted.
450
+ expect(formSubmitted).toBe(false);
451
452
- expect(submitButtonRef.current).toBe(null);
453
- },
454
- );
452
+ expect(submitButtonRef.current).toBe(null);
453
+ });
454
456
- it.experimental(
457
- 'ignores discrete events on a pending removed event listener',
458
- () => {
459
- const disableButtonRef = React.createRef();
460
- const submitButtonRef = React.createRef();
461
-
462
- let formSubmitted = false;
463
-
464
- class Form extends React.Component {
465
- state = {active: true};
466
- disableForm = () => {
467
- this.setState({active: false});
468
- };
469
- submitForm = () => {
470
- formSubmitted = true; // This should not get invoked
471
- };
472
- disabledSubmitForm = () => {
473
- // The form is disabled.
474
- };
475
- render() {
476
- return (
477
- <div>
478
- <button onClick={this.disableForm} ref={disableButtonRef}>
479
- Disable
480
- </button>
481
- <button
482
- onClick={
483
- this.state.active
484
- ? this.submitForm
485
- : this.disabledSubmitForm
486
- }
487
- ref={submitButtonRef}>
488
- Submit
489
- </button>
490
- </div>
491
- );
492
- }
455
+ // @gate experimental
456
+ it('ignores discrete events on a pending removed event listener', () => {
457
+ const disableButtonRef = React.createRef();
458
+ const submitButtonRef = React.createRef();
459
+
460
+ let formSubmitted = false;
461
+
462
+ class Form extends React.Component {
463
+ state = {active: true};
464
+ disableForm = () => {
465
+ this.setState({active: false});
466
+ };
467
+ submitForm = () => {
468
+ formSubmitted = true; // This should not get invoked
469
+ };
470
+ disabledSubmitForm = () => {
471
+ // The form is disabled.
472
+ };
473
+ render() {
474
+ return (
475
+ <div>
476
+ <button onClick={this.disableForm} ref={disableButtonRef}>
477
+ Disable
478
+ </button>
479
+ <button
480
+ onClick={
481
+ this.state.active ? this.submitForm : this.disabledSubmitForm
482
+ }
483
+ ref={submitButtonRef}>
484
+ Submit
485
+ </button>
486
+ </div>
487
+ );
488
}
489
+ }
490
495
- const root = ReactDOM.createRoot(container);
496
- root.render(<Form />);
497
- // Flush
498
- Scheduler.unstable_flushAll();
491
+ const root = ReactDOM.createRoot(container);
492
+ root.render(<Form />);
493
+ // Flush
494
+ Scheduler.unstable_flushAll();
495
500
- const disableButton = disableButtonRef.current;
501
- expect(disableButton.tagName).toBe('BUTTON');
496
+ const disableButton = disableButtonRef.current;
497
+ expect(disableButton.tagName).toBe('BUTTON');
498
503
- // Dispatch a click event on the Disable-button.
504
- const firstEvent = document.createEvent('Event');
505
- firstEvent.initEvent('click', true, true);
506
- disableButton.dispatchEvent(firstEvent);
499
+ // Dispatch a click event on the Disable-button.
500
+ const firstEvent = document.createEvent('Event');
501
+ firstEvent.initEvent('click', true, true);
502
+ disableButton.dispatchEvent(firstEvent);
503
508
- // There should now be a pending update to disable the form.
504
+ // There should now be a pending update to disable the form.
505
510
- // This should not have flushed yet since it's in concurrent mode.
511
- const submitButton = submitButtonRef.current;
512
- expect(submitButton.tagName).toBe('BUTTON');
506
+ // This should not have flushed yet since it's in concurrent mode.
507
+ const submitButton = submitButtonRef.current;
508
+ expect(submitButton.tagName).toBe('BUTTON');
509
514
- // In the meantime, we can dispatch a new client event on the submit button.
515
- const secondEvent = document.createEvent('Event');
516
- secondEvent.initEvent('click', true, true);
517
- // This should force the pending update to flush which disables the submit button before the event is invoked.
518
- submitButton.dispatchEvent(secondEvent);
510
+ // In the meantime, we can dispatch a new client event on the submit button.
511
+ const secondEvent = document.createEvent('Event');
512
+ secondEvent.initEvent('click', true, true);
513
+ // This should force the pending update to flush which disables the submit button before the event is invoked.
514
+ submitButton.dispatchEvent(secondEvent);
515
520
- // Therefore the form should never have been submitted.
521
- expect(formSubmitted).toBe(false);
522
- },
523
- );
516
+ // Therefore the form should never have been submitted.
517
+ expect(formSubmitted).toBe(false);
518
+ });
519
525
- it.experimental(
526
- 'uses the newest discrete events on a pending changed event listener',
527
- () => {
528
- const enableButtonRef = React.createRef();
529
- const submitButtonRef = React.createRef();
530
-
531
- let formSubmitted = false;
532
-
533
- class Form extends React.Component {
534
- state = {active: false};
535
- enableForm = () => {
536
- this.setState({active: true});
537
- };
538
- submitForm = () => {
539
- formSubmitted = true; // This should happen
540
- };
541
- render() {
542
- return (
543
- <div>
544
- <button onClick={this.enableForm} ref={enableButtonRef}>
545
- Enable
546
- </button>
547
- <button
548
- onClick={this.state.active ? this.submitForm : null}
549
- ref={submitButtonRef}>
550
- Submit
551
- </button>{' '}
552
- : null}
553
- </div>
554
- );
555
- }
520
+ // @gate experimental
521
+ it('uses the newest discrete events on a pending changed event listener', () => {
522
+ const enableButtonRef = React.createRef();
523
+ const submitButtonRef = React.createRef();
524
+
525
+ let formSubmitted = false;
526
+
527
+ class Form extends React.Component {
528
+ state = {active: false};
529
+ enableForm = () => {
530
+ this.setState({active: true});
531
+ };
532
+ submitForm = () => {
533
+ formSubmitted = true; // This should happen
534
+ };
535
+ render() {
536
+ return (
537
+ <div>
538
+ <button onClick={this.enableForm} ref={enableButtonRef}>
539
+ Enable
540
+ </button>
541
+ <button
542
+ onClick={this.state.active ? this.submitForm : null}
543
+ ref={submitButtonRef}>
544
+ Submit
545
+ </button>{' '}
546
+ : null}
547
+ </div>
548
+ );
549
}
550
+ }
551
558
- const root = ReactDOM.createRoot(container);
559
- root.render(<Form />);
560
- // Flush
561
- Scheduler.unstable_flushAll();
552
+ const root = ReactDOM.createRoot(container);
553
+ root.render(<Form />);
554
+ // Flush
555
+ Scheduler.unstable_flushAll();
556
563
- const enableButton = enableButtonRef.current;
564
- expect(enableButton.tagName).toBe('BUTTON');
557
+ const enableButton = enableButtonRef.current;
558
+ expect(enableButton.tagName).toBe('BUTTON');
559
566
- // Dispatch a click event on the Enable-button.
567
- const firstEvent = document.createEvent('Event');
568
- firstEvent.initEvent('click', true, true);
569
- enableButton.dispatchEvent(firstEvent);
560
+ // Dispatch a click event on the Enable-button.
561
+ const firstEvent = document.createEvent('Event');
562
+ firstEvent.initEvent('click', true, true);
563
+ enableButton.dispatchEvent(firstEvent);
564
571
- // There should now be a pending update to enable the form.
565
+ // There should now be a pending update to enable the form.
566
573
- // This should not have flushed yet since it's in concurrent mode.
574
- const submitButton = submitButtonRef.current;
575
- expect(submitButton.tagName).toBe('BUTTON');
567
+ // This should not have flushed yet since it's in concurrent mode.
568
+ const submitButton = submitButtonRef.current;
569
+ expect(submitButton.tagName).toBe('BUTTON');
570
577
- // In the meantime, we can dispatch a new client event on the submit button.
578
- const secondEvent = document.createEvent('Event');
579
- secondEvent.initEvent('click', true, true);
580
- // This should force the pending update to flush which enables the submit button before the event is invoked.
581
- submitButton.dispatchEvent(secondEvent);
571
+ // In the meantime, we can dispatch a new client event on the submit button.
572
+ const secondEvent = document.createEvent('Event');
573
+ secondEvent.initEvent('click', true, true);
574
+ // This should force the pending update to flush which enables the submit button before the event is invoked.
575
+ submitButton.dispatchEvent(secondEvent);
576
583
- // Therefore the form should have been submitted.
584
- expect(formSubmitted).toBe(true);
585
- },
586
- );
577
+ // Therefore the form should have been submitted.
578
+ expect(formSubmitted).toBe(true);
579
+ });
580
});
581
582
it('regression test: does not drop passive effects across roots (#17066)', () => {
@@ -617,7 +610,8 @@ describe('ReactDOMFiberAsync', () => {
610
});
611
612
describe('createBlockingRoot', () => {
620
- it.experimental('updates flush without yielding in the next event', () => {
613
+ // @gate experimental
614
+ it('updates flush without yielding in the next event', () => {
615
const root = ReactDOM.createBlockingRoot(container);
616
617
function Text(props) {
packages/react-dom/src/__tests__/ReactDOMHooks-test.js
+32
-34
@@ -105,41 +105,39 @@ describe('ReactDOMHooks', () => {
105
expect(labelRef.current.innerHTML).toBe('abc');
106
});
107
108
- it.experimental(
109
- 'should not bail out when an update is scheduled from within an event handler in Concurrent Mode',
110
- () => {
111
- const {createRef, useCallback, useState} = React;
112
-
113
- const Example = ({inputRef, labelRef}) => {
114
- const [text, setText] = useState('');
115
- const handleInput = useCallback(event => {
116
- setText(event.target.value);
117
- });
118
-
119
- return (
120
- <>
121
- <input ref={inputRef} onInput={handleInput} />
122
- <label ref={labelRef}>{text}</label>
123
- </>
124
- );
125
- };
126
-
127
- const inputRef = createRef();
128
- const labelRef = createRef();
129
-
130
- const root = ReactDOM.createRoot(container);
131
- root.render(<Example inputRef={inputRef} labelRef={labelRef} />);
132
-
133
- Scheduler.unstable_flushAll();
134
-
135
- inputRef.current.value = 'abc';
136
- inputRef.current.dispatchEvent(
137
- new Event('input', {bubbles: true, cancelable: true}),
108
+ // @gate experimental
109
+ it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', () => {
110
+ const {createRef, useCallback, useState} = React;
111
+
112
+ const Example = ({inputRef, labelRef}) => {
113
+ const [text, setText] = useState('');
114
+ const handleInput = useCallback(event => {
115
+ setText(event.target.value);
116
+ });
117
+
118
+ return (
119
+ <>
120
+ <input ref={inputRef} onInput={handleInput} />
121
+ <label ref={labelRef}>{text}</label>
122
+ </>
123
);
124
+ };
125
140
- Scheduler.unstable_flushAll();
126
+ const inputRef = createRef();
127
+ const labelRef = createRef();
128
+
129
+ const root = ReactDOM.createRoot(container);
130
+ root.render(<Example inputRef={inputRef} labelRef={labelRef} />);
131
+
132
+ Scheduler.unstable_flushAll();
133
+
134
+ inputRef.current.value = 'abc';
135
+ inputRef.current.dispatchEvent(
136
+ new Event('input', {bubbles: true, cancelable: true}),
137
+ );
138
142
- expect(labelRef.current.innerHTML).toBe('abc');
143
- },
144
- );
139
+ Scheduler.unstable_flushAll();
140
+
141
+ expect(labelRef.current.innerHTML).toBe('abc');
142
+ });
143
});
packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
+15
-17
@@ -491,23 +491,21 @@ describe('ReactDOMServerHydration', () => {
491
expect(element.textContent).toBe('Hello world');
492
});
493
494
- it.experimental(
495
- 'does not re-enter hydration after committing the first one',
496
- () => {
497
- const finalHTML = ReactDOMServer.renderToString(<div />);
498
- const container = document.createElement('div');
499
- container.innerHTML = finalHTML;
500
- const root = ReactDOM.createRoot(container, {hydrate: true});
501
- root.render(<div />);
502
- Scheduler.unstable_flushAll();
503
- root.render(null);
504
- Scheduler.unstable_flushAll();
505
- // This should not reenter hydration state and therefore not trigger hydration
506
- // warnings.
507
- root.render(<div />);
508
- Scheduler.unstable_flushAll();
509
- },
510
- );
494
+ // @gate experimental
495
+ it('does not re-enter hydration after committing the first one', () => {
496
+ const finalHTML = ReactDOMServer.renderToString(<div />);
497
+ const container = document.createElement('div');
498
+ container.innerHTML = finalHTML;
499
+ const root = ReactDOM.createRoot(container, {hydrate: true});
500
+ root.render(<div />);
501
+ Scheduler.unstable_flushAll();
502
+ root.render(null);
503
+ Scheduler.unstable_flushAll();
504
+ // This should not reenter hydration state and therefore not trigger hydration
505
+ // warnings.
506
+ root.render(<div />);
507
+ Scheduler.unstable_flushAll();
508
+ });
509
510
it('Suspense + hydration in legacy mode', () => {
511
const element = document.createElement('div');
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+4
-2
@@ -124,7 +124,8 @@ describe('ReactTestUtils.act()', () => {
124
]);
125
});
126
127
- it.experimental('warns in blocking mode', () => {
127
+ // @gate experimental
128
+ it('warns in blocking mode', () => {
129
expect(() => {
130
const root = ReactDOM.createBlockingRoot(document.createElement('div'));
131
root.render(<App />);
@@ -134,7 +135,8 @@ describe('ReactTestUtils.act()', () => {
135
]);
136
});
137
137
- it.experimental('warns in concurrent mode', () => {
138
+ // @gate experimental
139
+ it('warns in concurrent mode', () => {
140
expect(() => {
141
const root = ReactDOM.createRoot(document.createElement('div'));
142
root.render(<App />);
packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.js
+4
-2
@@ -27,7 +27,8 @@ it('does not warn when rendering in legacy mode', () => {
27
}).toErrorDev([]);
28
});
29
30
-it.experimental('should warn when rendering in concurrent mode', () => {
30
+// @gate experimental
31
+it('should warn when rendering in concurrent mode', () => {
32
expect(() => {
33
ReactDOM.createRoot(document.createElement('div')).render(<App />);
34
}).toErrorDev(
@@ -41,7 +42,8 @@ it.experimental('should warn when rendering in concurrent mode', () => {
42
}).toErrorDev([]);
43
});
44
44
-it.experimental('should warn when rendering in blocking mode', () => {
45
+// @gate experimental
46
+it('should warn when rendering in blocking mode', () => {
47
expect(() => {
48
ReactDOM.createBlockingRoot(document.createElement('div')).render(<App />);
49
}).toErrorDev(
packages/react-dom/src/__tests__/ReactUpdates-test.js
+49
-51
@@ -1287,64 +1287,62 @@ describe('ReactUpdates', () => {
1287
expect(ops).toEqual(['Foo', 'Bar', 'Baz']);
1288
});
1289
1290
- it.experimental(
1291
- 'delays sync updates inside hidden subtrees in Concurrent Mode',
1292
- () => {
1293
- const container = document.createElement('div');
1294
-
1295
- function Baz() {
1296
- Scheduler.unstable_yieldValue('Baz');
1297
- return <p>baz</p>;
1298
- }
1299
-
1300
- let setCounter;
1301
- function Bar() {
1302
- const [counter, _setCounter] = React.useState(0);
1303
- setCounter = _setCounter;
1304
- Scheduler.unstable_yieldValue('Bar');
1305
- return <p>bar {counter}</p>;
1306
- }
1290
+ // @gate experimental
1291
+ it('delays sync updates inside hidden subtrees in Concurrent Mode', () => {
1292
+ const container = document.createElement('div');
1293
1308
- function Foo() {
1309
- Scheduler.unstable_yieldValue('Foo');
1310
- React.useEffect(() => {
1311
- Scheduler.unstable_yieldValue('Foo#effect');
1312
- });
1313
- return (
1314
- <div>
1315
- <div hidden={true}>
1316
- <Bar />
1317
- </div>
1318
- <Baz />
1319
- </div>
1320
- );
1321
- }
1294
+ function Baz() {
1295
+ Scheduler.unstable_yieldValue('Baz');
1296
+ return <p>baz</p>;
1297
+ }
1298
1323
- const root = ReactDOM.createRoot(container);
1324
- let hiddenDiv;
1325
- act(() => {
1326
- root.render(<Foo />);
1327
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Baz', 'Foo#effect']);
1328
- hiddenDiv = container.firstChild.firstChild;
1329
- expect(hiddenDiv.hidden).toBe(true);
1330
- expect(hiddenDiv.innerHTML).toBe('');
1331
- // Run offscreen update
1332
- expect(Scheduler).toFlushAndYield(['Bar']);
1333
- expect(hiddenDiv.hidden).toBe(true);
1334
- expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1335
- });
1299
+ let setCounter;
1300
+ function Bar() {
1301
+ const [counter, _setCounter] = React.useState(0);
1302
+ setCounter = _setCounter;
1303
+ Scheduler.unstable_yieldValue('Bar');
1304
+ return <p>bar {counter}</p>;
1305
+ }
1306
1337
- ReactDOM.flushSync(() => {
1338
- setCounter(1);
1307
+ function Foo() {
1308
+ Scheduler.unstable_yieldValue('Foo');
1309
+ React.useEffect(() => {
1310
+ Scheduler.unstable_yieldValue('Foo#effect');
1311
});
1340
- // Should not flush yet
1341
- expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1312
+ return (
1313
+ <div>
1314
+ <div hidden={true}>
1315
+ <Bar />
1316
+ </div>
1317
+ <Baz />
1318
+ </div>
1319
+ );
1320
+ }
1321
1322
+ const root = ReactDOM.createRoot(container);
1323
+ let hiddenDiv;
1324
+ act(() => {
1325
+ root.render(<Foo />);
1326
+ expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Baz', 'Foo#effect']);
1327
+ hiddenDiv = container.firstChild.firstChild;
1328
+ expect(hiddenDiv.hidden).toBe(true);
1329
+ expect(hiddenDiv.innerHTML).toBe('');
1330
// Run offscreen update
1331
expect(Scheduler).toFlushAndYield(['Bar']);
1345
- expect(hiddenDiv.innerHTML).toBe('<p>bar 1</p>');
1346
- },
1347
- );
1332
+ expect(hiddenDiv.hidden).toBe(true);
1333
+ expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1334
+ });
1335
+
1336
+ ReactDOM.flushSync(() => {
1337
+ setCounter(1);
1338
+ });
1339
+ // Should not flush yet
1340
+ expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1341
+
1342
+ // Run offscreen update
1343
+ expect(Scheduler).toFlushAndYield(['Bar']);
1344
+ expect(hiddenDiv.innerHTML).toBe('<p>bar 1</p>');
1345
+ });
1346
1347
it('can render ridiculously large number of roots without triggering infinite update loop error', () => {
1348
class Foo extends React.Component {
packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js
+158
-166
@@ -432,87 +432,85 @@ describe('DOMModernPluginEventSystem', () => {
432
expect(log).toEqual(['second', 'first']);
433
});
434
435
- it.experimental(
436
- 'does not invoke an event on a parent tree when a subtree is dehydrated',
437
- async () => {
438
- let suspend = false;
439
- let resolve;
440
- const promise = new Promise(
441
- resolvePromise => (resolve = resolvePromise),
442
- );
435
+ // @gate experimental
436
+ it('does not invoke an event on a parent tree when a subtree is dehydrated', async () => {
437
+ let suspend = false;
438
+ let resolve;
439
+ const promise = new Promise(
440
+ resolvePromise => (resolve = resolvePromise),
441
+ );
442
444
- let clicks = 0;
445
- const childSlotRef = React.createRef();
443
+ let clicks = 0;
444
+ const childSlotRef = React.createRef();
445
447
- function Parent() {
448
- return <div onClick={() => clicks++} ref={childSlotRef} />;
449
- }
446
+ function Parent() {
447
+ return <div onClick={() => clicks++} ref={childSlotRef} />;
448
+ }
449
451
- function Child({text}) {
452
- if (suspend) {
453
- throw promise;
454
- } else {
455
- return <a>Click me</a>;
456
- }
450
+ function Child({text}) {
451
+ if (suspend) {
452
+ throw promise;
453
+ } else {
454
+ return <a>Click me</a>;
455
}
456
+ }
457
459
- function App() {
460
- // The root is a Suspense boundary.
461
- return (
462
- <React.Suspense fallback="Loading...">
463
- <Child />
464
- </React.Suspense>
465
- );
466
- }
458
+ function App() {
459
+ // The root is a Suspense boundary.
460
+ return (
461
+ <React.Suspense fallback="Loading...">
462
+ <Child />
463
+ </React.Suspense>
464
+ );
465
+ }
466
468
- suspend = false;
469
- const finalHTML = ReactDOMServer.renderToString(<App />);
467
+ suspend = false;
468
+ const finalHTML = ReactDOMServer.renderToString(<App />);
469
471
- const parentContainer = document.createElement('div');
472
- const childContainer = document.createElement('div');
470
+ const parentContainer = document.createElement('div');
471
+ const childContainer = document.createElement('div');
472
474
- // We need this to be in the document since we'll dispatch events on it.
475
- document.body.appendChild(parentContainer);
473
+ // We need this to be in the document since we'll dispatch events on it.
474
+ document.body.appendChild(parentContainer);
475
477
- // We're going to use a different root as a parent.
478
- // This lets us detect whether an event goes through React's event system.
479
- const parentRoot = ReactDOM.createRoot(parentContainer);
480
- parentRoot.render(<Parent />);
481
- Scheduler.unstable_flushAll();
476
+ // We're going to use a different root as a parent.
477
+ // This lets us detect whether an event goes through React's event system.
478
+ const parentRoot = ReactDOM.createRoot(parentContainer);
479
+ parentRoot.render(<Parent />);
480
+ Scheduler.unstable_flushAll();
481
483
- childSlotRef.current.appendChild(childContainer);
482
+ childSlotRef.current.appendChild(childContainer);
483
485
- childContainer.innerHTML = finalHTML;
484
+ childContainer.innerHTML = finalHTML;
485
487
- const a = childContainer.getElementsByTagName('a')[0];
486
+ const a = childContainer.getElementsByTagName('a')[0];
487
489
- suspend = true;
488
+ suspend = true;
489
491
- // Hydrate asynchronously.
492
- const root = ReactDOM.createRoot(childContainer, {hydrate: true});
493
- root.render(<App />);
494
- jest.runAllTimers();
495
- Scheduler.unstable_flushAll();
490
+ // Hydrate asynchronously.
491
+ const root = ReactDOM.createRoot(childContainer, {hydrate: true});
492
+ root.render(<App />);
493
+ jest.runAllTimers();
494
+ Scheduler.unstable_flushAll();
495
497
- // The Suspense boundary is not yet hydrated.
498
- a.click();
499
- expect(clicks).toBe(0);
496
+ // The Suspense boundary is not yet hydrated.
497
+ a.click();
498
+ expect(clicks).toBe(0);
499
501
- // Resolving the promise so that rendering can complete.
502
- suspend = false;
503
- resolve();
504
- await promise;
500
+ // Resolving the promise so that rendering can complete.
501
+ suspend = false;
502
+ resolve();
503
+ await promise;
504
506
- Scheduler.unstable_flushAll();
507
- jest.runAllTimers();
505
+ Scheduler.unstable_flushAll();
506
+ jest.runAllTimers();
507
509
- // We're now full hydrated.
508
+ // We're now full hydrated.
509
511
- expect(clicks).toBe(1);
510
+ expect(clicks).toBe(1);
511
513
- document.body.removeChild(parentContainer);
514
- },
515
- );
512
+ document.body.removeChild(parentContainer);
513
+ });
514
515
it('handle click events on dynamic portals', () => {
516
const log = [];
@@ -1679,55 +1677,53 @@ describe('DOMModernPluginEventSystem', () => {
1677
expect(targetListerner4).toHaveBeenCalledTimes(1);
1678
});
1679
1682
- it.experimental(
1683
- 'should work with concurrent mode updates',
1684
- async () => {
1685
- const log = [];
1686
- const ref = React.createRef();
1680
+ // @gate experimental
1681
+ it('should work with concurrent mode updates', async () => {
1682
+ const log = [];
1683
+ const ref = React.createRef();
1684
1688
- function Test({counter}) {
1689
- const click = ReactDOM.unstable_useEvent('click');
1685
+ function Test({counter}) {
1686
+ const click = ReactDOM.unstable_useEvent('click');
1687
1691
- React.useLayoutEffect(() => {
1692
- click.setListener(ref.current, () => {
1693
- log.push({counter});
1694
- });
1688
+ React.useLayoutEffect(() => {
1689
+ click.setListener(ref.current, () => {
1690
+ log.push({counter});
1691
});
1692
+ });
1693
1697
- Scheduler.unstable_yieldValue('Test');
1698
- return <button ref={ref}>Press me</button>;
1699
- }
1694
+ Scheduler.unstable_yieldValue('Test');
1695
+ return <button ref={ref}>Press me</button>;
1696
+ }
1697
1701
- const root = ReactDOM.createRoot(container);
1702
- root.render(<Test counter={0} />);
1698
+ const root = ReactDOM.createRoot(container);
1699
+ root.render(<Test counter={0} />);
1700
1704
- expect(Scheduler).toFlushAndYield(['Test']);
1701
+ expect(Scheduler).toFlushAndYield(['Test']);
1702
1706
- // Click the button
1707
- dispatchClickEvent(ref.current);
1708
- expect(log).toEqual([{counter: 0}]);
1703
+ // Click the button
1704
+ dispatchClickEvent(ref.current);
1705
+ expect(log).toEqual([{counter: 0}]);
1706
1710
- // Clear log
1711
- log.length = 0;
1707
+ // Clear log
1708
+ log.length = 0;
1709
1713
- // Increase counter
1714
- root.render(<Test counter={1} />);
1715
- // Yield before committing
1716
- expect(Scheduler).toFlushAndYieldThrough(['Test']);
1710
+ // Increase counter
1711
+ root.render(<Test counter={1} />);
1712
+ // Yield before committing
1713
+ expect(Scheduler).toFlushAndYieldThrough(['Test']);
1714
1718
- // Click the button again
1719
- dispatchClickEvent(ref.current);
1720
- expect(log).toEqual([{counter: 0}]);
1715
+ // Click the button again
1716
+ dispatchClickEvent(ref.current);
1717
+ expect(log).toEqual([{counter: 0}]);
1718
1722
- // Clear log
1723
- log.length = 0;
1719
+ // Clear log
1720
+ log.length = 0;
1721
1725
- // Commit
1726
- expect(Scheduler).toFlushAndYield([]);
1727
- dispatchClickEvent(ref.current);
1728
- expect(log).toEqual([{counter: 1}]);
1729
- },
1730
- );
1722
+ // Commit
1723
+ expect(Scheduler).toFlushAndYield([]);
1724
+ dispatchClickEvent(ref.current);
1725
+ expect(log).toEqual([{counter: 1}]);
1726
+ });
1727
1728
it('should correctly work for a basic "click" listener that upgrades', () => {
1729
const clickEvent = jest.fn();
@@ -2259,86 +2255,82 @@ describe('DOMModernPluginEventSystem', () => {
2255
expect(log).toEqual(['beforeblur', 'afterblur']);
2256
});
2257
2262
- it.experimental(
2263
- 'beforeblur and afterblur are called after a focused element is suspended',
2264
- () => {
2265
- const log = [];
2266
- // We have to persist here because we want to read relatedTarget later.
2267
- const onAfterBlur = jest.fn(e => {
2268
- e.persist();
2269
- log.push(e.type);
2270
- });
2271
- const onBeforeBlur = jest.fn(e => log.push(e.type));
2272
- const innerRef = React.createRef();
2273
- const Suspense = React.Suspense;
2274
- let suspend = false;
2275
- let resolve;
2276
- const promise = new Promise(
2277
- resolvePromise => (resolve = resolvePromise),
2278
- );
2258
+ // @gate experimental
2259
+ it('beforeblur and afterblur are called after a focused element is suspended', () => {
2260
+ const log = [];
2261
+ // We have to persist here because we want to read relatedTarget later.
2262
+ const onAfterBlur = jest.fn(e => {
2263
+ e.persist();
2264
+ log.push(e.type);
2265
+ });
2266
+ const onBeforeBlur = jest.fn(e => log.push(e.type));
2267
+ const innerRef = React.createRef();
2268
+ const Suspense = React.Suspense;
2269
+ let suspend = false;
2270
+ let resolve;
2271
+ const promise = new Promise(
2272
+ resolvePromise => (resolve = resolvePromise),
2273
+ );
2274
2280
- function Child() {
2281
- if (suspend) {
2282
- throw promise;
2283
- } else {
2284
- return <input ref={innerRef} />;
2285
- }
2275
+ function Child() {
2276
+ if (suspend) {
2277
+ throw promise;
2278
+ } else {
2279
+ return <input ref={innerRef} />;
2280
}
2281
+ }
2282
2288
- const Component = () => {
2289
- const ref = React.useRef(null);
2290
- const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur');
2291
- const beforeBlurHandle = ReactDOM.unstable_useEvent(
2292
- 'beforeblur',
2293
- );
2283
+ const Component = () => {
2284
+ const ref = React.useRef(null);
2285
+ const afterBlurHandle = ReactDOM.unstable_useEvent('afterblur');
2286
+ const beforeBlurHandle = ReactDOM.unstable_useEvent('beforeblur');
2287
2295
- React.useEffect(() => {
2296
- afterBlurHandle.setListener(document, onAfterBlur);
2297
- beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2298
- });
2288
+ React.useEffect(() => {
2289
+ afterBlurHandle.setListener(document, onAfterBlur);
2290
+ beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2291
+ });
2292
2300
- return (
2301
- <div ref={ref}>
2302
- <Suspense fallback="Loading...">
2303
- <Child />
2304
- </Suspense>
2305
- </div>
2306
- );
2307
- };
2293
+ return (
2294
+ <div ref={ref}>
2295
+ <Suspense fallback="Loading...">
2296
+ <Child />
2297
+ </Suspense>
2298
+ </div>
2299
+ );
2300
+ };
2301
2309
- const container2 = document.createElement('div');
2310
- document.body.appendChild(container2);
2302
+ const container2 = document.createElement('div');
2303
+ document.body.appendChild(container2);
2304
2312
- const root = ReactDOM.createRoot(container2);
2305
+ const root = ReactDOM.createRoot(container2);
2306
2314
- ReactTestUtils.act(() => {
2315
- root.render(<Component />);
2316
- });
2317
- jest.runAllTimers();
2307
+ ReactTestUtils.act(() => {
2308
+ root.render(<Component />);
2309
+ });
2310
+ jest.runAllTimers();
2311
2319
- const inner = innerRef.current;
2320
- const target = createEventTarget(inner);
2321
- target.focus();
2322
- expect(onBeforeBlur).toHaveBeenCalledTimes(0);
2323
- expect(onAfterBlur).toHaveBeenCalledTimes(0);
2312
+ const inner = innerRef.current;
2313
+ const target = createEventTarget(inner);
2314
+ target.focus();
2315
+ expect(onBeforeBlur).toHaveBeenCalledTimes(0);
2316
+ expect(onAfterBlur).toHaveBeenCalledTimes(0);
2317
2325
- suspend = true;
2326
- ReactTestUtils.act(() => {
2327
- root.render(<Component />);
2328
- });
2329
- jest.runAllTimers();
2318
+ suspend = true;
2319
+ ReactTestUtils.act(() => {
2320
+ root.render(<Component />);
2321
+ });
2322
+ jest.runAllTimers();
2323
2331
- expect(onBeforeBlur).toHaveBeenCalledTimes(1);
2332
- expect(onAfterBlur).toHaveBeenCalledTimes(1);
2333
- expect(onAfterBlur).toHaveBeenCalledWith(
2334
- expect.objectContaining({relatedTarget: inner}),
2335
- );
2336
- resolve();
2337
- expect(log).toEqual(['beforeblur', 'afterblur']);
2324
+ expect(onBeforeBlur).toHaveBeenCalledTimes(1);
2325
+ expect(onAfterBlur).toHaveBeenCalledTimes(1);
2326
+ expect(onAfterBlur).toHaveBeenCalledWith(
2327
+ expect.objectContaining({relatedTarget: inner}),
2328
+ );
2329
+ resolve();
2330
+ expect(log).toEqual(['beforeblur', 'afterblur']);
2331
2339
- document.body.removeChild(container2);
2340
- },
2341
- );
2332
+ document.body.removeChild(container2);
2333
+ });
2334
2335
describe('Compatibility with Scopes API', () => {
2336
beforeEach(() => {
packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js
+2
-1
@@ -789,7 +789,8 @@ describe('DOMEventResponderSystem', () => {
789
buttonRef.current.dispatchEvent(createEvent('foobar'));
790
});
791
792
- it.experimental('should work with concurrent mode updates', async () => {
792
+ // @gate experimental
793
+ it('should work with concurrent mode updates', async () => {
794
const log = [];
795
const TestResponder = createEventResponder({
796
targetEventTypes: ['click'],
packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
+49
-46
@@ -477,7 +477,8 @@ describe('ChangeEventPlugin', () => {
477
Scheduler = require('scheduler');
478
});
479
480
- it.experimental('text input', () => {
480
+ // @gate experimental
481
+ it('text input', () => {
482
const root = ReactDOM.createRoot(container);
483
let input;
484
@@ -519,7 +520,8 @@ describe('ChangeEventPlugin', () => {
520
expect(input.value).toBe('changed [!]');
521
});
522
522
- it.experimental('checkbox input', () => {
523
+ // @gate experimental
524
+ it('checkbox input', () => {
525
const root = ReactDOM.createRoot(container);
526
let input;
527
@@ -574,7 +576,8 @@ describe('ChangeEventPlugin', () => {
576
expect(input.checked).toBe(false);
577
});
578
577
- it.experimental('textarea', () => {
579
+ // @gate experimental
580
+ it('textarea', () => {
581
const root = ReactDOM.createRoot(container);
582
let textarea;
583
@@ -616,7 +619,8 @@ describe('ChangeEventPlugin', () => {
619
expect(textarea.value).toBe('changed [!]');
620
});
621
619
- it.experimental('parent of input', () => {
622
+ // @gate experimental
623
+ it('parent of input', () => {
624
const root = ReactDOM.createRoot(container);
625
let input;
626
@@ -662,7 +666,8 @@ describe('ChangeEventPlugin', () => {
666
expect(input.value).toBe('changed [!]');
667
});
668
665
- it.experimental('is async for non-input events', () => {
669
+ // @gate experimental
670
+ it('is async for non-input events', () => {
671
const root = ReactDOM.createRoot(container);
672
let input;
673
@@ -711,50 +716,48 @@ describe('ChangeEventPlugin', () => {
716
expect(input.value).toBe('');
717
});
718
714
- it.experimental(
715
- 'mouse enter/leave should be user-blocking but not discrete',
716
- async () => {
717
- // This is currently behind a feature flag
718
- jest.resetModules();
719
- React = require('react');
720
- ReactDOM = require('react-dom');
721
- TestUtils = require('react-dom/test-utils');
722
- Scheduler = require('scheduler');
723
-
724
- const {act} = TestUtils;
725
- const {useState} = React;
719
+ // @gate experimental
720
+ it('mouse enter/leave should be user-blocking but not discrete', async () => {
721
+ // This is currently behind a feature flag
722
+ jest.resetModules();
723
+ React = require('react');
724
+ ReactDOM = require('react-dom');
725
+ TestUtils = require('react-dom/test-utils');
726
+ Scheduler = require('scheduler');
727
727
- const root = ReactDOM.createRoot(container);
728
+ const {act} = TestUtils;
729
+ const {useState} = React;
730
729
- const target = React.createRef(null);
730
- function Foo() {
731
- const [isHover, setHover] = useState(false);
732
- return (
733
- <div
734
- ref={target}
735
- onMouseEnter={() => setHover(true)}
736
- onMouseLeave={() => setHover(false)}>
737
- {isHover ? 'hovered' : 'not hovered'}
738
- </div>
739
- );
740
- }
741
-
742
- await act(async () => {
743
- root.render(<Foo />);
744
- });
745
- expect(container.textContent).toEqual('not hovered');
731
+ const root = ReactDOM.createRoot(container);
732
747
- await act(async () => {
748
- const mouseOverEvent = document.createEvent('MouseEvents');
749
- mouseOverEvent.initEvent('mouseover', true, true);
750
- target.current.dispatchEvent(mouseOverEvent);
733
+ const target = React.createRef(null);
734
+ function Foo() {
735
+ const [isHover, setHover] = useState(false);
736
+ return (
737
+ <div
738
+ ref={target}
739
+ onMouseEnter={() => setHover(true)}
740
+ onMouseLeave={() => setHover(false)}>
741
+ {isHover ? 'hovered' : 'not hovered'}
742
+ </div>
743
+ );
744
+ }
745
752
- // 3s should be enough to expire the updates
753
- Scheduler.unstable_advanceTime(3000);
754
- expect(Scheduler).toFlushExpired([]);
755
- expect(container.textContent).toEqual('hovered');
756
- });
757
- },
758
- );
746
+ await act(async () => {
747
+ root.render(<Foo />);
748
+ });
749
+ expect(container.textContent).toEqual('not hovered');
750
+
751
+ await act(async () => {
752
+ const mouseOverEvent = document.createEvent('MouseEvents');
753
+ mouseOverEvent.initEvent('mouseover', true, true);
754
+ target.current.dispatchEvent(mouseOverEvent);
755
+
756
+ // 3s should be enough to expire the updates
757
+ Scheduler.unstable_advanceTime(3000);
758
+ expect(Scheduler).toFlushExpired([]);
759
+ expect(container.textContent).toEqual('hovered');
760
+ });
761
+ });
762
});
763
});
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+118
-121
@@ -236,138 +236,135 @@ describe('SimpleEventPlugin', function() {
236
Scheduler = require('scheduler');
237
});
238
239
- it.experimental(
240
- 'flushes pending interactive work before extracting event handler',
241
- () => {
242
- container = document.createElement('div');
243
- const root = ReactDOM.createRoot(container);
244
- document.body.appendChild(container);
245
-
246
- let button;
247
- class Button extends React.Component {
248
- state = {disabled: false};
249
- onClick = () => {
250
- // Perform some side-effect
251
- Scheduler.unstable_yieldValue('Side-effect');
252
- // Disable the button
253
- this.setState({disabled: true});
254
- };
255
- render() {
256
- Scheduler.unstable_yieldValue(
257
- `render button: ${this.state.disabled ? 'disabled' : 'enabled'}`,
258
- );
259
- return (
260
- <button
261
- ref={el => (button = el)}
262
- // Handler is removed after the first click
263
- onClick={this.state.disabled ? null : this.onClick}
264
- />
265
- );
266
- }
267
- }
239
+ // @gate experimental
240
+ it('flushes pending interactive work before extracting event handler', () => {
241
+ container = document.createElement('div');
242
+ const root = ReactDOM.createRoot(container);
243
+ document.body.appendChild(container);
244
269
- // Initial mount
270
- root.render(<Button />);
271
- // Should not have flushed yet because it's async
272
- expect(Scheduler).toHaveYielded([]);
273
- expect(button).toBe(undefined);
274
- // Flush async work
275
- expect(Scheduler).toFlushAndYield(['render button: enabled']);
276
-
277
- function click() {
278
- button.dispatchEvent(
279
- new MouseEvent('click', {bubbles: true, cancelable: true}),
245
+ let button;
246
+ class Button extends React.Component {
247
+ state = {disabled: false};
248
+ onClick = () => {
249
+ // Perform some side-effect
250
+ Scheduler.unstable_yieldValue('Side-effect');
251
+ // Disable the button
252
+ this.setState({disabled: true});
253
+ };
254
+ render() {
255
+ Scheduler.unstable_yieldValue(
256
+ `render button: ${this.state.disabled ? 'disabled' : 'enabled'}`,
257
+ );
258
+ return (
259
+ <button
260
+ ref={el => (button = el)}
261
+ // Handler is removed after the first click
262
+ onClick={this.state.disabled ? null : this.onClick}
263
+ />
264
);
265
}
266
+ }
267
283
- // Click the button to trigger the side-effect
284
- click();
285
- expect(Scheduler).toHaveYielded([
286
- // The handler fired
287
- 'Side-effect',
288
- // but the component did not re-render yet, because it's async
289
- ]);
290
-
291
- // Click the button again
292
- click();
293
- expect(Scheduler).toHaveYielded([
294
- // Before handling this second click event, the previous interactive
295
- // update is flushed
296
- 'render button: disabled',
297
- // The event handler was removed from the button, so there's no second
298
- // side-effect
299
- ]);
300
-
301
- // The handler should not fire again no matter how many times we
302
- // click the handler.
303
- click();
304
- click();
305
- click();
306
- click();
307
- click();
308
- expect(Scheduler).toFlushAndYield([]);
309
- },
310
- );
268
+ // Initial mount
269
+ root.render(<Button />);
270
+ // Should not have flushed yet because it's async
271
+ expect(Scheduler).toHaveYielded([]);
272
+ expect(button).toBe(undefined);
273
+ // Flush async work
274
+ expect(Scheduler).toFlushAndYield(['render button: enabled']);
275
312
- it.experimental(
313
- 'end result of many interactive updates is deterministic',
314
- () => {
315
- container = document.createElement('div');
316
- const root = ReactDOM.createRoot(container);
317
- document.body.appendChild(container);
276
+ function click() {
277
+ button.dispatchEvent(
278
+ new MouseEvent('click', {bubbles: true, cancelable: true}),
279
+ );
280
+ }
281
319
- let button;
320
- class Button extends React.Component {
321
- state = {count: 0};
322
- render() {
323
- return (
324
- <button
325
- ref={el => (button = el)}
326
- onClick={() =>
327
- // Intentionally not using the updater form here
328
- this.setState({count: this.state.count + 1})
329
- }>
330
- Count: {this.state.count}
331
- </button>
332
- );
333
- }
334
- }
282
+ // Click the button to trigger the side-effect
283
+ click();
284
+ expect(Scheduler).toHaveYielded([
285
+ // The handler fired
286
+ 'Side-effect',
287
+ // but the component did not re-render yet, because it's async
288
+ ]);
289
+
290
+ // Click the button again
291
+ click();
292
+ expect(Scheduler).toHaveYielded([
293
+ // Before handling this second click event, the previous interactive
294
+ // update is flushed
295
+ 'render button: disabled',
296
+ // The event handler was removed from the button, so there's no second
297
+ // side-effect
298
+ ]);
299
+
300
+ // The handler should not fire again no matter how many times we
301
+ // click the handler.
302
+ click();
303
+ click();
304
+ click();
305
+ click();
306
+ click();
307
+ expect(Scheduler).toFlushAndYield([]);
308
+ });
309
+
310
+ // @gate experimental
311
+ it('end result of many interactive updates is deterministic', () => {
312
+ container = document.createElement('div');
313
+ const root = ReactDOM.createRoot(container);
314
+ document.body.appendChild(container);
315
336
- // Initial mount
337
- root.render(<Button />);
338
- // Should not have flushed yet because it's async
339
- expect(button).toBe(undefined);
340
- // Flush async work
341
- Scheduler.unstable_flushAll();
342
- expect(button.textContent).toEqual('Count: 0');
343
-
344
- function click() {
345
- button.dispatchEvent(
346
- new MouseEvent('click', {bubbles: true, cancelable: true}),
316
+ let button;
317
+ class Button extends React.Component {
318
+ state = {count: 0};
319
+ render() {
320
+ return (
321
+ <button
322
+ ref={el => (button = el)}
323
+ onClick={() =>
324
+ // Intentionally not using the updater form here
325
+ this.setState({count: this.state.count + 1})
326
+ }>
327
+ Count: {this.state.count}
328
+ </button>
329
);
330
}
331
+ }
332
350
- // Click the button a single time
351
- click();
352
- // The counter should not have updated yet because it's async
353
- expect(button.textContent).toEqual('Count: 0');
354
-
355
- // Click the button many more times
356
- click();
357
- click();
358
- click();
359
- click();
360
- click();
361
- click();
362
-
363
- // Flush the remaining work
364
- Scheduler.unstable_flushAll();
365
- // The counter should equal the total number of clicks
366
- expect(button.textContent).toEqual('Count: 7');
367
- },
368
- );
333
+ // Initial mount
334
+ root.render(<Button />);
335
+ // Should not have flushed yet because it's async
336
+ expect(button).toBe(undefined);
337
+ // Flush async work
338
+ Scheduler.unstable_flushAll();
339
+ expect(button.textContent).toEqual('Count: 0');
340
+
341
+ function click() {
342
+ button.dispatchEvent(
343
+ new MouseEvent('click', {bubbles: true, cancelable: true}),
344
+ );
345
+ }
346
+
347
+ // Click the button a single time
348
+ click();
349
+ // The counter should not have updated yet because it's async
350
+ expect(button.textContent).toEqual('Count: 0');
351
+
352
+ // Click the button many more times
353
+ click();
354
+ click();
355
+ click();
356
+ click();
357
+ click();
358
+ click();
359
+
360
+ // Flush the remaining work
361
+ Scheduler.unstable_flushAll();
362
+ // The counter should equal the total number of clicks
363
+ expect(button.textContent).toEqual('Count: 7');
364
+ });
365
370
- it.experimental('flushes discrete updates in order', () => {
366
+ // @gate experimental
367
+ it('flushes discrete updates in order', () => {
368
container = document.createElement('div');
369
document.body.appendChild(container);
370
packages/react-flight-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
+2
-1
@@ -93,7 +93,8 @@ describe('ReactFlightDOMRelay', () => {
93
});
94
});
95
96
- it.experimental('can transfer a Block to the client and render there', () => {
96
+ // @gate experimental
97
+ it('can transfer a Block to the client and render there', () => {
98
function load(firstName, lastName) {
99
return {name: firstName + ' ' + lastName};
100
}
packages/react-flight-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+8
-4
@@ -140,7 +140,8 @@ describe('ReactFlightDOM', () => {
140
});
141
});
142
143
- it.experimental('should resolve the root', async () => {
143
+ // @gate experimental
144
+ it('should resolve the root', async () => {
145
const {Suspense} = React;
146
147
// Model
@@ -191,7 +192,8 @@ describe('ReactFlightDOM', () => {
192
);
193
});
194
194
- it.experimental('should not get confused by $', async () => {
195
+ // @gate experimental
196
+ it('should not get confused by $', async () => {
197
const {Suspense} = React;
198
199
// Model
@@ -227,7 +229,8 @@ describe('ReactFlightDOM', () => {
229
expect(container.innerHTML).toBe('<p>$1</p>');
230
});
231
230
- it.experimental('should not get confused by @', async () => {
232
+ // @gate experimental
233
+ it('should not get confused by @', async () => {
234
const {Suspense} = React;
235
236
// Model
@@ -263,7 +266,8 @@ describe('ReactFlightDOM', () => {
266
expect(container.innerHTML).toBe('<p>@div</p>');
267
});
268
266
- it.experimental('should progressively reveal Blocks', async () => {
269
+ // @gate experimental
270
+ it('should progressively reveal Blocks', async () => {
271
const {Suspense} = React;
272
273
class ErrorBoundary extends React.Component {
packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js
+55
-59
@@ -344,69 +344,65 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
344
);
345
});
346
347
- it.experimental(
348
- 'is called after a focused suspended element is hidden',
349
- () => {
350
- const Suspense = React.Suspense;
351
- let suspend = false;
352
- let resolve;
353
- const promise = new Promise(
354
- resolvePromise => (resolve = resolvePromise),
355
- );
356
-
357
- function Child() {
358
- if (suspend) {
359
- throw promise;
360
- } else {
361
- return <input ref={innerRef} />;
362
- }
347
+ // @gate experimental
348
+ it('is called after a focused suspended element is hidden', () => {
349
+ const Suspense = React.Suspense;
350
+ let suspend = false;
351
+ let resolve;
352
+ const promise = new Promise(resolvePromise => (resolve = resolvePromise));
353
+
354
+ function Child() {
355
+ if (suspend) {
356
+ throw promise;
357
+ } else {
358
+ return <input ref={innerRef} />;
359
}
360
+ }
361
365
- const Component = ({show}) => {
366
- const listener = useFocusWithin({
367
- onBeforeBlurWithin,
368
- onBlurWithin,
369
- });
370
-
371
- return (
372
- <div DEPRECATED_flareListeners={listener}>
373
- <Suspense fallback="Loading...">
374
- <Child />
375
- </Suspense>
376
- </div>
377
- );
378
- };
379
-
380
- const container2 = document.createElement('div');
381
- document.body.appendChild(container2);
382
-
383
- const root = ReactDOM.createRoot(container2);
384
- root.render(<Component />);
385
- Scheduler.unstable_flushAll();
386
- jest.runAllTimers();
387
- expect(container2.innerHTML).toBe('<div><input></div>');
388
-
389
- const inner = innerRef.current;
390
- const target = createEventTarget(inner);
391
- target.keydown({key: 'Tab'});
392
- target.focus();
393
- expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
394
- expect(onBlurWithin).toHaveBeenCalledTimes(0);
395
-
396
- suspend = true;
397
- root.render(<Component />);
398
- Scheduler.unstable_flushAll();
399
- jest.runAllTimers();
400
- expect(container2.innerHTML).toBe(
401
- '<div><input style="display: none;">Loading...</div>',
362
+ const Component = ({show}) => {
363
+ const listener = useFocusWithin({
364
+ onBeforeBlurWithin,
365
+ onBlurWithin,
366
+ });
367
+
368
+ return (
369
+ <div DEPRECATED_flareListeners={listener}>
370
+ <Suspense fallback="Loading...">
371
+ <Child />
372
+ </Suspense>
373
+ </div>
374
);
403
- expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
404
- expect(onBlurWithin).toHaveBeenCalledTimes(1);
405
- resolve();
375
+ };
376
+
377
+ const container2 = document.createElement('div');
378
+ document.body.appendChild(container2);
379
+
380
+ const root = ReactDOM.createRoot(container2);
381
+ root.render(<Component />);
382
+ Scheduler.unstable_flushAll();
383
+ jest.runAllTimers();
384
+ expect(container2.innerHTML).toBe('<div><input></div>');
385
407
- document.body.removeChild(container2);
408
- },
409
- );
386
+ const inner = innerRef.current;
387
+ const target = createEventTarget(inner);
388
+ target.keydown({key: 'Tab'});
389
+ target.focus();
390
+ expect(onBeforeBlurWithin).toHaveBeenCalledTimes(0);
391
+ expect(onBlurWithin).toHaveBeenCalledTimes(0);
392
+
393
+ suspend = true;
394
+ root.render(<Component />);
395
+ Scheduler.unstable_flushAll();
396
+ jest.runAllTimers();
397
+ expect(container2.innerHTML).toBe(
398
+ '<div><input style="display: none;">Loading...</div>',
399
+ );
400
+ expect(onBeforeBlurWithin).toHaveBeenCalledTimes(1);
401
+ expect(onBlurWithin).toHaveBeenCalledTimes(1);
402
+ resolve();
403
+
404
+ document.body.removeChild(container2);
405
+ });
406
});
407
408
it('expect displayName to show up for event component', () => {
packages/react-interactions/events/src/dom/__tests__/Input-test.internal.js
+6
-3
@@ -793,7 +793,8 @@ describe('Input event responder', () => {
793
});
794
795
describe('concurrent mode', () => {
796
- it.experimental('text input', () => {
796
+ // @gate experimental
797
+ it('text input', () => {
798
const root = ReactDOM.createRoot(container);
799
let input;
800
@@ -848,7 +849,8 @@ describe('Input event responder', () => {
849
expect(input.value).toBe('changed [!]');
850
});
851
851
- it.experimental('checkbox input', () => {
852
+ // @gate experimental
853
+ it('checkbox input', () => {
854
const root = ReactDOM.createRoot(container);
855
let input;
856
@@ -916,7 +918,8 @@ describe('Input event responder', () => {
918
expect(input.checked).toBe(false);
919
});
920
919
- it.experimental('textarea', () => {
921
+ // @gate experimental
922
+ it('textarea', () => {
923
const root = ReactDOM.createRoot(container);
924
let textarea;
925
packages/react-reconciler/src/__tests__/ReactBlocks-test.js
+97
-98
@@ -49,7 +49,8 @@ describe('ReactBlocks', () => {
49
};
50
});
51
52
- it.experimental('renders a simple component', () => {
52
+ // @gate experimental
53
+ it('renders a simple component', () => {
54
function User(props, data) {
55
return <div>{typeof data}</div>;
56
}
@@ -71,7 +72,8 @@ describe('ReactBlocks', () => {
72
expect(ReactNoop).toMatchRenderedOutput(<div>undefined</div>);
73
});
74
74
- it.experimental('prints the name of the render function in warnings', () => {
75
+ // @gate experimental
76
+ it('prints the name of the render function in warnings', () => {
77
function load(firstName) {
78
return {
79
name: firstName,
@@ -108,7 +110,8 @@ describe('ReactBlocks', () => {
110
);
111
});
112
111
- it.experimental('renders a component with a suspending load', async () => {
113
+ // @gate experimental
114
+ it('renders a component with a suspending load', async () => {
115
function load(id) {
116
return {
117
id: id,
@@ -147,115 +150,111 @@ describe('ReactBlocks', () => {
150
expect(ReactNoop).toMatchRenderedOutput(<span>Name: Sebastian</span>);
151
});
152
150
- it.experimental(
151
- 'does not support a lazy wrapper around a chunk',
152
- async () => {
153
- function load(id) {
154
- return {
155
- id: id,
156
- name: readString('Sebastian'),
157
- };
158
- }
159
-
160
- function Render(props, data) {
161
- return (
162
- <span>
163
- {props.title}: {data.name}
164
- </span>
165
- );
166
- }
167
-
168
- const loadUser = block(Render, load);
169
-
170
- function App({User}) {
171
- return (
172
- <Suspense fallback={'Loading...'}>
173
- <User title="Name" />
174
- </Suspense>
175
- );
176
- }
153
+ // @gate experimental
154
+ it('does not support a lazy wrapper around a chunk', async () => {
155
+ function load(id) {
156
+ return {
157
+ id: id,
158
+ name: readString('Sebastian'),
159
+ };
160
+ }
161
178
- let resolveLazy;
179
- const LazyUser = React.lazy(
180
- () =>
181
- new Promise(resolve => {
182
- resolveLazy = function() {
183
- resolve({
184
- default: loadUser(123),
185
- });
186
- };
187
- }),
162
+ function Render(props, data) {
163
+ return (
164
+ <span>
165
+ {props.title}: {data.name}
166
+ </span>
167
);
168
+ }
169
190
- await ReactNoop.act(async () => {
191
- ReactNoop.render(<App User={LazyUser} />);
192
- });
170
+ const loadUser = block(Render, load);
171
194
- expect(ReactNoop).toMatchRenderedOutput('Loading...');
172
+ function App({User}) {
173
+ return (
174
+ <Suspense fallback={'Loading...'}>
175
+ <User title="Name" />
176
+ </Suspense>
177
+ );
178
+ }
179
196
- // Resolve the component.
197
- await resolveLazy();
180
+ let resolveLazy;
181
+ const LazyUser = React.lazy(
182
+ () =>
183
+ new Promise(resolve => {
184
+ resolveLazy = function() {
185
+ resolve({
186
+ default: loadUser(123),
187
+ });
188
+ };
189
+ }),
190
+ );
191
199
- expect(Scheduler).toFlushAndThrow(
200
- 'Element type is invalid. Received a promise that resolves to: [object Object]. ' +
201
- 'Lazy element type must resolve to a class or function.' +
202
- (__DEV__
203
- ? ' Did you wrap a component in React.lazy() more than once?'
204
- : ''),
205
- );
206
- },
207
- );
208
-
209
- it.experimental(
210
- 'can receive updated data for the same component',
211
- async () => {
212
- function load(firstName) {
213
- return {
214
- name: firstName,
215
- };
216
- }
192
+ await ReactNoop.act(async () => {
193
+ ReactNoop.render(<App User={LazyUser} />);
194
+ });
195
218
- function Render(props, data) {
219
- const [initialName] = useState(data.name);
220
- return (
221
- <>
222
- <span>Initial name: {initialName}</span>
223
- <span>Latest name: {data.name}</span>
224
- </>
225
- );
226
- }
196
+ expect(ReactNoop).toMatchRenderedOutput('Loading...');
197
228
- const loadUser = block(Render, load);
198
+ // Resolve the component.
199
+ await resolveLazy();
200
230
- function App({User}) {
231
- return (
232
- <Suspense fallback={'Loading...'}>
233
- <User title="Name" />
234
- </Suspense>
235
- );
236
- }
201
+ expect(Scheduler).toFlushAndThrow(
202
+ 'Element type is invalid. Received a promise that resolves to: [object Object]. ' +
203
+ 'Lazy element type must resolve to a class or function.' +
204
+ (__DEV__
205
+ ? ' Did you wrap a component in React.lazy() more than once?'
206
+ : ''),
207
+ );
208
+ });
209
238
- await ReactNoop.act(async () => {
239
- ReactNoop.render(<App User={loadUser('Sebastian')} />);
240
- });
210
+ // @gate experimental
211
+ it('can receive updated data for the same component', async () => {
212
+ function load(firstName) {
213
+ return {
214
+ name: firstName,
215
+ };
216
+ }
217
242
- expect(ReactNoop).toMatchRenderedOutput(
218
+ function Render(props, data) {
219
+ const [initialName] = useState(data.name);
220
+ return (
221
<>
244
- <span>Initial name: Sebastian</span>
245
- <span>Latest name: Sebastian</span>
246
- </>,
222
+ <span>Initial name: {initialName}</span>
223
+ <span>Latest name: {data.name}</span>
224
+ </>
225
);
226
+ }
227
249
- await ReactNoop.act(async () => {
250
- ReactNoop.render(<App User={loadUser('Dan')} />);
251
- });
228
+ const loadUser = block(Render, load);
229
253
- expect(ReactNoop).toMatchRenderedOutput(
254
- <>
255
- <span>Initial name: Sebastian</span>
256
- <span>Latest name: Dan</span>
257
- </>,
230
+ function App({User}) {
231
+ return (
232
+ <Suspense fallback={'Loading...'}>
233
+ <User title="Name" />
234
+ </Suspense>
235
);
259
- },
260
- );
236
+ }
237
+
238
+ await ReactNoop.act(async () => {
239
+ ReactNoop.render(<App User={loadUser('Sebastian')} />);
240
+ });
241
+
242
+ expect(ReactNoop).toMatchRenderedOutput(
243
+ <>
244
+ <span>Initial name: Sebastian</span>
245
+ <span>Latest name: Sebastian</span>
246
+ </>,
247
+ );
248
+
249
+ await ReactNoop.act(async () => {
250
+ ReactNoop.render(<App User={loadUser('Dan')} />);
251
+ });
252
+
253
+ expect(ReactNoop).toMatchRenderedOutput(
254
+ <>
255
+ <span>Initial name: Sebastian</span>
256
+ <span>Latest name: Dan</span>
257
+ </>,
258
+ );
259
+ });
260
});
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+134
-136
@@ -798,7 +798,8 @@ describe('ReactHooksWithNoopRenderer', () => {
798
});
799
800
// TODO: This should probably warn
801
- it.experimental('calling startTransition inside render phase', async () => {
801
+ // @gate experimental
802
+ it('calling startTransition inside render phase', async () => {
803
let startTransition;
804
function App() {
805
const [counter, setCounter] = useState(0);
@@ -2728,152 +2729,149 @@ describe('ReactHooksWithNoopRenderer', () => {
2729
});
2730
});
2731
describe('useTransition', () => {
2731
- it.experimental(
2732
- 'delays showing loading state until after timeout',
2733
- async () => {
2734
- let transition;
2735
- function App() {
2736
- const [show, setShow] = useState(false);
2737
- const [startTransition, isPending] = useTransition({
2738
- timeoutMs: 1000,
2732
+ // @gate experimental
2733
+ it('delays showing loading state until after timeout', async () => {
2734
+ let transition;
2735
+ function App() {
2736
+ const [show, setShow] = useState(false);
2737
+ const [startTransition, isPending] = useTransition({
2738
+ timeoutMs: 1000,
2739
+ });
2740
+ transition = () => {
2741
+ startTransition(() => {
2742
+ setShow(true);
2743
});
2740
- transition = () => {
2741
- startTransition(() => {
2742
- setShow(true);
2743
- });
2744
- };
2745
- return (
2746
- <Suspense
2747
- fallback={<Text text={`Loading... Pending: ${isPending}`} />}>
2748
- {show ? (
2749
- <AsyncText ms={2000} text={`After... Pending: ${isPending}`} />
2750
- ) : (
2751
- <Text text={`Before... Pending: ${isPending}`} />
2752
- )}
2753
- </Suspense>
2754
- );
2755
- }
2756
- ReactNoop.render(<App />);
2757
- expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
2758
- expect(ReactNoop.getChildren()).toEqual([
2759
- span('Before... Pending: false'),
2760
- ]);
2744
+ };
2745
+ return (
2746
+ <Suspense
2747
+ fallback={<Text text={`Loading... Pending: ${isPending}`} />}>
2748
+ {show ? (
2749
+ <AsyncText ms={2000} text={`After... Pending: ${isPending}`} />
2750
+ ) : (
2751
+ <Text text={`Before... Pending: ${isPending}`} />
2752
+ )}
2753
+ </Suspense>
2754
+ );
2755
+ }
2756
+ ReactNoop.render(<App />);
2757
+ expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
2758
+ expect(ReactNoop.getChildren()).toEqual([
2759
+ span('Before... Pending: false'),
2760
+ ]);
2761
2762
- act(() => {
2763
- Scheduler.unstable_runWithPriority(
2764
- Scheduler.unstable_UserBlockingPriority,
2765
- transition,
2766
- );
2767
- });
2768
- Scheduler.unstable_advanceTime(500);
2769
- await advanceTimers(500);
2770
- expect(Scheduler).toHaveYielded([
2771
- 'Before... Pending: true',
2772
- 'Suspend! [After... Pending: false]',
2773
- 'Loading... Pending: false',
2774
- ]);
2775
- expect(ReactNoop.getChildren()).toEqual([
2776
- span('Before... Pending: true'),
2777
- ]);
2762
+ act(() => {
2763
+ Scheduler.unstable_runWithPriority(
2764
+ Scheduler.unstable_UserBlockingPriority,
2765
+ transition,
2766
+ );
2767
+ });
2768
+ Scheduler.unstable_advanceTime(500);
2769
+ await advanceTimers(500);
2770
+ expect(Scheduler).toHaveYielded([
2771
+ 'Before... Pending: true',
2772
+ 'Suspend! [After... Pending: false]',
2773
+ 'Loading... Pending: false',
2774
+ ]);
2775
+ expect(ReactNoop.getChildren()).toEqual([
2776
+ span('Before... Pending: true'),
2777
+ ]);
2778
2779
- Scheduler.unstable_advanceTime(1000);
2780
- await advanceTimers(1000);
2781
- expect(ReactNoop.getChildren()).toEqual([
2782
- hiddenSpan('Before... Pending: true'),
2783
- span('Loading... Pending: false'),
2784
- ]);
2779
+ Scheduler.unstable_advanceTime(1000);
2780
+ await advanceTimers(1000);
2781
+ expect(ReactNoop.getChildren()).toEqual([
2782
+ hiddenSpan('Before... Pending: true'),
2783
+ span('Loading... Pending: false'),
2784
+ ]);
2785
2786
- Scheduler.unstable_advanceTime(500);
2787
- await advanceTimers(500);
2788
- expect(Scheduler).toHaveYielded([
2789
- 'Promise resolved [After... Pending: false]',
2790
- ]);
2791
- expect(Scheduler).toFlushAndYield(['After... Pending: false']);
2792
- expect(ReactNoop.getChildren()).toEqual([
2793
- span('After... Pending: false'),
2794
- ]);
2795
- },
2796
- );
2797
- it.experimental(
2798
- 'delays showing loading state until after busyDelayMs + busyMinDurationMs',
2799
- async () => {
2800
- let transition;
2801
- function App() {
2802
- const [show, setShow] = useState(false);
2803
- const [startTransition, isPending] = useTransition({
2804
- busyDelayMs: 1000,
2805
- busyMinDurationMs: 2000,
2786
+ Scheduler.unstable_advanceTime(500);
2787
+ await advanceTimers(500);
2788
+ expect(Scheduler).toHaveYielded([
2789
+ 'Promise resolved [After... Pending: false]',
2790
+ ]);
2791
+ expect(Scheduler).toFlushAndYield(['After... Pending: false']);
2792
+ expect(ReactNoop.getChildren()).toEqual([
2793
+ span('After... Pending: false'),
2794
+ ]);
2795
+ });
2796
+ // @gate experimental
2797
+ it('delays showing loading state until after busyDelayMs + busyMinDurationMs', async () => {
2798
+ let transition;
2799
+ function App() {
2800
+ const [show, setShow] = useState(false);
2801
+ const [startTransition, isPending] = useTransition({
2802
+ busyDelayMs: 1000,
2803
+ busyMinDurationMs: 2000,
2804
+ });
2805
+ transition = () => {
2806
+ startTransition(() => {
2807
+ setShow(true);
2808
});
2807
- transition = () => {
2808
- startTransition(() => {
2809
- setShow(true);
2810
- });
2811
- };
2812
- return (
2813
- <Suspense
2814
- fallback={<Text text={`Loading... Pending: ${isPending}`} />}>
2815
- {show ? (
2816
- <AsyncText ms={2000} text={`After... Pending: ${isPending}`} />
2817
- ) : (
2818
- <Text text={`Before... Pending: ${isPending}`} />
2819
- )}
2820
- </Suspense>
2821
- );
2822
- }
2823
- ReactNoop.render(<App />);
2824
- expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
2825
- expect(ReactNoop.getChildren()).toEqual([
2826
- span('Before... Pending: false'),
2827
- ]);
2809
+ };
2810
+ return (
2811
+ <Suspense
2812
+ fallback={<Text text={`Loading... Pending: ${isPending}`} />}>
2813
+ {show ? (
2814
+ <AsyncText ms={2000} text={`After... Pending: ${isPending}`} />
2815
+ ) : (
2816
+ <Text text={`Before... Pending: ${isPending}`} />
2817
+ )}
2818
+ </Suspense>
2819
+ );
2820
+ }
2821
+ ReactNoop.render(<App />);
2822
+ expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
2823
+ expect(ReactNoop.getChildren()).toEqual([
2824
+ span('Before... Pending: false'),
2825
+ ]);
2826
2829
- act(() => {
2830
- Scheduler.unstable_runWithPriority(
2831
- Scheduler.unstable_UserBlockingPriority,
2832
- transition,
2833
- );
2834
- });
2835
- Scheduler.unstable_advanceTime(1000);
2836
- await advanceTimers(1000);
2837
- expect(Scheduler).toHaveYielded([
2838
- 'Before... Pending: true',
2839
- 'Suspend! [After... Pending: false]',
2840
- 'Loading... Pending: false',
2841
- ]);
2842
- expect(ReactNoop.getChildren()).toEqual([
2843
- span('Before... Pending: true'),
2844
- ]);
2827
+ act(() => {
2828
+ Scheduler.unstable_runWithPriority(
2829
+ Scheduler.unstable_UserBlockingPriority,
2830
+ transition,
2831
+ );
2832
+ });
2833
+ Scheduler.unstable_advanceTime(1000);
2834
+ await advanceTimers(1000);
2835
+ expect(Scheduler).toHaveYielded([
2836
+ 'Before... Pending: true',
2837
+ 'Suspend! [After... Pending: false]',
2838
+ 'Loading... Pending: false',
2839
+ ]);
2840
+ expect(ReactNoop.getChildren()).toEqual([
2841
+ span('Before... Pending: true'),
2842
+ ]);
2843
2846
- // Resolve the promise. The whole tree has now completed. However,
2847
- // because we exceeded the busy threshold, we won't commit the
2848
- // result yet.
2849
- Scheduler.unstable_advanceTime(1000);
2850
- await advanceTimers(1000);
2851
- expect(Scheduler).toHaveYielded([
2852
- 'Promise resolved [After... Pending: false]',
2853
- ]);
2854
- expect(Scheduler).toFlushAndYield(['After... Pending: false']);
2855
- expect(ReactNoop.getChildren()).toEqual([
2856
- span('Before... Pending: true'),
2857
- ]);
2844
+ // Resolve the promise. The whole tree has now completed. However,
2845
+ // because we exceeded the busy threshold, we won't commit the
2846
+ // result yet.
2847
+ Scheduler.unstable_advanceTime(1000);
2848
+ await advanceTimers(1000);
2849
+ expect(Scheduler).toHaveYielded([
2850
+ 'Promise resolved [After... Pending: false]',
2851
+ ]);
2852
+ expect(Scheduler).toFlushAndYield(['After... Pending: false']);
2853
+ expect(ReactNoop.getChildren()).toEqual([
2854
+ span('Before... Pending: true'),
2855
+ ]);
2856
2859
- // Advance time until just before the `busyMinDuration` threshold.
2860
- Scheduler.unstable_advanceTime(999);
2861
- await advanceTimers(999);
2862
- expect(ReactNoop.getChildren()).toEqual([
2863
- span('Before... Pending: true'),
2864
- ]);
2857
+ // Advance time until just before the `busyMinDuration` threshold.
2858
+ Scheduler.unstable_advanceTime(999);
2859
+ await advanceTimers(999);
2860
+ expect(ReactNoop.getChildren()).toEqual([
2861
+ span('Before... Pending: true'),
2862
+ ]);
2863
2866
- // Advance time just a bit more. Now we complete the transition.
2867
- Scheduler.unstable_advanceTime(300);
2868
- await advanceTimers(300);
2869
- expect(ReactNoop.getChildren()).toEqual([
2870
- span('After... Pending: false'),
2871
- ]);
2872
- },
2873
- );
2864
+ // Advance time just a bit more. Now we complete the transition.
2865
+ Scheduler.unstable_advanceTime(300);
2866
+ await advanceTimers(300);
2867
+ expect(ReactNoop.getChildren()).toEqual([
2868
+ span('After... Pending: false'),
2869
+ ]);
2870
+ });
2871
});
2872
describe('useDeferredValue', () => {
2876
- it.experimental('defers text value until specified timeout', async () => {
2873
+ // @gate experimental
2874
+ it('defers text value until specified timeout', async () => {
2875
function TextBox({text}) {
2876
return <AsyncText ms={1000} text={text} />;
2877
}
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
+106
-108
@@ -3738,127 +3738,125 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3738
});
3739
3740
// Regression: https://github.com/facebook/react/issues/18486
3741
- it.experimental(
3742
- 'does not get stuck in pending state with render phase updates',
3743
- async () => {
3744
- let setTextWithShortTransition;
3745
- let setTextWithLongTransition;
3746
-
3747
- function App() {
3748
- const [startShortTransition, isPending1] = React.useTransition({
3749
- timeoutMs: 5000,
3750
- });
3751
- const [startLongTransition, isPending2] = React.useTransition({
3752
- timeoutMs: 30000,
3753
- });
3754
- const isPending = isPending1 || isPending2;
3755
- const [text, setText] = React.useState('');
3756
- const [mirror, setMirror] = React.useState('');
3757
-
3758
- if (text !== mirror) {
3759
- // Render phase update was needed to repro the bug.
3760
- setMirror(text);
3761
- }
3741
+ // @gate experimental
3742
+ it('does not get stuck in pending state with render phase updates', async () => {
3743
+ let setTextWithShortTransition;
3744
+ let setTextWithLongTransition;
3745
3763
- setTextWithShortTransition = value => {
3764
- startShortTransition(() => {
3765
- setText(value);
3766
- });
3767
- };
3768
- setTextWithLongTransition = value => {
3769
- startLongTransition(() => {
3770
- setText(value);
3771
- });
3772
- };
3773
-
3774
- return (
3775
- <>
3776
- {isPending ? <Text text="Pending..." /> : null}
3777
- {text !== '' ? <AsyncText text={text} /> : <Text text={text} />}
3778
- </>
3779
- );
3780
- }
3746
+ function App() {
3747
+ const [startShortTransition, isPending1] = React.useTransition({
3748
+ timeoutMs: 5000,
3749
+ });
3750
+ const [startLongTransition, isPending2] = React.useTransition({
3751
+ timeoutMs: 30000,
3752
+ });
3753
+ const isPending = isPending1 || isPending2;
3754
+ const [text, setText] = React.useState('');
3755
+ const [mirror, setMirror] = React.useState('');
3756
3782
- function Root() {
3783
- return (
3784
- <Suspense fallback={<Text text="Loading..." />}>
3785
- <App />
3786
- </Suspense>
3787
- );
3757
+ if (text !== mirror) {
3758
+ // Render phase update was needed to repro the bug.
3759
+ setMirror(text);
3760
}
3761
3790
- const root = ReactNoop.createRoot();
3791
- await ReactNoop.act(async () => {
3792
- root.render(<Root />);
3793
- });
3794
- expect(Scheduler).toHaveYielded(['']);
3795
- expect(root).toMatchRenderedOutput(<span prop="" />);
3762
+ setTextWithShortTransition = value => {
3763
+ startShortTransition(() => {
3764
+ setText(value);
3765
+ });
3766
+ };
3767
+ setTextWithLongTransition = value => {
3768
+ startLongTransition(() => {
3769
+ setText(value);
3770
+ });
3771
+ };
3772
3797
- // Update to "a". That will suspend.
3798
- await ReactNoop.act(async () => {
3799
- setTextWithShortTransition('a');
3800
- expect(Scheduler).toFlushAndYield([
3801
- 'Pending...',
3802
- '',
3803
- 'Suspend! [a]',
3804
- 'Loading...',
3805
- ]);
3806
- });
3807
- expect(Scheduler).toHaveYielded([]);
3808
- expect(root).toMatchRenderedOutput(
3773
+ return (
3774
<>
3810
- <span prop="Pending..." />
3811
- <span prop="" />
3812
- </>,
3775
+ {isPending ? <Text text="Pending..." /> : null}
3776
+ {text !== '' ? <AsyncText text={text} /> : <Text text={text} />}
3777
+ </>
3778
);
3779
+ }
3780
3815
- // Update to "b". That will suspend, too.
3816
- await ReactNoop.act(async () => {
3817
- setTextWithLongTransition('b');
3818
- expect(Scheduler).toFlushAndYield([
3819
- // Neither is resolved yet.
3820
- 'Pending...',
3821
- '',
3822
- 'Pending...',
3823
- 'Suspend! [a]',
3824
- 'Loading...',
3825
- 'Suspend! [b]',
3826
- 'Loading...',
3827
- ]);
3828
- });
3829
- expect(Scheduler).toHaveYielded([]);
3830
- expect(root).toMatchRenderedOutput(
3831
- <>
3832
- <span prop="Pending..." />
3833
- <span prop="" />
3834
- </>,
3781
+ function Root() {
3782
+ return (
3783
+ <Suspense fallback={<Text text="Loading..." />}>
3784
+ <App />
3785
+ </Suspense>
3786
);
3787
+ }
3788
3837
- // Resolve "a". But "b" is still pending.
3838
- await ReactNoop.act(async () => {
3839
- await resolveText('a');
3840
- });
3841
- expect(Scheduler).toHaveYielded([
3842
- 'Promise resolved [a]',
3789
+ const root = ReactNoop.createRoot();
3790
+ await ReactNoop.act(async () => {
3791
+ root.render(<Root />);
3792
+ });
3793
+ expect(Scheduler).toHaveYielded(['']);
3794
+ expect(root).toMatchRenderedOutput(<span prop="" />);
3795
+
3796
+ // Update to "a". That will suspend.
3797
+ await ReactNoop.act(async () => {
3798
+ setTextWithShortTransition('a');
3799
+ expect(Scheduler).toFlushAndYield([
3800
'Pending...',
3844
- 'a',
3801
+ '',
3802
+ 'Suspend! [a]',
3803
+ 'Loading...',
3804
+ ]);
3805
+ });
3806
+ expect(Scheduler).toHaveYielded([]);
3807
+ expect(root).toMatchRenderedOutput(
3808
+ <>
3809
+ <span prop="Pending..." />
3810
+ <span prop="" />
3811
+ </>,
3812
+ );
3813
+
3814
+ // Update to "b". That will suspend, too.
3815
+ await ReactNoop.act(async () => {
3816
+ setTextWithLongTransition('b');
3817
+ expect(Scheduler).toFlushAndYield([
3818
+ // Neither is resolved yet.
3819
+ 'Pending...',
3820
+ '',
3821
+ 'Pending...',
3822
+ 'Suspend! [a]',
3823
+ 'Loading...',
3824
'Suspend! [b]',
3825
'Loading...',
3826
]);
3848
- expect(root).toMatchRenderedOutput(
3849
- <>
3850
- <span prop="Pending..." />
3851
- <span prop="a" />
3852
- </>,
3853
- );
3827
+ });
3828
+ expect(Scheduler).toHaveYielded([]);
3829
+ expect(root).toMatchRenderedOutput(
3830
+ <>
3831
+ <span prop="Pending..." />
3832
+ <span prop="" />
3833
+ </>,
3834
+ );
3835
3855
- // Resolve "b". This should remove the pending state.
3856
- await ReactNoop.act(async () => {
3857
- await resolveText('b');
3858
- });
3859
- expect(Scheduler).toHaveYielded(['Promise resolved [b]', 'b']);
3860
- // The bug was that the pending state got stuck forever.
3861
- expect(root).toMatchRenderedOutput(<span prop="b" />);
3862
- },
3863
- );
3836
+ // Resolve "a". But "b" is still pending.
3837
+ await ReactNoop.act(async () => {
3838
+ await resolveText('a');
3839
+ });
3840
+ expect(Scheduler).toHaveYielded([
3841
+ 'Promise resolved [a]',
3842
+ 'Pending...',
3843
+ 'a',
3844
+ 'Suspend! [b]',
3845
+ 'Loading...',
3846
+ ]);
3847
+ expect(root).toMatchRenderedOutput(
3848
+ <>
3849
+ <span prop="Pending..." />
3850
+ <span prop="a" />
3851
+ </>,
3852
+ );
3853
+
3854
+ // Resolve "b". This should remove the pending state.
3855
+ await ReactNoop.act(async () => {
3856
+ await resolveText('b');
3857
+ });
3858
+ expect(Scheduler).toHaveYielded(['Promise resolved [b]', 'b']);
3859
+ // The bug was that the pending state got stuck forever.
3860
+ expect(root).toMatchRenderedOutput(<span prop="b" />);
3861
+ });
3862
});
packages/react-reconciler/src/__tests__/ReactTransition-test.internal.js
+35
-37
@@ -59,47 +59,45 @@ describe('ReactTransition', () => {
59
return Component;
60
}
61
62
- it.experimental(
63
- 'isPending works even if called from outside an input event',
64
- async () => {
65
- const Async = createAsyncText('Async');
66
- let start;
67
- function App() {
68
- const [show, setShow] = useState(false);
69
- const [startTransition, isPending] = useTransition();
70
- start = () => startTransition(() => setShow(true));
71
- return (
72
- <Suspense fallback={<Text text="Loading..." />}>
73
- {isPending ? <Text text="Pending..." /> : null}
74
- {show ? <Async /> : <Text text="(empty)" />}
75
- </Suspense>
76
- );
77
- }
62
+ // @gate experimental
63
+ it('isPending works even if called from outside an input event', async () => {
64
+ const Async = createAsyncText('Async');
65
+ let start;
66
+ function App() {
67
+ const [show, setShow] = useState(false);
68
+ const [startTransition, isPending] = useTransition();
69
+ start = () => startTransition(() => setShow(true));
70
+ return (
71
+ <Suspense fallback={<Text text="Loading..." />}>
72
+ {isPending ? <Text text="Pending..." /> : null}
73
+ {show ? <Async /> : <Text text="(empty)" />}
74
+ </Suspense>
75
+ );
76
+ }
77
79
- const root = ReactNoop.createRoot();
78
+ const root = ReactNoop.createRoot();
79
81
- await act(async () => {
82
- root.render(<App />);
83
- });
84
- expect(Scheduler).toHaveYielded(['(empty)']);
85
- expect(root).toMatchRenderedOutput('(empty)');
80
+ await act(async () => {
81
+ root.render(<App />);
82
+ });
83
+ expect(Scheduler).toHaveYielded(['(empty)']);
84
+ expect(root).toMatchRenderedOutput('(empty)');
85
87
- await act(async () => {
88
- start();
86
+ await act(async () => {
87
+ start();
88
90
- expect(Scheduler).toFlushAndYield([
91
- 'Pending...',
92
- '(empty)',
93
- 'Suspend! [Async]',
94
- 'Loading...',
95
- ]);
89
+ expect(Scheduler).toFlushAndYield([
90
+ 'Pending...',
91
+ '(empty)',
92
+ 'Suspend! [Async]',
93
+ 'Loading...',
94
+ ]);
95
97
- expect(root).toMatchRenderedOutput('Pending...(empty)');
96
+ expect(root).toMatchRenderedOutput('Pending...(empty)');
97
99
- await Async.resolve();
100
- });
101
- expect(Scheduler).toHaveYielded(['Async']);
102
- expect(root).toMatchRenderedOutput('Async');
103
- },
104
- );
98
+ await Async.resolve();
99
+ });
100
+ expect(Scheduler).toHaveYielded(['Async']);
101
+ expect(root).toMatchRenderedOutput('Async');
102
+ });
103
});
packages/react/src/__tests__/ReactStrictMode-test.js
+90
-93
@@ -362,76 +362,75 @@ describe('Concurrent Mode', () => {
362
Scheduler = require('scheduler');
363
});
364
365
- it.experimental(
366
- 'should warn about unsafe legacy lifecycle methods anywhere in the tree',
367
- () => {
368
- class AsyncRoot extends React.Component {
369
- UNSAFE_componentWillMount() {}
370
- UNSAFE_componentWillUpdate() {}
371
- render() {
372
- return (
365
+ // @gate experimental
366
+ it('should warn about unsafe legacy lifecycle methods anywhere in the tree', () => {
367
+ class AsyncRoot extends React.Component {
368
+ UNSAFE_componentWillMount() {}
369
+ UNSAFE_componentWillUpdate() {}
370
+ render() {
371
+ return (
372
+ <div>
373
+ <Wrapper>
374
+ <Foo />
375
+ </Wrapper>
376
<div>
374
- <Wrapper>
375
- <Foo />
376
- </Wrapper>
377
- <div>
378
- <Bar />
379
- <Foo />
380
- </div>
377
+ <Bar />
378
+ <Foo />
379
</div>
382
- );
383
- }
384
- }
385
- function Wrapper({children}) {
386
- return <div>{children}</div>;
380
+ </div>
381
+ );
382
}
388
- class Foo extends React.Component {
389
- UNSAFE_componentWillReceiveProps() {}
390
- render() {
391
- return null;
392
- }
383
+ }
384
+ function Wrapper({children}) {
385
+ return <div>{children}</div>;
386
+ }
387
+ class Foo extends React.Component {
388
+ UNSAFE_componentWillReceiveProps() {}
389
+ render() {
390
+ return null;
391
}
394
- class Bar extends React.Component {
395
- UNSAFE_componentWillReceiveProps() {}
396
- render() {
397
- return null;
398
- }
392
+ }
393
+ class Bar extends React.Component {
394
+ UNSAFE_componentWillReceiveProps() {}
395
+ render() {
396
+ return null;
397
}
398
+ }
399
401
- const container = document.createElement('div');
402
- const root = ReactDOM.createRoot(container);
403
- root.render(<AsyncRoot />);
404
- expect(() => Scheduler.unstable_flushAll()).toErrorDev(
405
- [
406
- /* eslint-disable max-len */
407
- `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
400
+ const container = document.createElement('div');
401
+ const root = ReactDOM.createRoot(container);
402
+ root.render(<AsyncRoot />);
403
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev(
404
+ [
405
+ /* eslint-disable max-len */
406
+ `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
407
408
* Move code with side effects to componentDidMount, and set initial state in the constructor.
409
410
Please update the following components: AsyncRoot`,
412
- `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
411
+ `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
412
413
* Move data fetching code or side effects to componentDidUpdate.
414
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
415
416
Please update the following components: Bar, Foo`,
418
- `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
417
+ `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
418
419
* Move data fetching code or side effects to componentDidUpdate.
420
421
Please update the following components: AsyncRoot`,
423
- /* eslint-enable max-len */
424
- ],
425
- {withoutStack: true},
426
- );
422
+ /* eslint-enable max-len */
423
+ ],
424
+ {withoutStack: true},
425
+ );
426
428
- // Dedupe
429
- root.render(<AsyncRoot />);
430
- Scheduler.unstable_flushAll();
431
- },
432
- );
427
+ // Dedupe
428
+ root.render(<AsyncRoot />);
429
+ Scheduler.unstable_flushAll();
430
+ });
431
434
- it.experimental('should coalesce warnings by lifecycle name', () => {
432
+ // @gate experimental
433
+ it('should coalesce warnings by lifecycle name', () => {
434
class AsyncRoot extends React.Component {
435
UNSAFE_componentWillMount() {}
436
UNSAFE_componentWillUpdate() {}
@@ -513,52 +512,50 @@ Please update the following components: Parent`,
512
Scheduler.unstable_flushAll();
513
});
514
516
- it.experimental(
517
- 'should warn about components not present during the initial render',
518
- () => {
519
- class AsyncRoot extends React.Component {
520
- render() {
521
- return this.props.foo ? <Foo /> : <Bar />;
522
- }
523
- }
524
- class Foo extends React.Component {
525
- UNSAFE_componentWillMount() {}
526
- render() {
527
- return null;
528
- }
529
- }
530
- class Bar extends React.Component {
531
- UNSAFE_componentWillMount() {}
532
- render() {
533
- return null;
534
- }
535
- }
536
-
537
- const container = document.createElement('div');
538
- const root = ReactDOM.createRoot(container);
539
- root.render(<AsyncRoot foo={true} />);
540
- expect(() =>
541
- Scheduler.unstable_flushAll(),
542
- ).toErrorDev(
543
- 'Using UNSAFE_componentWillMount in strict mode is not recommended',
544
- {withoutStack: true},
545
- );
515
+ // @gate experimental
516
+ it('should warn about components not present during the initial render', () => {
517
+ class AsyncRoot extends React.Component {
518
+ render() {
519
+ return this.props.foo ? <Foo /> : <Bar />;
520
+ }
521
+ }
522
+ class Foo extends React.Component {
523
+ UNSAFE_componentWillMount() {}
524
+ render() {
525
+ return null;
526
+ }
527
+ }
528
+ class Bar extends React.Component {
529
+ UNSAFE_componentWillMount() {}
530
+ render() {
531
+ return null;
532
+ }
533
+ }
534
547
- root.render(<AsyncRoot foo={false} />);
548
- expect(() =>
549
- Scheduler.unstable_flushAll(),
550
- ).toErrorDev(
551
- 'Using UNSAFE_componentWillMount in strict mode is not recommended',
552
- {withoutStack: true},
553
- );
535
+ const container = document.createElement('div');
536
+ const root = ReactDOM.createRoot(container);
537
+ root.render(<AsyncRoot foo={true} />);
538
+ expect(() =>
539
+ Scheduler.unstable_flushAll(),
540
+ ).toErrorDev(
541
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
542
+ {withoutStack: true},
543
+ );
544
+
545
+ root.render(<AsyncRoot foo={false} />);
546
+ expect(() =>
547
+ Scheduler.unstable_flushAll(),
548
+ ).toErrorDev(
549
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
550
+ {withoutStack: true},
551
+ );
552
555
- // Dedupe
556
- root.render(<AsyncRoot foo={true} />);
557
- Scheduler.unstable_flushAll();
558
- root.render(<AsyncRoot foo={false} />);
559
- Scheduler.unstable_flushAll();
560
- },
561
- );
553
+ // Dedupe
554
+ root.render(<AsyncRoot foo={true} />);
555
+ Scheduler.unstable_flushAll();
556
+ root.render(<AsyncRoot foo={false} />);
557
+ Scheduler.unstable_flushAll();
558
+ });
559
560
it('should also warn inside of "strict" mode trees', () => {
561
const {StrictMode} = React;
scripts/jest/setupTests.js
-31
@@ -267,37 +267,6 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
267
throw Error(errorMsg);
268
};
269
270
- // TODO: Deprecate these helpers in favor of @gate pragma
271
- const it = global.it;
272
- const fit = global.fit;
273
- const xit = global.xit;
274
- if (__EXPERIMENTAL__) {
275
- it.experimental = it;
276
- fit.experimental = it.only.experimental = it.experimental.only = fit;
277
- xit.experimental = it.skip.experimental = it.experimental.skip = xit;
278
- } else {
279
- const errorMessage =
280
- 'Tests marked experimental are expected to fail, but this one passed.';
281
- it.experimental = (message, callback) => {
282
- it(`[EXPERIMENTAL, SHOULD FAIL] ${message}`, () =>
283
- expectTestToFail(callback, errorMessage));
284
- };
285
- fit.experimental = it.only.experimental = it.experimental.only = (
286
- message,
287
- callback
288
- ) => {
289
- fit(`[EXPERIMENTAL, SHOULD FAIL] ${message}`, () =>
290
- expectTestToFail(callback, errorMessage));
291
- };
292
- xit.experimental = it.skip.experimental = it.experimental.skip = (
293
- message,
294
- callback
295
- ) => {
296
- xit(`[EXPERIMENTAL, SHOULD FAIL] ${message}`, () =>
297
- expectTestToFail(callback, errorMessage));
298
- };
299
- }
300
-
270
const gatedErrorMessage = 'Gated test was expected to fail, but it passed.';
271
global._test_gate = (gateFn, testName, callback) => {
272
let shouldPass;