11import * as babel from '@babel/core' ;
22import puppeteer from 'puppeteer' ;
33
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 > {
528 const babelOptions = {
629 configFile : false ,
730 babelrc : false ,
@@ -43,22 +66,76 @@ export async function measurePerformance(code: string) {
4366 }
4467
4568 const browser = await puppeteer . launch ( ) ;
46-
4769 const page = await browser . newPage ( ) ;
4870 await page . setViewport ( { width : 1280 , height : 720 } ) ;
4971 const html = buildHtml ( transpiled ) ;
50- await page . setContent ( html , { waitUntil : 'networkidle0' } ) ;
5172
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+ } ;
5592
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+ }
59135
60136 await browser . close ( ) ;
61- return result ;
137+
138+ return performanceResults ;
62139}
63140
64141function buildHtml ( transpiled : string ) {
@@ -82,7 +159,7 @@ function buildHtml(transpiled: string) {
82159 window.__RESULT__ = {
83160 renderTime: null,
84161 webVitals: {},
85- reactProfilerMetrics : {},
162+ reactProfiler : {},
86163 error: null
87164 };
88165
@@ -112,12 +189,12 @@ function buildHtml(transpiled: string) {
112189 React.createElement(React.Profiler, {
113190 id: 'App',
114191 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;
121198 }
122199 }, React.createElement(AppComponent))
123200 );
@@ -127,10 +204,7 @@ function buildHtml(transpiled: string) {
127204 window.__RESULT__.renderTime = renderEnd - renderStart;
128205 } catch (error) {
129206 console.error('Error rendering component:', error);
130- window.__RESULT__.error = {
131- message: error.message,
132- stack: error.stack
133- };
207+ window.__RESULT__.error = error;
134208 }
135209 </script>
136210 <script>
0 commit comments