1
1
import * as babel from '@babel/core' ;
2
2
import puppeteer from 'puppeteer' ;
3
3
4
- export async function measurePerformance ( code : string ) {
4
+ type PerformanceResults = {
5
+ renderTime : number ;
6
+ webVitals : {
7
+ cls : number ;
8
+ lcp : number ;
9
+ inp : number ;
10
+ fid : number ;
11
+ ttfb : number ;
12
+ } ;
13
+ reactProfiler : {
14
+ id : number ;
15
+ phase : number ;
16
+ actualDuration : number ;
17
+ baseDuration : number ;
18
+ startTime : number ;
19
+ commitTime : number ;
20
+ } ;
21
+ error : Error | null ;
22
+ } ;
23
+
24
+ export async function measurePerformance (
25
+ code : string ,
26
+ iterations : number ,
27
+ ) : Promise < PerformanceResults > {
5
28
const babelOptions = {
6
29
configFile : false ,
7
30
babelrc : false ,
@@ -42,23 +65,77 @@ export async function measurePerformance(code: string) {
42
65
throw new Error ( 'Failed to transpile code' ) ;
43
66
}
44
67
45
- const browser = await puppeteer . launch ( ) ;
46
-
68
+ const browser = await puppeteer . launch ( { headless : false } ) ;
47
69
const page = await browser . newPage ( ) ;
48
70
await page . setViewport ( { width : 1280 , height : 720 } ) ;
49
71
const html = buildHtml ( transpiled ) ;
50
- await page . setContent ( html , { waitUntil : 'networkidle0' } ) ;
51
72
52
- await page . waitForFunction (
53
- 'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)' ,
54
- ) ;
73
+ let performanceResults : PerformanceResults = {
74
+ renderTime : 0 ,
75
+ webVitals : {
76
+ cls : 0 ,
77
+ lcp : 0 ,
78
+ inp : 0 ,
79
+ fid : 0 ,
80
+ ttfb : 0 ,
81
+ } ,
82
+ reactProfiler : {
83
+ id : 0 ,
84
+ phase : 0 ,
85
+ actualDuration : 0 ,
86
+ baseDuration : 0 ,
87
+ startTime : 0 ,
88
+ commitTime : 0 ,
89
+ } ,
90
+ error : null ,
91
+ } ;
55
92
56
- const result = await page . evaluate ( ( ) => {
57
- return ( window as any ) . __RESULT__ ;
58
- } ) ;
93
+ for ( let ii = 0 ; ii < iterations ; ii ++ ) {
94
+ await page . setContent ( html , { waitUntil : 'networkidle0' } ) ;
95
+ await page . waitForFunction (
96
+ 'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)' ,
97
+ ) ;
98
+ // ui chaos monkey
99
+ await page . waitForFunction ( `window.__RESULT__ !== undefined && (function() {
100
+ for (const el of [...document.querySelectorAll('a'), ...document.querySelectorAll('button')]) {
101
+ console.log(el);
102
+ el.click();
103
+ }
104
+ return true;
105
+ })() ` ) ;
106
+ const evaluationResult : PerformanceResults = await page . evaluate ( ( ) => {
107
+ return ( window as any ) . __RESULT__ ;
108
+ } ) ;
109
+
110
+ console . error ( JSON . stringify ( evaluationResult , null , 2 ) ) ;
111
+
112
+ // TODO: investigate why webvital metrics are not populating correctly
113
+ performanceResults . renderTime += evaluationResult . renderTime ;
114
+ performanceResults . webVitals . cls += evaluationResult . webVitals . cls || 0 ;
115
+ performanceResults . webVitals . lcp += evaluationResult . webVitals . lcp || 0 ;
116
+ performanceResults . webVitals . inp += evaluationResult . webVitals . inp || 0 ;
117
+ performanceResults . webVitals . fid += evaluationResult . webVitals . fid || 0 ;
118
+ performanceResults . webVitals . ttfb += evaluationResult . webVitals . ttfb || 0 ;
119
+
120
+ performanceResults . reactProfiler . id +=
121
+ evaluationResult . reactProfiler . actualDuration || 0 ;
122
+ performanceResults . reactProfiler . phase +=
123
+ evaluationResult . reactProfiler . phase || 0 ;
124
+ performanceResults . reactProfiler . actualDuration +=
125
+ evaluationResult . reactProfiler . actualDuration || 0 ;
126
+ performanceResults . reactProfiler . baseDuration +=
127
+ evaluationResult . reactProfiler . baseDuration || 0 ;
128
+ performanceResults . reactProfiler . startTime +=
129
+ evaluationResult . reactProfiler . startTime || 0 ;
130
+ performanceResults . reactProfiler . commitTime +=
131
+ evaluationResult . reactProfiler . commitTime || 0 ;
132
+
133
+ performanceResults . error = evaluationResult . error ;
134
+ }
59
135
60
136
await browser . close ( ) ;
61
- return result ;
137
+
138
+ return performanceResults ;
62
139
}
63
140
64
141
function buildHtml ( transpiled : string ) {
@@ -82,7 +159,7 @@ function buildHtml(transpiled: string) {
82
159
window.__RESULT__ = {
83
160
renderTime: null,
84
161
webVitals: {},
85
- reactProfilerMetrics : {},
162
+ reactProfiler : {},
86
163
error: null
87
164
};
88
165
@@ -112,12 +189,12 @@ function buildHtml(transpiled: string) {
112
189
React.createElement(React.Profiler, {
113
190
id: 'App',
114
191
onRender: (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
115
- window.__RESULT__.reactProfilerMetrics .id = id;
116
- window.__RESULT__.reactProfilerMetrics .phase = phase;
117
- window.__RESULT__.reactProfilerMetrics .actualDuration = actualDuration;
118
- window.__RESULT__.reactProfilerMetrics .baseDuration = baseDuration;
119
- window.__RESULT__.reactProfilerMetrics .startTime = startTime;
120
- window.__RESULT__.reactProfilerMetrics .commitTime = commitTime;
192
+ window.__RESULT__.reactProfiler .id = id;
193
+ window.__RESULT__.reactProfiler .phase = phase;
194
+ window.__RESULT__.reactProfiler .actualDuration = actualDuration;
195
+ window.__RESULT__.reactProfiler .baseDuration = baseDuration;
196
+ window.__RESULT__.reactProfiler .startTime = startTime;
197
+ window.__RESULT__.reactProfiler .commitTime = commitTime;
121
198
}
122
199
}, React.createElement(AppComponent))
123
200
);
@@ -127,10 +204,7 @@ function buildHtml(transpiled: string) {
127
204
window.__RESULT__.renderTime = renderEnd - renderStart;
128
205
} catch (error) {
129
206
console.error('Error rendering component:', error);
130
- window.__RESULT__.error = {
131
- message: error.message,
132
- stack: error.stack
133
- };
207
+ window.__RESULT__.error = error;
134
208
}
135
209
</script>
136
210
<script>
0 commit comments