@@ -32,36 +32,63 @@ describe('ReactTestUtils.act()', () => {
3232 concurrentRoot = ReactDOM . unstable_createRoot ( dom ) ;
3333 concurrentRoot . render ( el ) ;
3434 }
35+
3536 function unmountConcurrent ( _dom ) {
3637 if ( concurrentRoot !== null ) {
3738 concurrentRoot . unmount ( ) ;
3839 concurrentRoot = null ;
3940 }
4041 }
41- runActTests ( 'concurrent mode' , renderConcurrent , unmountConcurrent ) ;
42+
43+ function rerenderConcurrent ( el ) {
44+ concurrentRoot . render ( el ) ;
45+ }
46+
47+ runActTests (
48+ 'concurrent mode' ,
49+ renderConcurrent ,
50+ unmountConcurrent ,
51+ rerenderConcurrent ,
52+ ) ;
4253
4354 // and then in sync mode
55+
56+ let syncDom = null ;
4457 function renderSync ( el , dom ) {
58+ syncDom = dom ;
4559 ReactDOM . render ( el , dom ) ;
4660 }
61+
4762 function unmountSync ( dom ) {
63+ syncDom = null ;
4864 ReactDOM . unmountComponentAtNode ( dom ) ;
4965 }
50- runActTests ( 'legacy sync mode' , renderSync , unmountSync ) ;
66+
67+ function rerenderSync ( el ) {
68+ ReactDOM . render ( el , syncDom ) ;
69+ }
70+
71+ runActTests ( 'legacy sync mode' , renderSync , unmountSync , rerenderSync ) ;
5172
5273 // and then in batched mode
5374 let batchedRoot ;
5475 function renderBatched ( el , dom ) {
5576 batchedRoot = ReactDOM . unstable_createSyncRoot ( dom ) ;
5677 batchedRoot . render ( el ) ;
5778 }
79+
5880 function unmountBatched ( dom ) {
5981 if ( batchedRoot !== null ) {
6082 batchedRoot . unmount ( ) ;
6183 batchedRoot = null ;
6284 }
6385 }
64- runActTests ( 'batched mode' , renderBatched , unmountBatched ) ;
86+
87+ function rerenderBatched ( el ) {
88+ batchedRoot . render ( el ) ;
89+ }
90+
91+ runActTests ( 'batched mode' , renderBatched , unmountBatched , rerenderBatched ) ;
6592
6693 describe ( 'unacted effects' , ( ) => {
6794 function App ( ) {
@@ -117,7 +144,7 @@ describe('ReactTestUtils.act()', () => {
117144 } ) ;
118145} ) ;
119146
120- function runActTests ( label , render , unmount ) {
147+ function runActTests ( label , render , unmount , rerender ) {
121148 describe ( label , ( ) => {
122149 beforeEach ( ( ) => {
123150 jest . resetModules ( ) ;
@@ -546,7 +573,7 @@ function runActTests(label, render, unmount) {
546573 expect ( interactions . size ) . toBe ( 1 ) ;
547574 expectedInteraction = Array . from ( interactions ) [ 0 ] ;
548575
549- render ( < Component /> , container ) ;
576+ rerender ( < Component /> ) ;
550577 } ,
551578 ) ;
552579 } ) ;
@@ -576,7 +603,7 @@ function runActTests(label, render, unmount) {
576603 expect ( interactions . size ) . toBe ( 1 ) ;
577604 expectedInteraction = Array . from ( interactions ) [ 0 ] ;
578605
579- render ( < Component /> , secondContainer ) ;
606+ rerender ( < Component /> ) ;
580607 } ) ;
581608 } ,
582609 ) ;
@@ -693,5 +720,70 @@ function runActTests(label, render, unmount) {
693720 }
694721 } ) ;
695722 } ) ;
723+
724+ describe ( 'suspense' , ( ) => {
725+ it ( 'triggers fallbacks if available' , async ( ) => {
726+ let resolved = false ;
727+ let resolve ;
728+ const promise = new Promise ( _resolve => {
729+ resolve = _resolve ;
730+ } ) ;
731+
732+ function Suspends ( ) {
733+ if ( resolved ) {
734+ return 'was suspended' ;
735+ }
736+ throw promise ;
737+ }
738+
739+ function App ( props ) {
740+ return (
741+ < React . Suspense
742+ fallback = { < span data-test-id = "spinner" > loading...</ span > } >
743+ { props . suspend ? < Suspends /> : 'content' }
744+ </ React . Suspense >
745+ ) ;
746+ }
747+
748+ // render something so there's content
749+ act ( ( ) => {
750+ render ( < App suspend = { false } /> , container ) ;
751+ } ) ;
752+
753+ // trigger a suspendy update
754+ act ( ( ) => {
755+ rerender ( < App suspend = { true } /> ) ;
756+ } ) ;
757+ expect ( document . querySelector ( '[data-test-id=spinner]' ) ) . not . toBeNull ( ) ;
758+
759+ // now render regular content again
760+ act ( ( ) => {
761+ rerender ( < App suspend = { false } /> ) ;
762+ } ) ;
763+ expect ( document . querySelector ( '[data-test-id=spinner]' ) ) . toBeNull ( ) ;
764+
765+ // trigger a suspendy update with a delay
766+ React . unstable_withSuspenseConfig (
767+ ( ) => {
768+ act ( ( ) => {
769+ rerender ( < App suspend = { true } /> ) ;
770+ } ) ;
771+ } ,
772+ { timeout : 5000 } ,
773+ ) ;
774+ // the spinner shows up regardless
775+ expect ( document . querySelector ( '[data-test-id=spinner]' ) ) . not . toBeNull ( ) ;
776+
777+ // resolve the promise
778+ await act ( async ( ) => {
779+ resolved = true ;
780+ resolve ( ) ;
781+ } ) ;
782+
783+ // spinner gone, content showing
784+ expect ( document . querySelector ( '[data-test-id=spinner]' ) ) . toBeNull ( ) ;
785+ expect ( container . textContent ) . toBe ( 'was suspended' ) ;
786+ } ) ;
787+ } ) ;
696788 } ) ;
697789}
0 commit comments