@@ -857,6 +857,61 @@ test('should not emit after w/o before', async ({ browserType, mode }, testInfo)
857857 expect ( call2after ) . toBe ( call2before ) ;
858858} ) ;
859859
860+ test ( 'should save trace while a WebSocket keeps streaming frames' , {
861+ annotation : { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41351' }
862+ } , async ( { context, page, server } , testInfo ) => {
863+ let streaming = true ;
864+ server . onceWebSocketConnection ( ws => {
865+ const timer = setInterval ( ( ) => {
866+ if ( streaming && ws . readyState === ws . OPEN )
867+ ws . send ( 'x' . repeat ( 16 * 1024 ) , ( ) => { } ) ;
868+ } , 1 ) ;
869+ const stop = ( ) => clearInterval ( timer ) ;
870+ ws . on ( 'close' , stop ) ;
871+ ws . on ( 'error' , stop ) ;
872+ } ) ;
873+
874+ await context . tracing . start ( { snapshots : true } ) ;
875+
876+ await context . tracing . startChunk ( ) ;
877+ await page . goto ( server . EMPTY_PAGE ) ;
878+ await page . evaluate ( url => {
879+ ( window as any ) . ws = new WebSocket ( url ) ;
880+ return new Promise < void > ( resolve => ( window as any ) . ws . addEventListener ( 'open' , ( ) => resolve ( ) ) ) ;
881+ } , `ws://${ server . HOST } /ws` ) ;
882+ await page . waitForTimeout ( 100 ) ;
883+ const tracePath1 = testInfo . outputPath ( 'trace1.zip' ) ;
884+ await context . tracing . stopChunk ( { path : tracePath1 } ) ;
885+
886+ streaming = false ;
887+ await context . tracing . startChunk ( ) ;
888+ await page . waitForTimeout ( 100 ) ;
889+ const tracePath2 = testInfo . outputPath ( 'trace2.zip' ) ;
890+ await context . tracing . stopChunk ( { path : tracePath2 } ) ;
891+
892+ await page . evaluate ( ( ) => new Promise < void > ( resolve => {
893+ const ws = ( window as any ) . ws as WebSocket ;
894+ if ( ws . readyState === WebSocket . CLOSED ) {
895+ resolve ( ) ;
896+ return ;
897+ }
898+ ws . addEventListener ( 'close' , ( ) => resolve ( ) , { once : true } ) ;
899+ ws . close ( ) ;
900+ } ) ) ;
901+
902+ const webSocketLines = await Promise . all ( [ tracePath1 , tracePath2 ] . map ( async path => {
903+ const { resources } = await parseTraceRaw ( path ) ;
904+ const websocketResource = Array . from ( resources ) . find ( ( [ name , buffer ] ) => name . endsWith ( '.jsonl' ) ) ! ;
905+ const lines = websocketResource [ 1 ] . toString ( ) . split ( '\n' ) . filter ( Boolean ) ;
906+ expect ( lines . length ) . toBeGreaterThan ( 0 ) ;
907+ for ( const line of lines )
908+ expect ( ( ) => JSON . parse ( line ) ) . not . toThrow ( ) ;
909+ return { path, lines } ;
910+ } ) ) ;
911+ expect ( webSocketLines [ 0 ] . path ) . not . toEqual ( webSocketLines [ 1 ] . path ) ;
912+ expect ( webSocketLines [ 1 ] . lines ) . toEqual ( webSocketLines [ 1 ] . lines ) ;
913+ } ) ;
914+
860915function expectRed ( pixels : Buffer , offset : number ) {
861916 const r = pixels . readUInt8 ( offset ) ;
862917 const g = pixels . readUInt8 ( offset + 1 ) ;
0 commit comments