@@ -132,3 +132,65 @@ test('closing the library before a scene fetch settles does not replace the curr
132132 globalThis . fetch = originalFetch
133133 }
134134} )
135+
136+ test ( 'a later library confirm wins over a slower first scene fetch' , { concurrency : false } , async ( ) => {
137+ const { render, screen, fireEvent, waitFor, cleanup } = await import ( '@testing-library/react' )
138+ const { SceneLibraryDialog } = await import ( '../src/components/Sidebar/SceneLibraryDialog.tsx' )
139+ const originalFetch = globalThis . fetch
140+ const scenes = Array . from ( { length : 9 } , ( _ , index ) => ( {
141+ name : `2026-08-25-22h21m0${ index } s_Station-loop-${ index } _aaaaaa.scene.json` ,
142+ type : 'scene' ,
143+ mode : null ,
144+ size : 12 ,
145+ created_at : 1000 + index ,
146+ url : `/api/v1/file/scene-${ index } .scene.json` ,
147+ thumbnail_url : `/api/v1/file/scene-${ index } .scene.preview.png` ,
148+ } ) )
149+ const sceneFor = ( index : number ) => ( {
150+ version : 1 ,
151+ name : `Station loop ${ index } ` ,
152+ width : 1280 ,
153+ height : 720 ,
154+ duration : 10 ,
155+ layers : [ {
156+ id : 'plate' , name : 'Plate' , type : 'image' , source : '/api/v1/file/plate.jpg' , visible : true , z : 0 ,
157+ transform : { x : 50 , y : 50 , scale : 1 , opacity : 1 } ,
158+ animation : { start : { x : 50 , y : 50 , scale : 1 } , end : { x : 50 , y : 50 , scale : 1 } , duration : 10 , curve : 'linear' } ,
159+ } ] ,
160+ } )
161+ let releaseFirst : ( ( value : Response ) => void ) | undefined
162+ let releaseSecond : ( ( value : Response ) => void ) | undefined
163+ const firstGate = new Promise < Response > ( resolve => { releaseFirst = resolve } )
164+ const secondGate = new Promise < Response > ( resolve => { releaseSecond = resolve } )
165+ globalThis . fetch = ( async ( input : string | URL | Request ) => {
166+ const url = String ( input )
167+ if ( url . includes ( '/api/v1/outputs' ) && url . includes ( 'media_type=scene' ) ) {
168+ const parsed = new URL ( url , 'http://localhost' )
169+ const offset = Number ( parsed . searchParams . get ( 'offset' ) || 0 )
170+ return new Response ( JSON . stringify ( { outputs : scenes . slice ( offset , offset + 8 ) , total : scenes . length } ) , { headers : { 'content-type' : 'application/json' } } )
171+ }
172+ if ( url . includes ( '/api/v1/file/scene-0.scene.json' ) ) return firstGate
173+ if ( url . includes ( '/api/v1/file/scene-8.scene.json' ) ) return secondGate
174+ throw new Error ( `Unexpected request: ${ url } ` )
175+ } ) as typeof fetch
176+ const opened : string [ ] = [ ]
177+ try {
178+ render ( < SceneLibraryDialog open workspace = "default" onClose = { ( ) => undefined } onPickFile = { ( ) => undefined } onOpenScene = { scene => opened . push ( scene . name ) } /> )
179+ await waitFor ( ( ) => assert . ok ( screen . getAllByText ( 'Station loop 0' ) . length >= 1 ) )
180+ fireEvent . click ( screen . getAllByText ( 'Station loop 0' ) [ 0 ] )
181+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'Open scene' } ) )
182+ fireEvent . click ( screen . getByLabelText ( 'Next page' ) )
183+ await waitFor ( ( ) => assert . ok ( screen . getAllByText ( 'Station loop 8' ) . length >= 1 ) )
184+ fireEvent . click ( screen . getAllByText ( 'Station loop 8' ) [ 0 ] )
185+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'Open scene' } ) )
186+ releaseFirst ?.( new Response ( JSON . stringify ( sceneFor ( 0 ) ) , { headers : { 'content-type' : 'application/json' } } ) )
187+ await Promise . resolve ( )
188+ await Promise . resolve ( )
189+ assert . deepEqual ( opened , [ ] )
190+ releaseSecond ?.( new Response ( JSON . stringify ( sceneFor ( 8 ) ) , { headers : { 'content-type' : 'application/json' } } ) )
191+ await waitFor ( ( ) => assert . deepEqual ( opened , [ 'Station loop 8' ] ) )
192+ } finally {
193+ cleanup ( )
194+ globalThis . fetch = originalFetch
195+ }
196+ } )
0 commit comments