Skip to content

Commit d1198dd

Browse files
committed
stash
1 parent 407e069 commit d1198dd

File tree

8 files changed

+818
-5
lines changed

8 files changed

+818
-5
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,8 @@
762762
"webpack": "5.99.9",
763763
"webpack-cli": "^6.0.1",
764764
"webpack-dev-server": "^5.2.2",
765+
"web-vitals": "^4.2.4",
766+
"web-vitals-attribution": "^0.1.0",
765767
"ws": "^8.17.1",
766768
"yaml": "^2.4.1",
767769
"yargs": "^17.7.2",

test/e2e/benchmarks/types-generated.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,31 @@ export type BenchmarkResults = {
3232
p95: StatisticalResult;
3333
};
3434

35-
/** User action result with testTitle, persona and numeric timing metrics. */
35+
/**
36+
* Core Web Vitals metrics from the web-vitals library.
37+
* INP requires actual user interactions to measure meaningful data.
38+
*/
39+
export type WebVitalsMetrics = {
40+
/** Interaction to Next Paint in milliseconds */
41+
inp: number | null;
42+
/** Largest Contentful Paint in milliseconds */
43+
lcp: number | null;
44+
/** Cumulative Layout Shift (unitless score) */
45+
cls: number | null;
46+
/** Rating for INP metric */
47+
inpRating: 'good' | 'needs-improvement' | 'poor' | null;
48+
/** Rating for LCP metric */
49+
lcpRating: 'good' | 'needs-improvement' | 'poor' | null;
50+
/** Rating for CLS metric */
51+
clsRating: 'good' | 'needs-improvement' | 'poor' | null;
52+
};
53+
54+
/** User action result with testTitle, persona, timing metrics, and Core Web Vitals. */
3655
export type UserActionResult = {
3756
testTitle: string;
3857
persona?: string;
39-
[key: string]: string | number | undefined;
58+
webVitals?: WebVitalsMetrics;
59+
[key: string]: string | number | WebVitalsMetrics | undefined;
4060
};
4161

