@@ -25,6 +25,113 @@ test.describe("POST beacon LoAF", () => {
2525 }
2626 } ) ;
2727
28+ test ( "LoAFs are reset between SPA page transitions" , async ( { page } ) => {
29+ const loafSupported = await entryTypeSupported ( page , "long-animation-frame" ) ;
30+ const luxRequests = new RequestInterceptor ( page ) . createRequestMatcher ( "/store/" ) ;
31+ await page . goto ( "/long-animation-frames.html?injectScript=LUX.auto=false;" , {
32+ waitUntil : "networkidle" ,
33+ } ) ;
34+ await luxRequests . waitForMatchingRequest ( ( ) => page . evaluate ( ( ) => LUX . send ( ) ) ) ;
35+ let b = luxRequests . get ( 0 ) ! . postDataJSON ( ) as BeaconPayload ;
36+
37+ // First beacon has LoAFs
38+ if ( loafSupported ) {
39+ const loaf = b . loaf ! ;
40+ expect ( loaf . totalBlockingDuration ) . toBeGreaterThan ( 0 ) ;
41+ expect ( loaf . totalDuration ) . toBeGreaterThan ( 0 ) ;
42+ expect ( loaf . totalEntries ) . toBeGreaterThan ( 0 ) ;
43+ expect ( loaf . totalStyleAndLayoutDuration ) . toBeGreaterThan ( 0 ) ;
44+ expect ( loaf . totalWorkDuration ) . toBeGreaterThan ( 0 ) ;
45+ expect ( loaf . entries . length ) . toBeGreaterThan ( 0 ) ;
46+ expect ( loaf . scripts . length ) . toBeGreaterThan ( 0 ) ;
47+ } else {
48+ expect ( b . loaf ) . toBeUndefined ( ) ;
49+ }
50+
51+ // Second beacon has no LoAFs
52+ await page . evaluate ( ( ) => LUX . init ( ) ) ;
53+ await page . waitForTimeout ( 200 ) ;
54+ await luxRequests . waitForMatchingRequest ( ( ) => page . evaluate ( ( ) => LUX . send ( ) ) ) ;
55+ b = luxRequests . get ( 1 ) ! . postDataJSON ( ) as BeaconPayload ;
56+
57+ if ( loafSupported ) {
58+ const loaf = b . loaf ! ;
59+ expect ( loaf . totalDuration ) . toEqual ( 0 ) ;
60+ expect ( loaf . entries . length ) . toEqual ( 0 ) ;
61+ expect ( loaf . scripts . length ) . toEqual ( 0 ) ;
62+ } else {
63+ expect ( b . loaf ) . toBeUndefined ( ) ;
64+ }
65+
66+ // Third beacon has LoAFs again
67+ await page . evaluate ( ( ) => LUX . init ( ) ) ;
68+ await page . locator ( "#create-long-task" ) . click ( ) ;
69+ await page . waitForTimeout ( 50 ) ;
70+ await luxRequests . waitForMatchingRequest ( ( ) => page . evaluate ( ( ) => LUX . send ( ) ) ) ;
71+ b = luxRequests . get ( 2 ) ! . postDataJSON ( ) as BeaconPayload ;
72+
73+ if ( loafSupported ) {
74+ const loaf = b . loaf ! ;
75+ expect ( loaf . totalDuration ) . toBeGreaterThan ( 0 ) ;
76+ expect ( loaf . entries . length ) . toBeGreaterThan ( 0 ) ;
77+ expect ( loaf . scripts . length ) . toBeGreaterThan ( 0 ) ;
78+ } else {
79+ expect ( b . loaf ) . toBeUndefined ( ) ;
80+ }
81+ } ) ;
82+
83+ test ( "Only the slowest LoAFs are collected" , async ( { page } ) => {
84+ const MAX_ENTRIES = 3 ;
85+ const loafSupported = await entryTypeSupported ( page , "long-animation-frame" ) ;
86+ const luxRequests = new RequestInterceptor ( page ) . createRequestMatcher ( "/store/" ) ;
87+ await page . goto (
88+ `/long-animation-frames.html?injectScript=LUX.maxAttributionEntries=${ MAX_ENTRIES } ;` ,
89+ {
90+ waitUntil : "networkidle" ,
91+ } ,
92+ ) ;
93+
94+ // Create a mixture of short and long LoAFs
95+ // Short
96+ await page . locator ( "#create-long-task" ) . click ( ) ;
97+ await page . locator ( "#create-long-task" ) . click ( ) ;
98+
99+ // Long
100+ await page . locator ( "#long-task-duration" ) . fill ( "100" ) ;
101+ await page . locator ( "#create-long-task" ) . click ( ) ;
102+
103+ // Short
104+ await page . locator ( "#long-task-duration" ) . fill ( "50" ) ;
105+ await page . locator ( "#create-long-task" ) . click ( ) ;
106+ await page . locator ( "#create-long-task" ) . click ( ) ;
107+
108+ // Long
109+ await page . locator ( "#long-task-duration" ) . fill ( "150" ) ;
110+ await page . locator ( "#create-long-task" ) . click ( ) ;
111+ await page . locator ( "#create-long-task" ) . click ( ) ;
112+
113+ await luxRequests . waitForMatchingRequest ( ( ) => page . evaluate ( ( ) => LUX . send ( ) ) ) ;
114+ const b = luxRequests . get ( 0 ) ! . postDataJSON ( ) as BeaconPayload ;
115+
116+ if ( loafSupported ) {
117+ const loaf = b . loaf ! ;
118+
119+ expect ( loaf . entries . length ) . toEqual ( MAX_ENTRIES ) ;
120+
121+ // The entries should all be the longer LoAFs. Note the total duration is the value from the
122+ // #long-task-duration input, plus a hard-coded 50ms long task in external-long.task.js.
123+ expect ( loaf . entries [ 0 ] . duration ) . toBeGreaterThanOrEqual ( 150 ) ;
124+ expect ( loaf . entries [ 1 ] . duration ) . toBeGreaterThanOrEqual ( 150 ) ;
125+ expect ( loaf . entries [ 2 ] . duration ) . toBeGreaterThanOrEqual ( 100 ) ;
126+
127+ // The entries should be ordered by start time
128+ expect ( loaf . entries [ 0 ] . startTime ) . toBeLessThanOrEqual ( loaf . entries [ 1 ] . startTime ) ;
129+ expect ( loaf . entries [ 1 ] . startTime ) . toBeLessThanOrEqual ( loaf . entries [ 2 ] . startTime ) ;
130+ } else {
131+ expect ( b . loaf ) . toBeUndefined ( ) ;
132+ }
133+ } ) ;
134+
28135 test ( "LoAFs are collected as INP attribution" , async ( { page } ) => {
29136 const luxRequests = new RequestInterceptor ( page ) . createRequestMatcher ( "/store/" ) ;
30137 await page . goto ( "/long-animation-frames.html" , { waitUntil : "networkidle" } ) ;
@@ -46,32 +153,37 @@ test.describe("POST beacon LoAF", () => {
46153 if ( loafSupported ) {
47154 expect ( loafScripts . length ) . toEqual ( 2 ) ;
48155
49- const external = loafScripts [ 0 ] ;
50- const externalUrl = new URL ( external . sourceUrl ) ;
51- const [ externalStartTime , externalDuration ] = external . timings [ 0 ] ;
156+ const documentScript = loafScripts . find ( ( script ) =>
157+ script . sourceUrl . endsWith ( "/long-animation-frames.html" ) ,
158+ ) ! ;
159+ const externalScript = loafScripts . find ( ( script ) =>
160+ script . sourceUrl . endsWith ( "/external-long-task.js" ) ,
161+ ) ! ;
162+
163+ const externalUrl = new URL ( externalScript . sourceUrl ) ;
164+ const [ externalStartTime , externalDuration ] = externalScript . timings [ 0 ] ;
52165 expect ( externalUrl . pathname ) . toEqual ( "/external-long-task.js" ) ;
53166 // Invoker has been removed to try and reduce the number of LoAF entries
54167 // expect(external.invoker).toEqual(external.sourceUrl);
55- expect ( external . invoker ) . toEqual ( "" ) ;
56- expect ( external . sourceFunctionName ) . toEqual ( "" ) ;
57- expect ( external . totalEntries ) . toEqual ( 1 ) ;
58- expect ( external . totalDuration ) . toBeBetween ( 49 , 59 ) ;
168+ expect ( externalScript . invoker ) . toEqual ( "" ) ;
169+ expect ( externalScript . sourceFunctionName ) . toEqual ( "" ) ;
170+ expect ( externalScript . totalEntries ) . toEqual ( 1 ) ;
171+ expect ( externalScript . totalDuration ) . toBeBetween ( 49 , 59 ) ;
59172 expect ( externalStartTime ) . toBeGreaterThanOrEqual ( inp . startTime ) ;
60173 expect ( externalDuration ) . toBeBetween ( 49 , 59 ) ;
61174
62- const onload = loafScripts [ 1 ] ;
63- const onloadUrl = new URL ( onload . sourceUrl ) ;
64- const [ onloadStartTime , onloadDuration ] = onload . timings [ 0 ] ;
175+ const documentUrl = new URL ( documentScript . sourceUrl ) ;
176+ const [ documentStartTime , documentDuration ] = documentScript . timings [ 0 ] ;
65177
66- expect ( onloadUrl . pathname ) . toEqual ( "/long-animation-frames.html" ) ;
178+ expect ( documentUrl . pathname ) . toEqual ( "/long-animation-frames.html" ) ;
67179 // Invoker has been removed to try and reduce the number of LoAF entries
68180 // expect(onload.invoker).toEqual("SCRIPT[src=external-long-task.js].onload");
69- expect ( onload . invoker ) . toEqual ( "" ) ;
70- expect ( onload . sourceFunctionName ) . toEqual ( "" ) ;
71- expect ( onload . totalEntries ) . toEqual ( 1 ) ;
72- expect ( onload . totalDuration ) . toBeBetween ( 49 , 59 ) ;
73- expect ( onloadStartTime ) . toBeGreaterThanOrEqual ( inp . startTime ) ;
74- expect ( onloadDuration ) . toBeBetween ( 49 , 59 ) ;
181+ expect ( documentScript . invoker ) . toEqual ( "" ) ;
182+ expect ( documentScript . sourceFunctionName ) . toEqual ( "" ) ;
183+ expect ( documentScript . totalEntries ) . toEqual ( 1 ) ;
184+ expect ( documentScript . totalDuration ) . toBeBetween ( 49 , 59 ) ;
185+ expect ( documentStartTime ) . toBeGreaterThanOrEqual ( inp . startTime ) ;
186+ expect ( documentDuration ) . toBeBetween ( 49 , 59 ) ;
75187 } else {
76188 expect ( loafScripts . length ) . toEqual ( 0 ) ;
77189 }
0 commit comments