@@ -99,4 +99,102 @@ describe("createFlashDetection", () => {
9999 dispose ( ) ;
100100 } ) ;
101101 } ) ;
102+
103+ it ( "builds a peek update when a hot-polled item changes status and its repo is collapsed" , async ( ) => {
104+ await createRoot ( async ( dispose ) => {
105+ const [ items , setItems ] = createSignal < MockItem [ ] > ( [
106+ { id : 1 , repoFullName : "org/repo" , status : "pending" } ,
107+ ] ) ;
108+ const { flashingIds, peekUpdates } = createFlashDetection ( {
109+ getItems : items ,
110+ getHotIds : ( ) => new Set ( [ 1 ] ) ,
111+ isRepoExpanded : ( ) => false ,
112+ trackKey : ( item ) => item . status ,
113+ itemLabel : ( item ) => `Item ${ item . id } ` ,
114+ itemStatus : ( item ) => item . status ,
115+ } ) ;
116+
117+ // Let the initial effect seed prevValues (Solid schedules effects as microtasks)
118+ await Promise . resolve ( ) ;
119+
120+ // Status change on a hot-polled item whose repo is collapsed — peek expected
121+ setItems ( [ { id : 1 , repoFullName : "org/repo" , status : "success" } ] ) ;
122+ await Promise . resolve ( ) ;
123+
124+ expect ( flashingIds ( ) . has ( 1 ) ) . toBe ( true ) ;
125+ expect ( peekUpdates ( ) . size ) . toBe ( 1 ) ;
126+ expect ( peekUpdates ( ) . get ( "org/repo" ) ) . toEqual ( {
127+ itemLabel : "Item 1" ,
128+ newStatus : "success" ,
129+ } ) ;
130+
131+ dispose ( ) ;
132+ } ) ;
133+ } ) ;
134+
135+ it ( "suppresses the peek update when the item's repo is expanded" , async ( ) => {
136+ await createRoot ( async ( dispose ) => {
137+ const [ items , setItems ] = createSignal < MockItem [ ] > ( [
138+ { id : 1 , repoFullName : "org/repo" , status : "pending" } ,
139+ ] ) ;
140+ const { flashingIds, peekUpdates } = createFlashDetection ( {
141+ getItems : items ,
142+ getHotIds : ( ) => new Set ( [ 1 ] ) ,
143+ isRepoExpanded : ( ) => true ,
144+ trackKey : ( item ) => item . status ,
145+ itemLabel : ( item ) => `Item ${ item . id } ` ,
146+ itemStatus : ( item ) => item . status ,
147+ } ) ;
148+
149+ // Let the initial effect seed prevValues (Solid schedules effects as microtasks)
150+ await Promise . resolve ( ) ;
151+
152+ // Status change on a hot-polled item whose repo is expanded — flash still
153+ // fires, but the peek preview is suppressed for the expanded repo
154+ setItems ( [ { id : 1 , repoFullName : "org/repo" , status : "success" } ] ) ;
155+ await Promise . resolve ( ) ;
156+
157+ expect ( flashingIds ( ) . has ( 1 ) ) . toBe ( true ) ;
158+ expect ( peekUpdates ( ) . size ) . toBe ( 0 ) ;
159+
160+ dispose ( ) ;
161+ } ) ;
162+ } ) ;
163+
164+ it ( "aggregates the peek label when multiple items in a collapsed repo change" , async ( ) => {
165+ await createRoot ( async ( dispose ) => {
166+ const [ items , setItems ] = createSignal < MockItem [ ] > ( [
167+ { id : 1 , repoFullName : "org/repo" , status : "pending" } ,
168+ { id : 2 , repoFullName : "org/repo" , status : "pending" } ,
169+ ] ) ;
170+ const { peekUpdates } = createFlashDetection ( {
171+ getItems : items ,
172+ getHotIds : ( ) => new Set ( [ 1 , 2 ] ) ,
173+ isRepoExpanded : ( ) => false ,
174+ trackKey : ( item ) => item . status ,
175+ itemLabel : ( item ) => `Item ${ item . id } ` ,
176+ itemStatus : ( item ) => item . status ,
177+ } ) ;
178+
179+ // Let the initial effect seed prevValues (Solid schedules effects as microtasks)
180+ await Promise . resolve ( ) ;
181+
182+ // Two hot-polled items in the same collapsed repo change — the peek label
183+ // aggregates into a "first + N more" summary. The differing statuses confirm
184+ // the aggregated newStatus reflects the first changed item, not the last.
185+ setItems ( [
186+ { id : 1 , repoFullName : "org/repo" , status : "success" } ,
187+ { id : 2 , repoFullName : "org/repo" , status : "failure" } ,
188+ ] ) ;
189+ await Promise . resolve ( ) ;
190+
191+ expect ( peekUpdates ( ) . size ) . toBe ( 1 ) ;
192+ expect ( peekUpdates ( ) . get ( "org/repo" ) ) . toEqual ( {
193+ itemLabel : "Item 1 + 1 more" ,
194+ newStatus : "success" ,
195+ } ) ;
196+
197+ dispose ( ) ;
198+ } ) ;
199+ } ) ;
102200} ) ;
0 commit comments