@@ -377,4 +377,114 @@ describe('Fetch Ajax', () => {
377377 expect ( ajaxMetric . metrics . rxSize . t ) . toBeUndefined ( )
378378 expect ( ajaxMetric . metrics . rxSize . c ) . toEqual ( 1 )
379379 } )
380+
381+ describe ( 'response size fallback from payload capture' , ( ) => {
382+ it ( 'uses captured payload size when content-length header is missing' , async ( ) => {
383+ await browser . url ( await browser . testHandle . assetURL ( 'ajax/fetch-no-content-length.html' , {
384+ init : { ajax : { capture_payloads : 'all' } } ,
385+ loader : 'full'
386+ } ) )
387+ . then ( ( ) => browser . waitForAgentLoad ( ) )
388+ . then ( ( ) => browser . execute ( function ( ) {
389+ window . disableAjaxHashChange = true
390+ } ) )
391+
392+ const [ ajaxEventsHarvest , ajaxMetricsHarvest ] = await Promise . all ( [
393+ ajaxEventsCapture . waitForResult ( { timeout : 10000 } ) ,
394+ ajaxMetricsCapture . waitForResult ( { timeout : 10000 } ) ,
395+ $ ( '#sendAjax' ) . click ( )
396+ ] )
397+
398+ ajaxEventsHarvest . forEach ( harvest =>
399+ checkAjaxEvents ( harvest . request , { specificPath : '/json-no-content-length' } )
400+ )
401+ ajaxMetricsHarvest . forEach ( harvest =>
402+ checkAjaxMetrics ( harvest . request , { specificPath : '/json-no-content-length' , isFetch : true } )
403+ )
404+
405+ const ajaxEvent = ajaxEventsHarvest
406+ . flatMap ( harvest => harvest . request . body )
407+ . find ( event => event . path === '/json-no-content-length' )
408+ expect ( ajaxEvent . responseBodySize ) . toBeGreaterThan ( 0 )
409+
410+ const ajaxMetric = ajaxMetricsHarvest
411+ . flatMap ( harvest => harvest . request . body . xhr )
412+ . find ( metric => metric . params . pathname === '/json-no-content-length' )
413+ expect ( ajaxMetric . metrics . rxSize . t ) . toBeGreaterThan ( 0 )
414+ // Verify the size matches the expected JSON response body
415+ expect ( ajaxMetric . metrics . rxSize . t ) . toEqual ( JSON . stringify ( { text : 'response without content-length header' } ) . length )
416+ } )
417+
418+ it ( 'uses captured payload size when content-length is 0 with actual body' , async ( ) => {
419+ await browser . url ( await browser . testHandle . assetURL ( 'ajax/fetch-zero-content-length.html' , {
420+ init : { ajax : { capture_payloads : 'all' } } ,
421+ loader : 'full'
422+ } ) )
423+ . then ( ( ) => browser . waitForAgentLoad ( ) )
424+ . then ( ( ) => browser . execute ( function ( ) {
425+ window . disableAjaxHashChange = true
426+ } ) )
427+
428+ const [ ajaxEventsHarvest , ajaxMetricsHarvest ] = await Promise . all ( [
429+ ajaxEventsCapture . waitForResult ( { timeout : 10000 } ) ,
430+ ajaxMetricsCapture . waitForResult ( { timeout : 10000 } ) ,
431+ $ ( '#sendAjax' ) . click ( )
432+ ] )
433+
434+ ajaxEventsHarvest . forEach ( harvest =>
435+ checkAjaxEvents ( harvest . request , { specificPath : '/json-zero-content-length' } )
436+ )
437+ ajaxMetricsHarvest . forEach ( harvest =>
438+ checkAjaxMetrics ( harvest . request , { specificPath : '/json-zero-content-length' , isFetch : true } )
439+ )
440+
441+ const ajaxEvent = ajaxEventsHarvest
442+ . flatMap ( harvest => harvest . request . body )
443+ . find ( event => event . path === '/json-zero-content-length' )
444+ expect ( ajaxEvent . responseBodySize ) . toBeGreaterThan ( 0 )
445+
446+ const ajaxMetric = ajaxMetricsHarvest
447+ . flatMap ( harvest => harvest . request . body . xhr )
448+ . find ( metric => metric . params . pathname === '/json-zero-content-length' )
449+ expect ( ajaxMetric . metrics . rxSize . t ) . toBeGreaterThan ( 0 )
450+ // Verify the size matches the expected JSON response body
451+ expect ( ajaxMetric . metrics . rxSize . t ) . toEqual ( JSON . stringify ( { text : 'content-length says 0 but body exists' } ) . length )
452+ } )
453+
454+ it ( 'reports rxSize as 0 for empty 204 No Content response' , async ( ) => {
455+ await browser . url ( await browser . testHandle . assetURL ( 'ajax/fetch-empty-204.html' , {
456+ init : { ajax : { capture_payloads : 'all' } } ,
457+ loader : 'full'
458+ } ) )
459+ . then ( ( ) => browser . waitForAgentLoad ( ) )
460+ . then ( ( ) => browser . execute ( function ( ) {
461+ window . disableAjaxHashChange = true
462+ } ) )
463+
464+ const [ ajaxEventsHarvest , ajaxMetricsHarvest ] = await Promise . all ( [
465+ ajaxEventsCapture . waitForResult ( { timeout : 10000 } ) ,
466+ ajaxMetricsCapture . waitForResult ( { timeout : 10000 } ) ,
467+ $ ( '#sendAjax' ) . click ( )
468+ ] )
469+
470+ ajaxEventsHarvest . forEach ( harvest =>
471+ checkAjaxEvents ( harvest . request , { specificPath : '/empty-204' } )
472+ )
473+ ajaxMetricsHarvest . forEach ( harvest =>
474+ checkAjaxMetrics ( harvest . request , { specificPath : '/empty-204' , isFetch : true } )
475+ )
476+
477+ const ajaxEvent = ajaxEventsHarvest
478+ . flatMap ( harvest => harvest . request . body )
479+ . find ( event => event . path === '/empty-204' )
480+ expect ( ajaxEvent . responseBodySize ) . toEqual ( 0 )
481+ expect ( ajaxEvent . status ) . toEqual ( 204 )
482+
483+ const ajaxMetric = ajaxMetricsHarvest
484+ . flatMap ( harvest => harvest . request . body . xhr )
485+ . find ( metric => metric . params . pathname === '/empty-204' )
486+ expect ( ajaxMetric . metrics . rxSize . t ) . toEqual ( 0 )
487+ expect ( ajaxMetric . params . status ) . toEqual ( 204 )
488+ } )
489+ } )
380490} )
0 commit comments