@@ -2,6 +2,50 @@ import { test, expect } from '@playwright/test'
22import { createTest } from '../lib/framework'
33
44test . describe ( 'transport' , ( ) => {
5+ test . describe ( 'batch flushing on beforeunload' , ( ) => {
6+ createTest ( 'use sendBeacon for a batch flushed by bytes_limit during beforeunload' )
7+ // This test reproduces a bug where a batch near the size limit is flushed using fetch
8+ // instead of sendBeacon when the beforeunload event fires.
9+ //
10+ // Scenario:
11+ // 1. A batch is almost full (close to RECOMMENDED_REQUEST_BYTES_LIMIT = 16 KiB)
12+ // 2. beforeunload fires and triggers a final view update
13+ // 3. Adding the view update exceeds the limit → the batch is flushed due to bytes_limit
14+ // 4. BUG: this flush uses fetch instead of sendBeacon, so it may be cancelled during unload
15+ // 5. A new batch is created for the view update and flushed via sendBeacon
16+ //
17+ // Expected: the bytes_limit flush should use sendBeacon when triggered in the context of a
18+ // page exit, so that it is not cancelled by the browser.
19+ . withRum ( { telemetrySampleRate : 0 } )
20+ . run ( async ( { page, flushEvents, intakeRegistry } ) => {
21+ // Fill the batch close to the 16 KiB limit using a custom action. The action name is sized
22+ // so that action event is almost at the limit 16KB limit → no flush yet.
23+ await page . evaluate ( ( ) => {
24+ window . DD_RUM ! . addAction ( 'x' . repeat ( 15000 ) )
25+ } )
26+
27+ // Navigating away fires beforeunload, which triggers a final view update. Adding the view
28+ // update (~2KB) to the near-full batch tips it over the limit and causes a bytes_limit
29+ // flush, which the SDK issues via fetch — and that fetch gets cancelled on page unload.
30+ await flushEvents ( )
31+
32+ // We expect two last RUM batches:
33+ // 1. The near-full batch containing the large action, flushed due to bytes_limit
34+ // 2. The final view update batch, flushed due to beforeunload
35+ const [ penultimateBatch , finalBatch ] = intakeRegistry . rumRequests . slice ( - 2 )
36+
37+ // The action event should be present in one of the batches
38+ expect (
39+ penultimateBatch . events . some ( ( e ) => e . type === 'action' ) || finalBatch . events . some ( ( e ) => e . type === 'action' )
40+ ) . toBe ( true )
41+
42+ // Both batches should use sendBeacon so they are not cancelled during page unload.
43+ // With the bug, penultimateBatch.transport is 'fetch' instead of 'beacon'.
44+ expect ( penultimateBatch . transport ) . toBe ( 'beacon' )
45+ expect ( finalBatch . transport ) . toBe ( 'beacon' )
46+ } )
47+ } )
48+
549 test . describe ( 'data compression' , ( ) => {
650 createTest ( 'send RUM data compressed' )
751 . withRum ( {
0 commit comments