@@ -8,25 +8,51 @@ import * as babelPresetEnv from '@babel/preset-env';
8
8
import * as babelPresetReact from '@babel/preset-react' ;
9
9
10
10
type PerformanceResults = {
11
- renderTime : number ;
11
+ renderTime : number [ ] ;
12
12
webVitals : {
13
- cls : number ;
14
- lcp : number ;
15
- inp : number ;
16
- fid : number ;
17
- ttfb : number ;
13
+ cls : number [ ] ;
14
+ lcp : number [ ] ;
15
+ inp : number [ ] ;
16
+ fid : number [ ] ;
17
+ ttfb : number [ ] ;
18
18
} ;
19
19
reactProfiler : {
20
- id : number ;
21
- phase : number ;
22
- actualDuration : number ;
23
- baseDuration : number ;
24
- startTime : number ;
25
- commitTime : number ;
20
+ id : number [ ] ;
21
+ phase : number [ ] ;
22
+ actualDuration : number [ ] ;
23
+ baseDuration : number [ ] ;
24
+ startTime : number [ ] ;
25
+ commitTime : number [ ] ;
26
26
} ;
27
27
error : Error | null ;
28
28
} ;
29
29
30
+ type EvaluationResults = {
31
+ renderTime : number | null ;
32
+ webVitals : {
33
+ cls : number | null ;
34
+ lcp : number | null ;
35
+ inp : number | null ;
36
+ fid : number | null ;
37
+ ttfb : number | null ;
38
+ } ;
39
+ reactProfiler : {
40
+ id : number | null ;
41
+ phase : number | null ;
42
+ actualDuration : number | null ;
43
+ baseDuration : number | null ;
44
+ startTime : number | null ;
45
+ commitTime : number | null ;
46
+ } ;
47
+ error : Error | null ;
48
+ } ;
49
+
50
+ function delay ( time : number ) {
51
+ return new Promise ( function ( resolve ) {
52
+ setTimeout ( resolve , time ) ;
53
+ } ) ;
54
+ }
55
+
30
56
export async function measurePerformance (
31
57
code : string ,
32
58
iterations : number ,
@@ -72,21 +98,21 @@ export async function measurePerformance(
72
98
const html = buildHtml ( transpiled ) ;
73
99
74
100
let performanceResults : PerformanceResults = {
75
- renderTime : 0 ,
101
+ renderTime : [ ] ,
76
102
webVitals : {
77
- cls : 0 ,
78
- lcp : 0 ,
79
- inp : 0 ,
80
- fid : 0 ,
81
- ttfb : 0 ,
103
+ cls : [ ] ,
104
+ lcp : [ ] ,
105
+ inp : [ ] ,
106
+ fid : [ ] ,
107
+ ttfb : [ ] ,
82
108
} ,
83
109
reactProfiler : {
84
- id : 0 ,
85
- phase : 0 ,
86
- actualDuration : 0 ,
87
- baseDuration : 0 ,
88
- startTime : 0 ,
89
- commitTime : 0 ,
110
+ id : [ ] ,
111
+ phase : [ ] ,
112
+ actualDuration : [ ] ,
113
+ baseDuration : [ ] ,
114
+ startTime : [ ] ,
115
+ commitTime : [ ] ,
90
116
} ,
91
117
error : null ,
92
118
} ;
@@ -96,38 +122,73 @@ export async function measurePerformance(
96
122
await page . waitForFunction (
97
123
'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)' ,
98
124
) ;
125
+
99
126
// ui chaos monkey
100
- await page . waitForFunction ( `window.__RESULT__ !== undefined && (function() {
101
- for (const el of [...document.querySelectorAll('a'), ...document.querySelectorAll('button')]) {
102
- console.log(el);
103
- el.click();
127
+ const selectors = await page . evaluate ( ( ) => {
128
+ window . __INTERACTABLE_SELECTORS__ = [ ] ;
129
+ const elements = Array . from ( document . querySelectorAll ( 'a' ) ) . concat (
130
+ Array . from ( document . querySelectorAll ( 'button' ) ) ,
131
+ ) ;
132
+ for ( const el of elements ) {
133
+ window . __INTERACTABLE_SELECTORS__ . push ( el . tagName . toLowerCase ( ) ) ;
104
134
}
105
- return true;
106
- })() ` ) ;
107
- const evaluationResult : PerformanceResults = await page . evaluate ( ( ) => {
135
+ return window . __INTERACTABLE_SELECTORS__ ;
136
+ } ) ;
137
+
138
+ await Promise . all (
139
+ selectors . map ( async ( selector : string ) => {
140
+ try {
141
+ await page . click ( selector ) ;
142
+ } catch ( e ) {
143
+ console . log ( `warning: Could not click ${ selector } : ${ e . message } ` ) ;
144
+ }
145
+ } ) ,
146
+ ) ;
147
+ await delay ( 500 ) ;
148
+
149
+ // Visit a new page for 1s to background the current page so that WebVitals can finish being calculated
150
+ const tempPage = await browser . newPage ( ) ;
151
+ await tempPage . evaluate ( ( ) => {
152
+ return new Promise ( resolve => {
153
+ setTimeout ( ( ) => {
154
+ resolve ( true ) ;
155
+ } , 1000 ) ;
156
+ } ) ;
157
+ } ) ;
158
+ await tempPage . close ( ) ;
159
+
160
+ const evaluationResult : EvaluationResults = await page . evaluate ( ( ) => {
108
161
return ( window as any ) . __RESULT__ ;
109
162
} ) ;
110
163
111
- // TODO: investigate why webvital metrics are not populating correctly
112
- performanceResults . renderTime += evaluationResult . renderTime ;
113
- performanceResults . webVitals . cls += evaluationResult . webVitals . cls || 0 ;
114
- performanceResults . webVitals . lcp += evaluationResult . webVitals . lcp || 0 ;
115
- performanceResults . webVitals . inp += evaluationResult . webVitals . inp || 0 ;
116
- performanceResults . webVitals . fid += evaluationResult . webVitals . fid || 0 ;
117
- performanceResults . webVitals . ttfb += evaluationResult . webVitals . ttfb || 0 ;
118
-
119
- performanceResults . reactProfiler . id +=
120
- evaluationResult . reactProfiler . actualDuration || 0 ;
121
- performanceResults . reactProfiler . phase +=
122
- evaluationResult . reactProfiler . phase || 0 ;
123
- performanceResults . reactProfiler . actualDuration +=
124
- evaluationResult . reactProfiler . actualDuration || 0 ;
125
- performanceResults . reactProfiler . baseDuration +=
126
- evaluationResult . reactProfiler . baseDuration || 0 ;
127
- performanceResults . reactProfiler . startTime +=
128
- evaluationResult . reactProfiler . startTime || 0 ;
129
- performanceResults . reactProfiler . commitTime +=
130
- evaluationResult . reactProfiler . commitTime || 0 ;
164
+ if ( evaluationResult . renderTime !== null ) {
165
+ performanceResults . renderTime . push ( evaluationResult . renderTime ) ;
166
+ }
167
+
168
+ const webVitalMetrics = [ 'cls' , 'lcp' , 'inp' , 'fid' , 'ttfb' ] as const ;
169
+ for ( const metric of webVitalMetrics ) {
170
+ if ( evaluationResult . webVitals [ metric ] !== null ) {
171
+ performanceResults . webVitals [ metric ] . push (
172
+ evaluationResult . webVitals [ metric ] ,
173
+ ) ;
174
+ }
175
+ }
176
+
177
+ const profilerMetrics = [
178
+ 'id' ,
179
+ 'phase' ,
180
+ 'actualDuration' ,
181
+ 'baseDuration' ,
182
+ 'startTime' ,
183
+ 'commitTime' ,
184
+ ] as const ;
185
+ for ( const metric of profilerMetrics ) {
186
+ if ( evaluationResult . reactProfiler [ metric ] !== null ) {
187
+ performanceResults . reactProfiler [ metric ] . push (
188
+ evaluationResult . reactProfiler [ metric ] ,
189
+ ) ;
190
+ }
191
+ }
131
192
132
193
performanceResults . error = evaluationResult . error ;
133
194
}
@@ -159,14 +220,14 @@ function buildHtml(transpiled: string) {
159
220
renderTime: null,
160
221
webVitals: {},
161
222
reactProfiler: {},
162
- error: null
223
+ error: null,
163
224
};
164
225
165
- webVitals.onCLS((metric ) => { window.__RESULT__.webVitals.cls = metric ; });
166
- webVitals.onLCP((metric ) => { window.__RESULT__.webVitals.lcp = metric ; });
167
- webVitals.onINP((metric ) => { window.__RESULT__.webVitals.inp = metric ; });
168
- webVitals.onFID((metric ) => { window.__RESULT__.webVitals.fid = metric ; });
169
- webVitals.onTTFB((metric ) => { window.__RESULT__.webVitals.ttfb = metric ; });
226
+ webVitals.onCLS(({value} ) => { window.__RESULT__.webVitals.cls = value ; });
227
+ webVitals.onLCP(({value} ) => { window.__RESULT__.webVitals.lcp = value ; });
228
+ webVitals.onINP(({value} ) => { window.__RESULT__.webVitals.inp = value ; });
229
+ webVitals.onFID(({value} ) => { window.__RESULT__.webVitals.fid = value ; });
230
+ webVitals.onTTFB(({value} ) => { window.__RESULT__.webVitals.ttfb = value ; });
170
231
171
232
try {
172
233
${ transpiled }
0 commit comments