Skip to content

Commit 5de4071

Browse files
committed
Only keep the slowest LoAF entries
1 parent 182bfdb commit 5de4071

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

src/metric/LoAF.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,17 @@ export function getData(config: UserConfig): LoAFSummary {
7979
totalEntries: entries.length,
8080
totalStyleAndLayoutDuration: floor(totalStyleAndLayoutDuration),
8181
totalWorkDuration: floor(totalWorkDuration),
82-
entries: summarizedEntries.slice(0, config.maxAttributionEntries),
82+
8383
scripts: summarizeLoAFScripts(
8484
entries.flatMap((entry) => entry.scripts),
8585
config,
8686
),
87+
88+
// Only keep the slowest LoAF entries
89+
entries: summarizedEntries
90+
.sort((a, b) => b.duration - a.duration)
91+
.slice(0, config.maxAttributionEntries)
92+
.sort((a, b) => a.startTime - b.startTime),
8793
};
8894
}
8995

tests/integration/post-beacon/loaf.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,58 @@ test.describe("POST beacon LoAF", () => {
8080
}
8181
});
8282

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+
83135
test("LoAFs are collected as INP attribution", async ({ page }) => {
84136
const luxRequests = new RequestInterceptor(page).createRequestMatcher("/store/");
85137
await page.goto("/long-animation-frames.html", { waitUntil: "networkidle" });

tests/test-pages/long-animation-frames.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ <h1>LUX long animation frames test page</h1>
1313

1414
<img src="eve.jpg" elementtiming="eve-image">
1515

16+
<input type="text" id="long-task-duration" value="50">
1617
<button id="create-long-task" type="button">Make a delayed paint</button>
1718

1819
<div style="margin-top: 2000px;">
@@ -21,12 +22,15 @@ <h1>LUX long animation frames test page</h1>
2122

2223
<script src="app.js"></script>
2324
<script>
25+
const durationInput = document.getElementById("long-task-duration");
2426
document.getElementById("create-long-task").addEventListener("click", globalClickHandler);
2527

2628
function globalClickHandler() {
29+
const duration = Number(durationInput.value) || 50;
2730
const script = document.createElement("script");
2831
script.src = "external-long-task.js";
29-
script.onload = () => externalLongTask(50);
32+
script.onload = () => externalLongTask(duration);
33+
3034
document.body.appendChild(script);
3135

3236
const firstImage = document.querySelector("img");

0 commit comments

Comments
 (0)