sitespeed.io 31.2.0 → 32.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # CHANGELOG - sitespeed.io (we use [semantic versioning](https://semver.org))
2
2
 
3
+ ## 32.0.0 - 2024-01-19
4
+ ### Breaking change
5
+ * If you use the compare plugin (`--compare.saveBaseline`) you need to remove your old baseline when you upgrade. The reason is that the original implementation was very narrow to some Browsertime metrics. The new version will be able to show more information (more metrics) between the baseline and the current test. This version allo disables comparing the CDP performance metrics, making the compare page easier to understand.
6
+
7
+
8
+ ## 31.2.1 - 2024-01-18
9
+ ### Fixed
10
+ * Another fix for `--budget.suppressExitCode` hopefully it works better this time [#4065](https://github.com/sitespeedio/sitespeed.io/pull/4065).
11
+
3
12
  ## 31.2.0 - 2024-01-17
4
13
  ### Fixed
5
14
  * There was bug that caused budget.suppressExitCode to fail that I introduced and reported in [#4062](https://github.com/sitespeedio/sitespeed.io/issues/4062). It's fixed in [#4063](https://github.com/sitespeedio/sitespeed.io/pull/4063).
package/bin/sitespeed.js CHANGED
@@ -163,10 +163,7 @@ async function start() {
163
163
  process.exitCode = 1;
164
164
  }
165
165
  // If you supress the exit code using budgets, we will always return 0
166
- else if (
167
- parsed.options.budget &&
168
- parsed.options.budget.suppressExitCode
169
- ) {
166
+ if (parsed.options.budget && parsed.options.budget.suppressExitCode) {
170
167
  process.exitCode = 0;
171
168
  }
172
169
 
@@ -226,6 +226,7 @@ function getVisualMetrics(data) {
226
226
  return results;
227
227
  }
228
228
 
229
+ /*
229
230
  function getCDPPerformance(data) {
230
231
  const metricsToKeep = new Set([
231
232
  'JSEventListeners',
@@ -261,6 +262,7 @@ function getCDPPerformance(data) {
261
262
  }
262
263
  return results;
263
264
  }
265
+ */
264
266
 
265
267
  function getCPU(data) {
266
268
  const cpuMetrics = {
@@ -351,8 +353,8 @@ export function getMetrics(data) {
351
353
  ...getElementTimings(data),
352
354
  ...getUserTimings(data),
353
355
  ...getCPU(data),
354
- ...getBrowserMetrics(data),
355
- ...getCDPPerformance(data)
356
+ ...getBrowserMetrics(data)
357
+ // ...getCDPPerformance(data)
356
358
  };
357
359
  }
358
360
 
@@ -58,6 +58,8 @@ export default class ComparePlugin extends SitespeedioPlugin {
58
58
  }
59
59
 
60
60
  async open(context, options) {
61
+ this.pageXrays = {};
62
+ this.browsertimes = {};
61
63
  this.page = 0;
62
64
  this.make = context.messageMaker('compare').make;
63
65
  this.compareOptions = merge({}, defaultConfig, options.compare);
@@ -93,163 +95,199 @@ export default class ComparePlugin extends SitespeedioPlugin {
93
95
  break;
94
96
  }
95
97
  case 'browsertime.pageSummary': {
96
- this.page++;
97
- const id = this.options.compare.id || urlToId(message.data.info.url);
98
- const baseline = await getBaseline(
99
- id + '-' + this.page,
100
- this.compareOptions
101
- );
102
- if (this.options.compare.id) {
103
- log.info('Using id %s for page baseline', id);
104
- } else {
105
- log.info('Using auto generated id for the baseline: %s ', id);
106
- }
98
+ this.browsertimes[message.url] = message;
99
+ break;
100
+ }
101
+ case 'sitespeedio.summarize': {
102
+ for (let url of Object.keys(this.browsertimes)) {
103
+ this.page++;
104
+ const id = this.options.compare.id || urlToId(url);
105
+ const baseline = await getBaseline(
106
+ id + '-' + this.page,
107
+ this.compareOptions
108
+ );
109
+ if (this.options.compare.id) {
110
+ log.info('Using id %s for page baseline', id);
111
+ } else {
112
+ log.info('Using auto generated id for the baseline: %s ', id);
113
+ }
107
114
 
108
- if (baseline) {
109
- if (
110
- baseline &&
111
- this.options.browsertime.iterations !== baseline.timestamps.length
112
- )
113
- log.warning(
114
- 'The baseline test has %s runs and you current have %s. You should make sure you test the same amount of runs',
115
- baseline.timestamps.length,
116
- this.options.browsertime.iterations
117
- );
118
- log.info('Got a baseline:' + id + '-' + this.page);
119
- const newMetrics = getMetrics(message.data);
120
- const baselineMetrics = getMetrics(baseline);
121
- const metricsInputData = {
122
- options: {
123
- test_type: this.compareOptions.testType,
124
- alternative: this.compareOptions.alternative
125
- },
126
- metrics: {}
127
- };
115
+ if (baseline) {
116
+ if (
117
+ this.options.browsertime.iterations !==
118
+ baseline.browsertime.timestamps.length
119
+ )
120
+ log.warning(
121
+ 'The baseline test has %s runs and you current have %s. You should make sure you test the same amount of runs',
122
+ baseline.timestamps.length,
123
+ this.options.browsertime.iterations
124
+ );
125
+ log.info('Got a baseline:' + id + '-' + this.page);
126
+ const newMetrics = getMetrics(this.browsertimes[url].data);
127
+ const baselineMetrics = getMetrics(baseline.browsertime);
128
+ const metricsInputData = {
129
+ options: {
130
+ test_type: this.compareOptions.testType,
131
+ alternative: this.compareOptions.alternative
132
+ },
133
+ metrics: {}
134
+ };
128
135
 
129
- if (this.compareOptions.testType === 'mannwhitneyu') {
130
- metricsInputData.options.use_continuity =
131
- this.compareOptions.mannwhitneyu.useContinuity;
132
- metricsInputData.options.method =
133
- this.compareOptions.mannwhitneyu.method;
134
- metricsInputData.options.nan_policy = 'omit';
135
- } else if (this.compareOptions.testType === 'wilcoxon') {
136
- metricsInputData.options.correction =
137
- this.compareOptions.wilcoxon.correction;
138
- metricsInputData.options.zero_method =
139
- this.compareOptions.wilcoxon.zeroMethod;
140
- }
136
+ if (this.compareOptions.testType === 'mannwhitneyu') {
137
+ metricsInputData.options.use_continuity =
138
+ this.compareOptions.mannwhitneyu.useContinuity;
139
+ metricsInputData.options.method =
140
+ this.compareOptions.mannwhitneyu.method;
141
+ metricsInputData.options.nan_policy = 'omit';
142
+ } else if (this.compareOptions.testType === 'wilcoxon') {
143
+ metricsInputData.options.correction =
144
+ this.compareOptions.wilcoxon.correction;
145
+ metricsInputData.options.zero_method =
146
+ this.compareOptions.wilcoxon.zeroMethod;
147
+ }
141
148
 
142
- for (let group in newMetrics) {
143
- if (baselineMetrics[group]) {
144
- metricsInputData.metrics[group] = {};
145
- for (let metricName in newMetrics[group]) {
146
- // Ensure both current and baseline metrics are available
147
- if (
148
- baselineMetrics[group][metricName] &&
149
- newMetrics[group][metricName]
150
- ) {
151
- // Directly access the Metric instance
152
- const currentMetric = newMetrics[group][metricName];
153
- const baselineMetric = baselineMetrics[group][metricName];
149
+ for (let group in newMetrics) {
150
+ if (baselineMetrics[group]) {
151
+ metricsInputData.metrics[group] = {};
152
+ for (let metricName in newMetrics[group]) {
153
+ // Ensure both current and baseline metrics are available
154
+ if (
155
+ baselineMetrics[group][metricName] &&
156
+ newMetrics[group][metricName]
157
+ ) {
158
+ // Directly access the Metric instance
159
+ const currentMetric = newMetrics[group][metricName];
160
+ const baselineMetric = baselineMetrics[group][metricName];
154
161
 
155
- // Ensure these are indeed Metric instances
156
- const currentStats = getStatistics(currentMetric.getValues());
157
- const baselineStats = getStatistics(
158
- baselineMetric.getValues()
159
- );
160
- metricsInputData.metrics[group][metricName] = {
161
- baseline: baselineStats.data,
162
- current: currentStats.data
163
- };
164
- } else {
165
- log.info(
166
- `Skipping ${group}.${metricName} as it's not present in both current and baseline metrics.`
167
- );
162
+ // Ensure these are indeed Metric instances
163
+ const currentStats = getStatistics(
164
+ currentMetric.getValues()
165
+ );
166
+ const baselineStats = getStatistics(
167
+ baselineMetric.getValues()
168
+ );
169
+ metricsInputData.metrics[group][metricName] = {
170
+ baseline: baselineStats.data,
171
+ current: currentStats.data
172
+ };
173
+ } else {
174
+ log.info(
175
+ `Skipping ${group}.${metricName} as it's not present in both current and baseline metrics.`
176
+ );
177
+ }
168
178
  }
169
179
  }
170
180
  }
171
- }
172
181
 
173
- const results = await runStatisticalTests(metricsInputData);
174
- const finalResult = {};
175
- for (let group in results) {
176
- finalResult[group] = {};
177
- for (let metricName in results[group]) {
178
- const result = results[group][metricName];
179
- // Again, accessing the metricName within the group
180
- const currentStats = getStatistics(
181
- newMetrics[group][metricName].getValues()
182
- );
183
- const baselineStats = getStatistics(
184
- baselineMetrics[group][metricName].getValues()
185
- );
182
+ const results = await runStatisticalTests(metricsInputData);
183
+ const finalResult = {};
184
+ for (let group in results) {
185
+ finalResult[group] = {};
186
+ for (let metricName in results[group]) {
187
+ const result = results[group][metricName];
188
+ // Again, accessing the metricName within the group
189
+ const currentStats = getStatistics(
190
+ newMetrics[group][metricName].getValues()
191
+ );
192
+ const baselineStats = getStatistics(
193
+ baselineMetrics[group][metricName].getValues()
194
+ );
186
195
 
187
- const cliffs = cliffsDelta(currentStats.data, baselineStats.data);
188
- finalResult[group][metricName] = {
189
- current: {
190
- stdev: currentStats.stddev(),
191
- mean: currentStats.amean(),
192
- median: currentStats.median(),
193
- values: currentStats.data
194
- },
195
- baseline: {
196
- stdev: baselineStats.stddev(),
197
- mean: baselineStats.amean(),
198
- median: baselineStats.median(),
199
- values: baselineStats.data
200
- },
201
- statisticalTestU: result['p-value'],
202
- cliffsDelta: cliffs,
203
- isSignificant: getIsSignificant(result['p-value'], cliffs)
204
- };
196
+ const cliffs = cliffsDelta(
197
+ currentStats.data,
198
+ baselineStats.data
199
+ );
200
+ finalResult[group][metricName] = {
201
+ current: {
202
+ stdev: currentStats.stddev(),
203
+ mean: currentStats.amean(),
204
+ median: currentStats.median(),
205
+ values: currentStats.data
206
+ },
207
+ baseline: {
208
+ stdev: baselineStats.stddev(),
209
+ mean: baselineStats.amean(),
210
+ median: baselineStats.median(),
211
+ values: baselineStats.data
212
+ },
213
+ statisticalTestU: result['p-value'],
214
+ cliffsDelta: cliffs,
215
+ isSignificant: getIsSignificant(result['p-value'], cliffs)
216
+ };
217
+ }
205
218
  }
206
- }
207
- const meta = {
208
- baseline: {
209
- timestamp: dayjs(baseline.info.timestamp).format(TIME_FORMAT),
210
- url: baseline.info.url,
211
- alias: baseline.info.alias
212
- },
213
- current: {
214
- timestamp: dayjs(message.data.info.timestamp).format(TIME_FORMAT),
215
- url: message.data.info.url,
216
- alias: message.data.info.alias
217
- },
218
- testOptions: this.compareOptions,
219
- iterations: this.options.browsertime.iterations
220
- };
221
219
 
222
- if (this.compareOptions.saveBaseline) {
223
- await saveBaseline(
224
- message.data,
225
- join(
226
- this.compareOptions.baselinePath || process.cwd(),
227
- `${id}-${this.page}.json`
228
- )
229
- );
230
- }
220
+ const meta = {
221
+ baseline: {
222
+ timestamp: dayjs(baseline.browsertime.info.timestamp).format(
223
+ TIME_FORMAT
224
+ ),
225
+ url: baseline.browsertime.info.url,
226
+ alias: baseline.browsertime.info.alias
227
+ },
228
+ current: {
229
+ timestamp: dayjs(
230
+ this.browsertimes[url].data.info.timestamp
231
+ ).format(TIME_FORMAT),
232
+ url: url,
233
+ alias: this.browsertimes[url].data.info.alias
234
+ },
235
+ testOptions: this.compareOptions,
236
+ iterations: this.options.browsertime.iterations
237
+ };
238
+
239
+ const raw = {
240
+ baseline: {
241
+ pagexray: baseline.pagexray,
242
+ browsertime: baseline.browsertime
243
+ },
244
+ current: {
245
+ pagexray: this.pageXrays[url].data,
246
+ browsertime: this.browsertimes[url].data
247
+ }
248
+ };
231
249
 
232
- super.sendMessage(
233
- 'compare.pageSummary',
234
- { metrics: finalResult, meta },
235
- {
236
- url: message.url,
237
- group: message.group,
238
- runTime: message.runTime
250
+ if (this.compareOptions.saveBaseline) {
251
+ await saveBaseline(
252
+ {
253
+ browsertime: this.browsertimes[url].data,
254
+ pagexray: this.pageXrays[url].data
255
+ },
256
+ join(
257
+ this.compareOptions.baselinePath || process.cwd(),
258
+ `${id}-${this.page}.json`
259
+ )
260
+ );
239
261
  }
240
- );
241
- } else {
242
- if (this.compareOptions.saveBaseline) {
243
- await saveBaseline(
244
- message.data,
245
- join(
246
- this.compareOptions.baselinePath || process.cwd(),
247
- `${id}-${this.page}.json`
248
- )
262
+
263
+ super.sendMessage(
264
+ 'compare.pageSummary',
265
+ { metrics: finalResult, meta, raw },
266
+ {
267
+ url: url,
268
+ group: this.browsertimes[url].group,
269
+ runTime: this.browsertimes[url].runTime
270
+ }
249
271
  );
272
+ } else {
273
+ if (this.compareOptions.saveBaseline) {
274
+ await saveBaseline(
275
+ {
276
+ browsertime: this.browsertimes[url].data,
277
+ pagexray: this.pageXrays[url].data
278
+ },
279
+ join(
280
+ this.compareOptions.baselinePath || process.cwd(),
281
+ `${id}-${this.page}.json`
282
+ )
283
+ );
284
+ }
250
285
  }
251
286
  }
252
-
287
+ break;
288
+ }
289
+ case 'pagexray.pageSummary': {
290
+ this.pageXrays[message.url] = message;
253
291
  break;
254
292
  }
255
293
  }
@@ -38,19 +38,85 @@ p
38
38
  | . For this test, a correction parameter (#{compare.meta.testOptions.wilcoxon.correction ? 'enabled' : 'disabled'}) was applied to adjust for small sample sizes.
39
39
 
40
40
 
41
- p
42
- | The baseline test
43
- if compare.meta.baseline.alias
44
- a(href=compare.meta.baseline.url) #{compare.meta.baseline.alias}
45
- else
46
- a(href=compare.meta.baseline.url) #{compare.meta.baseline.url}
47
- | was conducted at #{compare.meta.baseline.timestamp} and the current test
48
- if compare.meta.current.alias
49
- a(href=compare.meta.current.url) #{compare.meta.current.alias}
50
- else
51
- a(href=compare.meta.current.url) #{compare.meta.current.url}
52
- | was done at #{compare.meta.current.timestamp}.
41
+ h2 Setup
42
+ table
43
+ tr
44
+ th
45
+ b Metric
46
+ th
47
+ b baseline
48
+ th
49
+ b current
50
+ tr
51
+ td Test
52
+ td
53
+ if compare.meta.baseline.alias
54
+ a(href=compare.meta.baseline.url) #{compare.meta.baseline.alias}
55
+ else
56
+ a(href=compare.meta.baseline.url) #{compare.meta.baseline.url}
57
+ td
58
+ if compare.meta.current.alias
59
+ a(href=compare.meta.current.url) #{compare.meta.current.alias}
60
+ else
61
+ a(href=compare.meta.current.url) #{compare.meta.current.url}
62
+ tr
63
+ td Run time
64
+ td #{compare.meta.baseline.timestamp}
65
+ td #{compare.meta.current.timestamp}
66
+ tr
67
+ td Total
68
+ td #{compare.raw.baseline.pagexray.statistics.requests.median} (#{h.size.format(compare.raw.baseline.pagexray.statistics.transferSize.median)} / #{h.size.format(compare.raw.baseline.pagexray.statistics.contentSize.median)})
69
+ td #{compare.raw.current.pagexray.statistics.requests.median} (#{h.size.format(compare.raw.current.pagexray.statistics.transferSize.median)} / #{h.size.format(compare.raw.current.pagexray.statistics.contentSize.median)})
70
+ tr
71
+ td HTML
72
+ td #{compare.raw.baseline.pagexray.statistics.contentTypes.html.requests.median} (#{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.html.transferSize.median)} / #{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.html.contentSize.median)})
73
+ td #{compare.raw.current.pagexray.statistics.contentTypes.html.requests.median} (#{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.html.transferSize.median)} / #{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.html.contentSize.median)})
74
+ tr
75
+ td JavaScript
76
+ td #{compare.raw.baseline.pagexray.statistics.contentTypes.javascript.requests.median} (#{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.javascript.transferSize.median)} / #{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.javascript.contentSize.median)})
77
+ td #{compare.raw.current.pagexray.statistics.contentTypes.javascript.requests.median} (#{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.javascript.transferSize.median)} / #{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.javascript.contentSize.median)})
78
+ tr
79
+ td CSS requests
80
+ td #{compare.raw.baseline.pagexray.statistics.contentTypes.css.requests.median} (#{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.css.transferSize.median)} / #{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.css.contentSize.median)})
81
+ td #{compare.raw.current.pagexray.statistics.contentTypes.css.requests.median} (#{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.css.transferSize.median)} / #{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.css.contentSize.median)})
82
+ tr
83
+ td Image requests
84
+ td #{compare.raw.baseline.pagexray.statistics.contentTypes.image.requests.median} (#{h.size.format(compare.raw.baseline.pagexray.statistics.contentTypes.image.transferSize.median)})
85
+ td #{compare.raw.current.pagexray.statistics.contentTypes.image.requests.median} (#{h.size.format(compare.raw.current.pagexray.statistics.contentTypes.image.transferSize.median)})
86
+ tr
87
+ td DOM Elements
88
+ td #{compare.raw.baseline.browsertime.statistics.pageinfo.domElements.median}
89
+ td #{compare.raw.current.browsertime.statistics.pageinfo.domElements.median}
90
+
91
+ if compare.raw.current.pagexray.meta.screenshot
92
+ tr
93
+ td Screenshot
94
+ td
95
+ a(href=compare.raw.baseline.pagexray.meta.screenshot)
96
+ img.screenshot(src=compare.raw.baseline.pagexray.meta.screenshot , width=200)
97
+ td
98
+ a(href=compare.raw.current.pagexray.meta.screenshot)
99
+ img.screenshot(src=compare.raw.current.pagexray.meta.screenshot , width=200)
53
100
 
101
+ if compare.raw.current.pagexray.meta.video
102
+ tr
103
+ td Video
104
+ td
105
+ .videoWrapper
106
+ video(controls, preload='none', width=200)
107
+ source(src=compare.raw.baseline.pagexray.meta.video type='video/mp4')
108
+ td
109
+ .videoWrapper
110
+ video(controls, preload='none', width=200)
111
+ source(src=compare.raw.current.pagexray.meta.video type='video/mp4')
112
+
113
+ if compare.raw.baseline.pagexray.meta.result
114
+ tr
115
+ td Result
116
+ td
117
+ a(href=compare.raw.baseline.pagexray.meta.result) Result
118
+ td
119
+ a(href=compare.raw.current.pagexray.meta.result) Result
54
120
 
55
121
  h2 Comparison Data
56
122
 
@@ -74,7 +140,7 @@ table
74
140
  td
75
141
  a(href=createGraphLink(groupName, metricName))
76
142
  b #{groupName + '.' + metricName}
77
- if values.statisticalTestU === "N/A" || values.statisticalTestU === "Datasets are identical"
143
+ if values.statisticalTestU === "N/A" || values.statisticalTestU === "Datasets are identical" || values.statisticalTestU === "No variability"
78
144
  td #{values.statisticalTestU}
79
145
  else
80
146
  td #{h.decimals(values.statisticalTestU)}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "sitespeed.io",
3
- "version": "31.2.0",
3
+ "version": "32.0.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "sitespeed.io",
9
- "version": "31.2.0",
9
+ "version": "32.0.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@google-cloud/storage": "6.9.5",
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "sitespeed.io": "./bin/sitespeed.js",
6
6
  "sitespeed.io-wpr": "./bin/browsertimeWebPageReplay.js"
7
7
  },
8
- "version": "31.2.0",
8
+ "version": "32.0.0",
9
9
  "description": "sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.",
10
10
  "keywords": [
11
11
  "performance",