168
`onTransitionStart(${name}, ${startTime})`,
169
);
170
},
171
+ onTransitionProgress: (name, startTime, endTime, pending) => {
172
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
173
+ Scheduler.unstable_yieldValue(
174
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
175
+ );
176
+ },
177
onTransitionComplete: (name, startTime, endTime) => {
178
Scheduler.unstable_yieldValue(
179
`onTransitionComplete(${name}, ${startTime}, ${endTime})`,
289
`onTransitionStart(${name}, ${startTime})`,
290
);
291
},
292
+ onTransitionProgress: (name, startTime, endTime, pending) => {
293
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
294
+ Scheduler.unstable_yieldValue(
295
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
296
+ );
297
+ },
298
onTransitionComplete: (name, startTime, endTime) => {
299
Scheduler.unstable_yieldValue(
300
`onTransitionComplete(${name}, ${startTime}, ${endTime})`,
313
{navigate ? (
314
<Suspense
315
fallback={<Text text="Loading..." />}
304
- name="suspense page">
316
+ unstable_name="suspense page">
317
<AsyncText text="Page Two" />
318
</Suspense>
319
) : (
342
'Suspend [Page Two]',
343
'Loading...',
344
'onTransitionStart(page transition, 1000)',
345
+ 'onTransitionProgress(page transition, 1000, 2000, [suspense page])',
346
]);
347
348
ReactNoop.expire(1000);
351
352
expect(Scheduler).toFlushAndYield([
353
'Page Two',
354
+ 'onTransitionProgress(page transition, 1000, 3000, [])',
355
'onTransitionComplete(page transition, 1000, 3000)',
356
]);
357
});
365
`onTransitionStart(${name}, ${startTime})`,
366
);
367
},
368
+ onTransitionProgress: (name, startTime, endTime, pending) => {
369
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
370
+ Scheduler.unstable_yieldValue(
371
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
372
+ );
373
+ },
374
onTransitionComplete: (name, startTime, endTime) => {
375
Scheduler.unstable_yieldValue(
376
`onTransitionComplete(${name}, ${startTime}, ${endTime})`,
397
{navigate ? (
398
<>
399
{showText ? (
380
- <Suspense fallback={<Text text="Show Text Loading..." />}>
400
+ <Suspense
401
+ unstable_name="show text"
402
+ fallback={<Text text="Show Text Loading..." />}>
403
<AsyncText text="Show Text" />
404
</Suspense>
405
) : null}
406
<Suspense
407
fallback={<Text text="Loading..." />}
386
- name="suspense page">
408
+ unstable_name="suspense page">
409
<AsyncText text="Page Two" />
410
</Suspense>
411
</>
432
'Suspend [Page Two]',
433
'Loading...',
434
'onTransitionStart(page transition, 1000)',
435
+ 'onTransitionProgress(page transition, 1000, 1000, [suspense page])',
436
]);
437
438
await resolveText('Page Two');
440
await advanceTimers(1000);
441
expect(Scheduler).toFlushAndYield([
442
'Page Two',
443
+ 'onTransitionProgress(page transition, 1000, 2000, [])',
444
'onTransitionComplete(page transition, 1000, 2000)',
445
]);
446
450
'Show Text Loading...',
451
'Page Two',
452
'onTransitionStart(text transition, 2000)',
453
+ 'onTransitionProgress(text transition, 2000, 2000, [show text])',
454
]);
455
456
await resolveText('Show Text');
458
await advanceTimers(1000);
459
expect(Scheduler).toFlushAndYield([
460
'Show Text',
461
+ 'onTransitionProgress(text transition, 2000, 3000, [])',
462
'onTransitionComplete(text transition, 2000, 3000)',
463
]);
464
});
472
`onTransitionStart(${name}, ${startTime})`,
473
);
474
},
475
+ onTransitionProgress: (name, startTime, endTime, pending) => {
476
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
477
+ Scheduler.unstable_yieldValue(
478
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
479
+ );
480
+ },
481
onTransitionComplete: (name, startTime, endTime) => {
482
Scheduler.unstable_yieldValue(
483
`onTransitionComplete(${name}, ${startTime}, ${endTime})`,
502
{navigate ? (
503
<>
504
{showText ? (
473
- <Suspense fallback={<Text text="Show Text Loading..." />}>
505
+ <Suspense
506
+ unstable_name="show text"
507
+ fallback={<Text text="Show Text Loading..." />}>
508
<AsyncText text="Show Text" />
509
</Suspense>
510
) : null}
511
<Suspense
512
fallback={<Text text="Loading..." />}
479
- name="suspense page">
513
+ unstable_name="suspense page">
514
<AsyncText text="Page Two" />
515
</Suspense>
516
</>
539
'Suspend [Page Two]',
540
'Loading...',
541
'onTransitionStart(page transition, 1000)',
542
+ 'onTransitionProgress(page transition, 1000, 2000, [suspense page])',
543
]);
544
});
545
552
'Suspend [Page Two]',
553
'Loading...',
554
'onTransitionStart(show text, 2000)',
555
+ 'onTransitionProgress(show text, 2000, 2000, [show text])',
556
]);
557
});
558
563
564
expect(Scheduler).toFlushAndYield([
565
'Page Two',
566
+ 'onTransitionProgress(page transition, 1000, 3000, [])',
567
'onTransitionComplete(page transition, 1000, 3000)',
568
]);
569
573
574
expect(Scheduler).toFlushAndYield([
575
'Show Text',
576
+ 'onTransitionProgress(show text, 2000, 4000, [])',
577
'onTransitionComplete(show text, 2000, 4000)',
578
]);
579
});
580
});
581
582
+ // @gate enableTransitionTracing
583
+ it('trace interaction with nested and sibling suspense boundaries', async () => {
584
+ const transitionCallbacks = {
585
+ onTransitionStart: (name, startTime) => {
586
+ Scheduler.unstable_yieldValue(
587
+ `onTransitionStart(${name}, ${startTime})`,
588
+ );
589
+ },
590
+ onTransitionProgress: (name, startTime, endTime, pending) => {
591
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
592
+ Scheduler.unstable_yieldValue(
593
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
594
+ );
595
+ },
596
+ onTransitionComplete: (name, startTime, endTime) => {
597
+ Scheduler.unstable_yieldValue(
598
+ `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
599
+ );
600
+ },
601
+ };
602
+
603
+ let navigateToPageTwo;
604
+ function App() {
605
+ const [navigate, setNavigate] = useState(false);
606
+ navigateToPageTwo = () => {
607
+ setNavigate(true);
608
+ };
609
+
610
+ return (
611
+ <div>
612
+ {navigate ? (
613
+ <>
614
+ <Suspense
615
+ fallback={<Text text="Loading..." />}
616
+ unstable_name="suspense page">
617
+ <AsyncText text="Page Two" />
618
+ <Suspense
619
+ unstable_name="show text one"
620
+ fallback={<Text text="Show Text One Loading..." />}>
621
+ <AsyncText text="Show Text One" />
622
+ </Suspense>
623
+ <div>
624
+ <Suspense
625
+ unstable_name="show text two"
626
+ fallback={<Text text="Show Text Two Loading..." />}>
627
+ <AsyncText text="Show Text Two" />
628
+ </Suspense>
629
+ </div>
630
+ </Suspense>
631
+ </>
632
+ ) : (
633
+ <Text text="Page One" />
634
+ )}
635
+ </div>
636
+ );
637
+ }
638
+
639
+ const root = ReactNoop.createRoot({transitionCallbacks});
640
+ await act(async () => {
641
+ root.render(<App />);
642
+ ReactNoop.expire(1000);
643
+ await advanceTimers(1000);
644
+
645
+ expect(Scheduler).toFlushAndYield(['Page One']);
646
+ });
647
+
648
+ await act(async () => {
649
+ startTransition(() => navigateToPageTwo(), {name: 'page transition'});
650
+ ReactNoop.expire(1000);
651
+ await advanceTimers(1000);
652
+
653
+ expect(Scheduler).toFlushAndYield([
654
+ 'Suspend [Page Two]',
655
+ 'Suspend [Show Text One]',
656
+ 'Show Text One Loading...',
657
+ 'Suspend [Show Text Two]',
658
+ 'Show Text Two Loading...',
659
+ 'Loading...',
660
+ 'onTransitionStart(page transition, 1000)',
661
+ 'onTransitionProgress(page transition, 1000, 2000, [suspense page])',
662
+ ]);
663
+
664
+ resolveText('Page Two');
665
+ ReactNoop.expire(1000);
666
+ await advanceTimers(1000);
667
+
668
+ expect(Scheduler).toFlushAndYield([
669
+ 'Page Two',
670
+ 'Suspend [Show Text One]',
671
+ 'Show Text One Loading...',
672
+ 'Suspend [Show Text Two]',
673
+ 'Show Text Two Loading...',
674
+ 'onTransitionProgress(page transition, 1000, 3000, [show text one, show text two])',
675
+ ]);
676
+
677
+ resolveText('Show Text One');
678
+ ReactNoop.expire(1000);
679
+ await advanceTimers(1000);
680
+
681
+ expect(Scheduler).toFlushAndYield([
682
+ 'Show Text One',
683
+ 'onTransitionProgress(page transition, 1000, 4000, [show text two])',
684
+ ]);
685
+
686
+ resolveText('Show Text Two');
687
+ ReactNoop.expire(1000);
688
+ await advanceTimers(1000);
689
+
690
+ expect(Scheduler).toFlushAndYield([
691
+ 'Show Text Two',
692
+ 'onTransitionProgress(page transition, 1000, 5000, [])',
693
+ 'onTransitionComplete(page transition, 1000, 5000)',
694
+ ]);
695
+ });
696
+ });
697
+
698
+ // @gate enableTransitionTracing
699
+ it('trace interactions with the same child suspense boundaries', async () => {
700
+ const transitionCallbacks = {
701
+ onTransitionStart: (name, startTime) => {
702
+ Scheduler.unstable_yieldValue(
703
+ `onTransitionStart(${name}, ${startTime})`,
704
+ );
705
+ },
706
+ onTransitionProgress: (name, startTime, endTime, pending) => {
707
+ const suspenseNames = pending.map(p => p.name || '<null>').join(', ');
708
+ Scheduler.unstable_yieldValue(
709
+ `onTransitionProgress(${name}, ${startTime}, ${endTime}, [${suspenseNames}])`,
710
+ );
711
+ },
712
+ onTransitionComplete: (name, startTime, endTime) => {
713
+ Scheduler.unstable_yieldValue(
714
+ `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
715
+ );
716
+ },
717
+ };
718
+
719
+ let setNavigate;
720
+ let setShowTextOne;
721
+ let setShowTextTwo;
722
+ function App() {
723
+ const [navigate, _setNavigate] = useState(false);
724
+ const [showTextOne, _setShowTextOne] = useState(false);
725
+ const [showTextTwo, _setShowTextTwo] = useState(false);
726
+
727
+ setNavigate = () => _setNavigate(true);
728
+ setShowTextOne = () => _setShowTextOne(true);
729
+ setShowTextTwo = () => _setShowTextTwo(true);
730
+
731
+ return (
732
+ <div>
733
+ {navigate ? (
734
+ <>
735
+ <Suspense
736
+ fallback={<Text text="Loading..." />}
737
+ unstable_name="suspense page">
738
+ <AsyncText text="Page Two" />
739
+ {/* showTextOne is entangled with navigate */}
740
+ {showTextOne ? (
741
+ <Suspense
742
+ unstable_name="show text one"
743
+ fallback={<Text text="Show Text One Loading..." />}>
744
+ <AsyncText text="Show Text One" />
745
+ </Suspense>
746
+ ) : null}
747
+ <Suspense fallback={<Text text="Show Text Loading..." />}>
748
+ <AsyncText text="Show Text" />
749
+ </Suspense>
750
+ {/* showTextTwo's suspense boundaries shouldn't stop navigate's suspense boundaries
751
+ from completing */}
752
+ {showTextTwo ? (
753
+ <Suspense
754
+ unstable_name="show text two"
755
+ fallback={<Text text="Show Text Two Loading..." />}>
756
+ <AsyncText text="Show Text Two" />
757
+ </Suspense>
758
+ ) : null}
759
+ </Suspense>
760
+ </>
761
+ ) : (
762
+ <Text text="Page One" />
763
+ )}
764
+ </div>
765
+ );
766
+ }
767
+
768
+ const root = ReactNoop.createRoot({transitionCallbacks});
769
+ await act(async () => {
770
+ root.render(<App />);
771
+ ReactNoop.expire(1000);
772
+ await advanceTimers(1000);
773
+
774
+ expect(Scheduler).toFlushAndYield(['Page One']);
775
+ });
776
+
777
+ await act(async () => {
778
+ startTransition(() => setNavigate(), {name: 'navigate'});
779
+ startTransition(() => setShowTextOne(), {name: 'show text one'});
780
+ ReactNoop.expire(1000);
781
+ await advanceTimers(1000);
782
+
783
+ expect(Scheduler).toFlushAndYield([
784
+ 'Suspend [Page Two]',
785
+ 'Suspend [Show Text One]',
786
+ 'Show Text One Loading...',
787
+ 'Suspend [Show Text]',
788
+ 'Show Text Loading...',
789
+ 'Loading...',
790
+ 'onTransitionStart(navigate, 1000)',
791
+ 'onTransitionStart(show text one, 1000)',
792
+ 'onTransitionProgress(navigate, 1000, 2000, [suspense page])',
793
+ 'onTransitionProgress(show text one, 1000, 2000, [suspense page])',
794
+ ]);
795
+
796
+ resolveText('Page Two');
797
+ ReactNoop.expire(1000);
798
+ await advanceTimers(1000);
799
+ expect(Scheduler).toFlushAndYield([
800
+ 'Page Two',
801
+ 'Suspend [Show Text One]',
802
+ 'Show Text One Loading...',
803
+ 'Suspend [Show Text]',
804
+ 'Show Text Loading...',
805
+ 'onTransitionProgress(navigate, 1000, 3000, [show text one, <null>])',
806
+ 'onTransitionProgress(show text one, 1000, 3000, [show text one, <null>])',
807
+ ]);
808
+
809
+ startTransition(() => setShowTextTwo(), {name: 'show text two'});
810
+ ReactNoop.expire(1000);
811
+ await advanceTimers(1000);
812
+
813
+ expect(Scheduler).toFlushAndYield([
814
+ 'Page Two',
815
+ 'Suspend [Show Text One]',
816
+ 'Show Text One Loading...',
817
+ 'Suspend [Show Text]',
818
+ 'Show Text Loading...',
819
+ 'Suspend [Show Text Two]',
820
+ 'Show Text Two Loading...',
821
+ 'onTransitionStart(show text two, 3000)',
822
+ 'onTransitionProgress(show text two, 3000, 4000, [show text two])',
823
+ ]);
824
+
825
+ // This should not cause navigate to finish because it's entangled with
826
+ // show text one
827
+ resolveText('Show Text');
828
+ ReactNoop.expire(1000);
829
+ await advanceTimers(1000);
830
+
831
+ expect(Scheduler).toFlushAndYield([
832
+ 'Show Text',
833
+ 'onTransitionProgress(navigate, 1000, 5000, [show text one])',
834
+ 'onTransitionProgress(show text one, 1000, 5000, [show text one])',
835
+ ]);
836
+
837
+ // This should not cause show text two to finish but nothing else
838
+ resolveText('Show Text Two');
839
+ ReactNoop.expire(1000);
840
+ await advanceTimers(1000);
841
+ expect(Scheduler).toFlushAndYield([
842
+ 'Show Text Two',
843
+ 'onTransitionProgress(show text two, 3000, 6000, [])',
844
+ 'onTransitionComplete(show text two, 3000, 6000)',
845
+ ]);
846
+
847
+ // This should cause everything to finish
848
+ resolveText('Show Text One');
849
+ ReactNoop.expire(1000);
850
+ await advanceTimers(1000);
851
+
852
+ expect(Scheduler).toFlushAndYield([
853
+ 'Show Text One',
854
+ 'onTransitionProgress(navigate, 1000, 7000, [])',
855
+ 'onTransitionProgress(show text one, 1000, 7000, [])',
856
+ 'onTransitionComplete(navigate, 1000, 7000)',
857
+ 'onTransitionComplete(show text one, 1000, 7000)',
858
+ ]);
859
+ });
860
+ });
861
+
862
// @gate enableTransitionTracing
863
it('should correctly trace interactions for tracing markers complete', async () => {
864
const transitionCallbacks = {