4262
export type BenchmarkArguments = {

test/e2e/benchmarks/user-actions-benchmark.ts

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,48 @@ import {
2222
import { createInternalTransaction } from '../page-objects/flows/transaction';
2323
import { Driver } from '../webdriver/driver';
2424

25+
/**
26+
* Core Web Vitals metrics from the web-vitals library.
27+
* INP (Interaction to Next Paint) requires actual user interactions to measure.
28+
*/
29+
type WebVitalsMetrics = {
30+
inp: number | null;
31+
lcp: number | null;
32+
cls: number | null;
33+
inpRating: 'good' | 'needs-improvement' | 'poor' | null;
34+
lcpRating: 'good' | 'needs-improvement' | 'poor' | null;
35+
clsRating: 'good' | 'needs-improvement' | 'poor' | null;
36+
};
37+
38+
/**
39+
* Collect Core Web Vitals from stateHooks.
40+
* Must be called after user interactions for INP to have meaningful data.
41+
*/
42+
async function collectWebVitals(driver: Driver): Promise<WebVitalsMetrics> {
43+
return await driver.executeScript(() => {
44+
const stateHooks = (
45+
window as Window & {
46+
stateHooks?: {
47+
getWebVitalsMetrics?: () => WebVitalsMetrics;
48+
};
49+
}
50+
).stateHooks;
51+
52+
if (stateHooks?.getWebVitalsMetrics) {
53+
return stateHooks.getWebVitalsMetrics();
54+
}
55+
56+
return {
57+
inp: null,
58+
lcp: null,
59+
cls: null,
60+
inpRating: null,
61+
lcpRating: null,
62+
clsRating: null,
63+
};
64+
});
65+
}
66+
2567
async function mockTokensEthereum(mockServer: Mockttp) {
2668
return await mockServer.forPost(/getTokens\/search/u).thenCallback(() => {
2769
return {
@@ -47,8 +89,17 @@ async function loadNewAccount(): Promise<{
4789
duration: number;
4890
testTitle: string;
4991
persona: string;
92+
webVitals: WebVitalsMetrics;
5093
}> {
5194
let loadingTimes: number = 0;
95+
let webVitals: WebVitalsMetrics = {
96+
inp: null,
97+
lcp: null,
98+
cls: null,
99+
inpRating: null,
100+
lcpRating: null,
101+
clsRating: null,
102+
};
52103
const testTitle = 'benchmark-userActions-loadNewAccount';
53104

54105
await withFixtures(
@@ -73,17 +124,34 @@ async function loadNewAccount(): Promise<{
73124
const timestampAfterAction = new Date();
74125
loadingTimes =
75126
timestampAfterAction.getTime() - timestampBeforeAction.getTime();
127+
128+
// Collect Core Web Vitals after user interactions
129+
webVitals = await collectWebVitals(driver);
76130
},
77131
);
78-
return { duration: loadingTimes, testTitle, persona: USER_ACTIONS_PERSONA };
132+
return {
133+
duration: loadingTimes,
134+
testTitle,
135+
persona: USER_ACTIONS_PERSONA,
136+
webVitals,
137+
};
79138
}
80139

81140
async function confirmTx(): Promise<{
82141
duration: number;
83142
testTitle: string;
84143
persona: string;
144+
webVitals: WebVitalsMetrics;
85145
}> {
86146
let loadingTimes: number = 0;
147+
let webVitals: WebVitalsMetrics = {
148+
inp: null,
149+
lcp: null,
150+
cls: null,
151+
inpRating: null,
152+
lcpRating: null,
153+
clsRating: null,
154+
};
87155
const testTitle = 'benchmark-userActions-confirmTx';
88156

89157
await withFixtures(
@@ -119,9 +187,17 @@ async function confirmTx(): Promise<{
119187
const timestampAfterAction = new Date();
120188
loadingTimes =
121189
timestampAfterAction.getTime() - timestampBeforeAction.getTime();
190+
191+
// Collect Core Web Vitals after user interactions
192+
webVitals = await collectWebVitals(driver);
122193
},
123194
);
124-
return { duration: loadingTimes, testTitle, persona: USER_ACTIONS_PERSONA };
195+
return {
196+
duration: loadingTimes,
197+
testTitle,
198+
persona: USER_ACTIONS_PERSONA,
199+
webVitals,
200+
};
125201
}
126202

127203
async function bridgeUserActions(): Promise<{
@@ -130,10 +206,19 @@ async function bridgeUserActions(): Promise<{
130206
searchToken: number;
131207
testTitle: string;
132208
persona: string;
209+
webVitals: WebVitalsMetrics;
133210
}> {
134211
let loadPage: number = 0;
135212
let loadAssetPicker: number = 0;
136213
let searchToken: number = 0;
214+
let webVitals: WebVitalsMetrics = {
215+
inp: null,
216+
lcp: null,
217+
cls: null,
218+
inpRating: null,
219+
lcpRating: null,
220+
clsRating: null,
221+
};
137222
const testTitle = 'benchmark-userActions-bridgeUserActions';
138223

139224
const fixtureBuilder = new FixtureBuilder()
@@ -184,6 +269,9 @@ async function bridgeUserActions(): Promise<{
184269
searchToken =
185270
timestampAferTokenSearch.getTime() -
186271
timestampBeforeTokenSearch.getTime();
272+
273+
// Collect Core Web Vitals after user interactions
274+
webVitals = await collectWebVitals(driver);
187275
},
188276
);
189277
return {
@@ -192,6 +280,7 @@ async function bridgeUserActions(): Promise<{
192280
searchToken,
193281
testTitle,
194282
persona: USER_ACTIONS_PERSONA,
283+
webVitals,
195284
};
196285
}
197286

@@ -208,7 +297,10 @@ async function main(): Promise<void> {
208297
}),
209298
);
210299

211-
const results: Record<string, Record<string, string | number>> = {};
300+
const results: Record<
301+
string,
302+
Record<string, string | number | WebVitalsMetrics | null>
303+
> = {};
212304

213305
results.loadNewAccount = await loadNewAccount();
214306
results.confirmTx = await confirmTx();

types/global.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,29 @@ type StateHooks = {
289289
* tests.
290290
*/
291291
reloadExtension?: () => void;
292+
293+
/**
294+
* Initialize Core Web Vitals observers (INP, LCP, CLS).
295+
* @see ui/helpers/utils/web-vitals.ts
296+
*/
297+
initWebVitals?: () => void;
298+
/**
299+
* Get current Core Web Vitals metrics.
300+
* Returns stored INP, LCP, and CLS values with their ratings.
301+
*/
302+
getWebVitalsMetrics?: () => {
303+
inp: number | null;
304+
lcp: number | null;
305+
cls: number | null;
306+
inpRating: 'good' | 'needs-improvement' | 'poor' | null;
307+
lcpRating: 'good' | 'needs-improvement' | 'poor' | null;
308+
clsRating: 'good' | 'needs-improvement' | 'poor' | null;
309+
};
310+
/**
311+
* Reset Core Web Vitals metrics to initial null state.
312+
* Useful for clearing metrics between benchmark runs.
313+
*/
314+
resetWebVitalsMetrics?: () => void;
292315
};
293316

294317
export declare global {

0 commit comments

Comments
 (0)