@@ -11,7 +11,6 @@ import {
1111const SPAN_FIRST = 'span-first' ;
1212const SPAN_SECOND = 'span-second' ;
1313
14- /** Flush the coordinator's deferred up-flip timer. */
1514function flushDefer ( ) : void {
1615 jest . runOnlyPendingTimers ( ) ;
1716}
@@ -53,7 +52,7 @@ describe('timeToDisplayCoordinator', () => {
5352 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
5453 } ) ;
5554
56- test ( 'late-registering not-ready checkpoint un-readies the aggregate' , ( ) => {
55+ test ( 'late-registering not-ready checkpoint makes the aggregate "non-ready" ' , ( ) => {
5756 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
5857 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , true ) ;
5958 flushDefer ( ) ;
@@ -63,58 +62,6 @@ describe('timeToDisplayCoordinator', () => {
6362 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
6463 } ) ;
6564
66- test ( 'unregistering the sole blocker keeps the aggregate not-ready (sticky)' , ( ) => {
67- // A not-ready checkpoint that unmounts while it is the sole blocker is
68- // kept in the registry to prevent a premature aggregate flip. Otherwise
69- // a conditionally-rendered loading section that disappears before its
70- // data resolves would silently record an incomplete display.
71- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
72- const unregisterB = registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , false ) ;
73- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
74-
75- unregisterB ( ) ;
76- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
77- // The sticky 'b' is still tracked so a subsequent component on the same
78- // span continues to see it as a blocker.
79- expect ( hasAnyCheckpoints ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
80- } ) ;
81-
82- test ( 'unregistering a non-sole-blocker not-ready checkpoint removes it normally' , ( ) => {
83- // When other not-ready checkpoints are still present, removing one
84- // would not flip the aggregate, so the sticky safeguard does not apply.
85- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , false ) ;
86- const unregisterB = registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , false ) ;
87-
88- unregisterB ( ) ;
89- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
90- // 'a' remains, 'b' is gone.
91- updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
92- flushDefer ( ) ;
93- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
94- } ) ;
95-
96- test ( 'unregistering a ready checkpoint never goes sticky' , ( ) => {
97- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , false ) ;
98- const unregisterB = registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , true ) ;
99-
100- unregisterB ( ) ;
101- // 'a' is still blocking; 'b' was ready so removing it is safe.
102- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
103- updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
104- flushDefer ( ) ;
105- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
106- } ) ;
107-
108- test ( 'unregistering the last checkpoint leaves aggregate not-ready' , ( ) => {
109- const unregister = registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
110- flushDefer ( ) ;
111- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
112-
113- unregister ( ) ;
114- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
115- expect ( hasAnyCheckpoints ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
116- } ) ;
117-
11865 test ( 'different spans are independent' , ( ) => {
11966 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
12067 registerCheckpoint ( 'ttfd' , SPAN_SECOND , 'a' , false ) ;
@@ -123,29 +70,14 @@ describe('timeToDisplayCoordinator', () => {
12370 expect ( isAllReady ( 'ttfd' , SPAN_SECOND ) ) . toBe ( false ) ;
12471 } ) ;
12572
126- test ( 'different kinds are independent' , ( ) => {
73+ test ( 'different kinds of spans are independent' , ( ) => {
12774 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
12875 registerCheckpoint ( 'ttid' , SPAN_FIRST , 'a' , false ) ;
12976 flushDefer ( ) ;
13077 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
13178 expect ( isAllReady ( 'ttid' , SPAN_FIRST ) ) . toBe ( false ) ;
13279 } ) ;
13380
134- test ( 'updateCheckpoint is a no-op for unknown id' , ( ) => {
135- const listener = jest . fn ( ) ;
136- subscribe ( 'ttfd' , SPAN_FIRST , listener ) ;
137- updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'nope' , true ) ;
138- expect ( listener ) . not . toHaveBeenCalled ( ) ;
139- } ) ;
140-
141- test ( 'updateCheckpoint with same ready value does not notify' , ( ) => {
142- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
143- const listener = jest . fn ( ) ;
144- subscribe ( 'ttfd' , SPAN_FIRST , listener ) ;
145- updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
146- expect ( listener ) . not . toHaveBeenCalled ( ) ;
147- } ) ;
148-
14981 test ( 'subscribers are notified only on aggregate-ready flips' , ( ) => {
15082 const listener = jest . fn ( ) ;
15183 subscribe ( 'ttfd' , SPAN_FIRST , listener ) ;
@@ -159,25 +91,6 @@ describe('timeToDisplayCoordinator', () => {
15991 expect ( listener ) . toHaveBeenCalledTimes ( 2 ) ;
16092 } ) ;
16193
162- test ( 'non-flipping checkpoint changes do not wake subscribers (storm avoidance)' , ( ) => {
163- const listener = jest . fn ( ) ;
164- subscribe ( 'ttfd' , SPAN_FIRST , listener ) ;
165-
166- for ( let i = 0 ; i < 10 ; i ++ ) {
167- registerCheckpoint ( 'ttfd' , SPAN_FIRST , `cp-${ i } ` , false ) ;
168- }
169- expect ( listener ) . toHaveBeenCalledTimes ( 0 ) ;
170-
171- for ( let i = 0 ; i < 9 ; i ++ ) {
172- updateCheckpoint ( 'ttfd' , SPAN_FIRST , `cp-${ i } ` , true ) ;
173- }
174- expect ( listener ) . toHaveBeenCalledTimes ( 0 ) ;
175-
176- updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'cp-9' , true ) ;
177- flushDefer ( ) ;
178- expect ( listener ) . toHaveBeenCalledTimes ( 1 ) ;
179- } ) ;
180-
18194 test ( 'unsubscribe stops further notifications' , ( ) => {
18295 const listener = jest . fn ( ) ;
18396 const unsubscribe = subscribe ( 'ttfd' , SPAN_FIRST , listener ) ;
@@ -193,68 +106,25 @@ describe('timeToDisplayCoordinator', () => {
193106 expect ( listener ) . not . toHaveBeenCalled ( ) ;
194107 } ) ;
195108
196- test ( 'up-flip is deferred so a same-task late-mounting peer can cancel it' , ( ) => {
197- // The defining race: header registers ready=true alone, sidebar mounts
198- // a tick later (e.g. parent useEffect setState→child mount). Without the
199- // defer, header would fire instantly. With the defer, sidebar's
200- // registration cancels the pending up-flip before it fires.
109+ test ( 'up-flip is deferred' , ( ) => {
201110 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'header' , true ) ;
202- // Aggregate is *raw* ready, but `isAllReady` (deferred view) is still false.
203111 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
204112
205- // Same-task: sidebar mounts before the timer macrotask runs.
206113 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'sidebar' , false ) ;
207114
208115 flushDefer ( ) ;
209- // Aggregate must NOT have flipped — the defer protected us.
210116 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
211-
212- // Sidebar resolves — now we get a real ready transition.
213117 updateCheckpoint ( 'ttfd' , SPAN_FIRST , 'sidebar' , true ) ;
214118 flushDefer ( ) ;
215119 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
216120 } ) ;
217121
218- test ( 'down-flip is immediate (cancels pending up-flip and not deferred itself) ' , ( ) => {
122+ test ( 'down-flip is immediate' , ( ) => {
219123 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
220124 flushDefer ( ) ;
221125 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
222126
223127 registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , false ) ;
224- // No flushDefer: down-flip is immediate.
225128 expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
226129 } ) ;
227-
228- test ( 'clearSpan drops all coordinator state for a span' , ( ) => {
229- // Simulates the integration calling clearSpan after popTimeToDisplayFor
230- // returns. Prevents the registries from accumulating entries for screens
231- // that outlive their span (keep-alive, idle-timeout discarded txns, etc.).
232- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
233- registerCheckpoint ( 'ttid' , SPAN_FIRST , 'a' , true ) ;
234- registerCheckpoint ( 'ttfd' , SPAN_SECOND , 'a' , true ) ;
235- flushDefer ( ) ;
236- expect ( isAllReady ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
237- flushDefer ( ) ;
238- expect ( isAllReady ( 'ttid' , SPAN_FIRST ) ) . toBe ( true ) ;
239- flushDefer ( ) ;
240- expect ( isAllReady ( 'ttfd' , SPAN_SECOND ) ) . toBe ( true ) ;
241-
242- clearSpan ( SPAN_FIRST ) ;
243-
244- expect ( hasAnyCheckpoints ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
245- expect ( hasAnyCheckpoints ( 'ttid' , SPAN_FIRST ) ) . toBe ( false ) ;
246- // Other spans untouched.
247- flushDefer ( ) ;
248- expect ( isAllReady ( 'ttfd' , SPAN_SECOND ) ) . toBe ( true ) ;
249- } ) ;
250-
251- test ( 'clearSpan also drops sticky checkpoints' , ( ) => {
252- registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'a' , true ) ;
253- const unregisterB = registerCheckpoint ( 'ttfd' , SPAN_FIRST , 'b' , false ) ;
254- unregisterB ( ) ; // becomes sticky
255- expect ( hasAnyCheckpoints ( 'ttfd' , SPAN_FIRST ) ) . toBe ( true ) ;
256-
257- clearSpan ( SPAN_FIRST ) ;
258- expect ( hasAnyCheckpoints ( 'ttfd' , SPAN_FIRST ) ) . toBe ( false ) ;
259- } ) ;
260130} ) ;
0 commit comments