@@ -167,4 +167,60 @@ describe('EventSource', () => {
167167 } ) ,
168168 ) ;
169169 } ) ;
170+
171+ test ( 'calls onopen with parsed response headers during streaming (onprogress, pre-DONE)' , ( ) => {
172+ const onopen = jest . fn ( ) ;
173+ eventSource . onopen = onopen ;
174+
175+ mockXhr . getAllResponseHeaders = jest . fn (
176+ ( ) => 'X-Ld-Fd-Fallback: true\r\nX-Ld-Fd-Fallback-Ttl: 60\r\nContent-Type: text/event-stream' ,
177+ ) ;
178+ mockXhr . responseText = '' ;
179+
180+ jest . runAllTimers ( ) ;
181+
182+ // Simulate a chunk arriving while the connection is still LOADING (readyState 3).
183+ // This is the real runtime path for a live stream: DONE (readyState 4) only
184+ // fires once the connection closes, so gating 'open' on DONE would mean the
185+ // event, and the fallback headers it carries, never fires during normal streaming.
186+ mockXhr . readyState = 3 ;
187+ mockXhr . status = 200 ;
188+ mockXhr . onprogress ( ) ;
189+
190+ expect ( onopen ) . toHaveBeenCalledTimes ( 1 ) ;
191+ expect ( onopen ) . toHaveBeenCalledWith (
192+ expect . objectContaining ( {
193+ type : 'open' ,
194+ headers : expect . objectContaining ( {
195+ 'x-ld-fd-fallback' : 'true' ,
196+ 'x-ld-fd-fallback-ttl' : '60' ,
197+ } ) ,
198+ } ) ,
199+ ) ;
200+ } ) ;
201+
202+ test ( 'dispatches open exactly once when onprogress precedes an onreadystatechange DONE' , ( ) => {
203+ const onopen = jest . fn ( ) ;
204+ eventSource . onopen = onopen ;
205+
206+ mockXhr . getAllResponseHeaders = jest . fn (
207+ ( ) => 'X-Ld-Fd-Fallback: true\r\nX-Ld-Fd-Fallback-Ttl: 60\r\nContent-Type: text/event-stream' ,
208+ ) ;
209+ mockXhr . responseText = '' ;
210+
211+ jest . runAllTimers ( ) ;
212+
213+ // onprogress observes the connection first (LOADING) and transitions to OPEN.
214+ mockXhr . readyState = 3 ;
215+ mockXhr . status = 200 ;
216+ mockXhr . onprogress ( ) ;
217+
218+ // A later onreadystatechange at DONE must not re-dispatch open: the status is
219+ // already OPEN (no longer CONNECTING), so its open-dispatch guard is a no-op.
220+ mockXhr . readyState = 4 ;
221+ mockXhr . status = 200 ;
222+ mockXhr . onreadystatechange ( ) ;
223+
224+ expect ( onopen ) . toHaveBeenCalledTimes ( 1 ) ;
225+ } ) ;
170226} ) ;
0 commit comments