1212let React ;
1313let ReactDOM ;
1414let Suspense ;
15- let ReactCache ;
1615let Scheduler ;
17- let TextResource ;
1816let act ;
17+ let textCache ;
1918
2019describe ( 'ReactDOMSuspensePlaceholder' , ( ) => {
2120 let container ;
@@ -24,48 +23,78 @@ describe('ReactDOMSuspensePlaceholder', () => {
2423 jest . resetModules ( ) ;
2524 React = require ( 'react' ) ;
2625 ReactDOM = require ( 'react-dom' ) ;
27- ReactCache = require ( 'react-cache' ) ;
2826 Scheduler = require ( 'scheduler' ) ;
2927 act = require ( 'internal-test-utils' ) . act ;
3028 Suspense = React . Suspense ;
3129 container = document . createElement ( 'div' ) ;
3230 document . body . appendChild ( container ) ;
3331
34- TextResource = ReactCache . unstable_createResource (
35- ( [ text , ms = 0 ] ) => {
36- return new Promise ( ( resolve , reject ) =>
37- setTimeout ( ( ) => {
38- resolve ( text ) ;
39- } , ms ) ,
40- ) ;
41- } ,
42- ( [ text , ms ] ) => text ,
43- ) ;
32+ textCache = new Map ( ) ;
4433 } ) ;
4534
4635 afterEach ( ( ) => {
4736 document . body . removeChild ( container ) ;
4837 } ) ;
4938
50- function advanceTimers ( ms ) {
51- // Note: This advances Jest's virtual time but not React's. Use
52- // ReactNoop.expire for that.
53- if ( typeof ms !== 'number' ) {
54- throw new Error ( 'Must specify ms' ) ;
39+ function resolveText ( text ) {
40+ const record = textCache . get ( text ) ;
41+ if ( record === undefined ) {
42+ const newRecord = {
43+ status : 'resolved' ,
44+ value : text ,
45+ } ;
46+ textCache . set ( text , newRecord ) ;
47+ } else if ( record . status === 'pending' ) {
48+ const thenable = record . value ;
49+ record . status = 'resolved' ;
50+ record . value = text ;
51+ thenable . pings . forEach ( t => t ( ) ) ;
52+ }
53+ }
54+
55+ function readText ( text ) {
56+ const record = textCache . get ( text ) ;
57+ if ( record !== undefined ) {
58+ switch ( record . status ) {
59+ case 'pending' :
60+ Scheduler . log ( `Suspend! [${ text } ]` ) ;
61+ throw record . value ;
62+ case 'rejected' :
63+ throw record . value ;
64+ case 'resolved' :
65+ return record . value ;
66+ }
67+ } else {
68+ Scheduler . log ( `Suspend! [${ text } ]` ) ;
69+ const thenable = {
70+ pings : [ ] ,
71+ then ( resolve ) {
72+ if ( newRecord . status === 'pending' ) {
73+ thenable . pings . push ( resolve ) ;
74+ } else {
75+ Promise . resolve ( ) . then ( ( ) => resolve ( newRecord . value ) ) ;
76+ }
77+ } ,
78+ } ;
79+
80+ const newRecord = {
81+ status : 'pending' ,
82+ value : thenable ,
83+ } ;
84+ textCache . set ( text , newRecord ) ;
85+
86+ throw thenable ;
5587 }
56- jest . advanceTimersByTime ( ms ) ;
57- // Wait until the end of the current tick
58- // We cannot use a timer since we're faking them
59- return Promise . resolve ( ) . then ( ( ) => { } ) ;
6088 }
6189
62- function Text ( props ) {
63- return props . text ;
90+ function Text ( { text} ) {
91+ Scheduler . log ( text ) ;
92+ return text ;
6493 }
6594
66- function AsyncText ( props ) {
67- const text = props . text ;
68- TextResource . read ( [ props . text , props . ms ] ) ;
95+ function AsyncText ( { text } ) {
96+ readText ( text ) ;
97+ Scheduler . log ( text ) ;
6998 return text ;
7099 }
71100
@@ -82,7 +111,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
82111 < Text text = "A" />
83112 </ div >
84113 < div ref = { divs [ 1 ] } >
85- < AsyncText ms = { 500 } text = "B" />
114+ < AsyncText text = "B" />
86115 </ div >
87116 < div style = { { display : 'inline' } } ref = { divs [ 2 ] } >
88117 < Text text = "C" />
@@ -95,9 +124,9 @@ describe('ReactDOMSuspensePlaceholder', () => {
95124 expect ( window . getComputedStyle ( divs [ 1 ] . current ) . display ) . toEqual ( 'none' ) ;
96125 expect ( window . getComputedStyle ( divs [ 2 ] . current ) . display ) . toEqual ( 'none' ) ;
97126
98- await advanceTimers ( 500 ) ;
99-
100- Scheduler . unstable_flushAll ( ) ;
127+ await act ( async ( ) => {
128+ await resolveText ( 'B' ) ;
129+ } ) ;
101130
102131 expect ( window . getComputedStyle ( divs [ 0 ] . current ) . display ) . toEqual ( 'block' ) ;
103132 expect ( window . getComputedStyle ( divs [ 1 ] . current ) . display ) . toEqual ( 'block' ) ;
@@ -110,17 +139,17 @@ describe('ReactDOMSuspensePlaceholder', () => {
110139 return (
111140 < Suspense fallback = { < Text text = "Loading..." /> } >
112141 < Text text = "A" />
113- < AsyncText ms = { 500 } text = "B" />
142+ < AsyncText text = "B" />
114143 < Text text = "C" />
115144 </ Suspense >
116145 ) ;
117146 }
118147 ReactDOM . render ( < App /> , container ) ;
119148 expect ( container . textContent ) . toEqual ( 'Loading...' ) ;
120149
121- await advanceTimers ( 500 ) ;
122-
123- Scheduler . unstable_flushAll ( ) ;
150+ await act ( async ( ) => {
151+ await resolveText ( 'B' ) ;
152+ } ) ;
124153
125154 expect ( container . textContent ) . toEqual ( 'ABC' ) ;
126155 } ) ;
@@ -147,7 +176,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
147176 < Suspense fallback = { < Text text = "Loading..." /> } >
148177 < Sibling > Sibling</ Sibling >
149178 < span >
150- < AsyncText ms = { 500 } text = "Async" />
179+ < AsyncText text = "Async" />
151180 </ span >
152181 </ Suspense >
153182 ) ;
@@ -161,8 +190,16 @@ describe('ReactDOMSuspensePlaceholder', () => {
161190 '"display: none;"></span>Loading...' ,
162191 ) ;
163192
193+ // Update the inline display style. It will be overridden because it's
194+ // inside a hidden fallback.
164195 await act ( async ( ) => setIsVisible ( true ) ) ;
196+ expect ( container . innerHTML ) . toEqual (
197+ '<span style="display: none;">Sibling</span><span style=' +
198+ '"display: none;"></span>Loading...' ,
199+ ) ;
165200
201+ // Unsuspend. The style should now match the inline prop.
202+ await act ( async ( ) => resolveText ( 'Async' ) ) ;
166203 expect ( container . innerHTML ) . toEqual (
167204 '<span style="display: inline;">Sibling</span><span style="">Async</span>' ,
168205 ) ;
0 commit comments