sitespeed.io 30.3.0 → 30.4.1
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/.github/workflows/linux.yml +8 -1
- package/CHANGELOG.md +13 -1
- package/CONTRIBUTORS.md +1 -0
- package/Dockerfile +3 -1
- package/lib/cli/cli.js +62 -0
- package/lib/plugins/compare/baseline.js +35 -0
- package/lib/plugins/compare/helper.js +347 -0
- package/lib/plugins/compare/index.js +229 -0
- package/lib/plugins/compare/pug/index.pug +135 -0
- package/lib/plugins/compare/statistical.py +37 -0
- package/lib/plugins/graphite/data-generator.js +1 -1
- package/lib/plugins/html/assets/css/index.min.css +1 -1
- package/lib/plugins/html/src/sass/components/chartist.scss +31 -0
- package/lib/plugins/html/templates/url/iteration/downloads.pug +3 -0
- package/lib/plugins/html/templates/url/metrics/index.pug +1 -1
- package/lib/plugins/html/templates/url/summary/downloads.pug +4 -1
- package/lib/plugins/html/templates/url/summary/index.pug +3 -0
- package/lib/support/helpers/decimals.js +14 -2
- package/npm-shrinkwrap.json +23 -23
- package/package.json +2 -2
|
@@ -24,6 +24,11 @@ jobs:
|
|
|
24
24
|
sudo apt-get update
|
|
25
25
|
sudo apt-get --only-upgrade install google-chrome-stable
|
|
26
26
|
google-chrome --version
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade --user pip
|
|
30
|
+
python -m pip install --user scipy
|
|
31
|
+
python -m pip show scipy
|
|
27
32
|
- name: Install Firefox
|
|
28
33
|
uses: browser-actions/setup-firefox@latest
|
|
29
34
|
#with:
|
|
@@ -69,4 +74,6 @@ jobs:
|
|
|
69
74
|
- name: Run test with Influx 2.6.1
|
|
70
75
|
run: bin/sitespeed.js http://127.0.0.1:3001/simple/ -n 1 --influxdb.host 127.0.0.1 --influxdb.port 8087 --influxdb.version 2 --influxdb.organisation sitespeed --influxdb.token sitespeed --xvfb
|
|
71
76
|
- name: Run Chrome test with config
|
|
72
|
-
run: node bin/sitespeed.js --config test/exampleConfig.json http://127.0.0.1:3001/simple/ --xvfb
|
|
77
|
+
run: node bin/sitespeed.js --config test/exampleConfig.json http://127.0.0.1:3001/simple/ --xvfb
|
|
78
|
+
- name: Run Chrome test using compare plugin
|
|
79
|
+
run: node bin/sitespeed.js --compare.id compare --compare.saveBaseline --compare.baselinePath test/ http://127.0.0.1:3001/simple/ --xvfb
|
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
# CHANGELOG - sitespeed.io (we use [semantic versioning](https://semver.org))
|
|
2
2
|
|
|
3
|
+
## 30.4.1 - 2023-11-28
|
|
4
|
+
### Fixed
|
|
5
|
+
* Fix for Firefox when generating the result HTML. It was broken since we where missing CPU data.
|
|
6
|
+
|
|
7
|
+
## 30.4.0 - 2023-11-27
|
|
8
|
+
### Fixed
|
|
9
|
+
* Upgrade to Browsretime 19.1.0 with a fix for Geckodriver so that the correct ARM version is installed on Mac Arm machines.
|
|
10
|
+
### Added
|
|
11
|
+
* The new compare plugin [PR 4009](https://github.com/sitespeedio/sitespeed.io/pull/4009) makes it easy to use Mann Whitney U/Wilcox for support to find performance egressions. Read all about the plugin in the [documentation](https://www.sitespeed.io/documentation/sitespeed.io/compare/).
|
|
12
|
+
* Firefox 120 in the Docker container [#4010](https://github.com/sitespeedio/sitespeed.io/pull/4010).
|
|
13
|
+
* Button to download the console logs, thank you [bairov pavel](https://github.com/Amerousful) for PR [#4007](https://github.com/sitespeedio/sitespeed.io/pull/4007).
|
|
14
|
+
|
|
3
15
|
## 30.3.0 - 2023-11-09
|
|
4
16
|
|
|
5
17
|
### Added
|
|
6
18
|
* Upgrade to Browsertime 18.0.0.
|
|
7
|
-
* Added
|
|
19
|
+
* Added support to run user journeys with WebPageReplay [#4005](https://github.com/sitespeedio/sitespeed.io/pull/4005).
|
|
8
20
|
|
|
9
21
|
### Fixed
|
|
10
22
|
* Downgrade puppeteer in the +1 container for Lighthouse, thank you [bairov pavel](https://github.com/Amerousful) for PR [#123](https://github.com/sitespeedio/plugin-lighthouse/pull/123).
|
package/CONTRIBUTORS.md
CHANGED
package/Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
FROM sitespeedio/webbrowsers:chrome-119.0-firefox-
|
|
1
|
+
FROM sitespeedio/webbrowsers:chrome-119.0-firefox-120.0-edge-119.0
|
|
2
2
|
|
|
3
3
|
ARG TARGETPLATFORM=linux/amd64
|
|
4
4
|
|
|
@@ -44,4 +44,6 @@ RUN echo 'ALL ALL=NOPASSWD: /usr/sbin/tc, /usr/sbin/route, /usr/sbin/ip' > /etc/
|
|
|
44
44
|
|
|
45
45
|
ENTRYPOINT ["/start.sh"]
|
|
46
46
|
VOLUME /sitespeed.io
|
|
47
|
+
VOLUME /baseline
|
|
48
|
+
|
|
47
49
|
WORKDIR /sitespeed.io
|
package/lib/cli/cli.js
CHANGED
|
@@ -1870,6 +1870,68 @@ export async function parseCommandLine() {
|
|
|
1870
1870
|
group: 'API'
|
|
1871
1871
|
});
|
|
1872
1872
|
|
|
1873
|
+
parsed
|
|
1874
|
+
.option('compare.id', {
|
|
1875
|
+
type: 'string',
|
|
1876
|
+
describe:
|
|
1877
|
+
'The id of the test. Will be used to find the baseline test, that is using the id as a part of the name.',
|
|
1878
|
+
group: 'compare'
|
|
1879
|
+
})
|
|
1880
|
+
.option('compare.baselinePath', {
|
|
1881
|
+
type: 'string',
|
|
1882
|
+
describe:
|
|
1883
|
+
'Specifies the path to the baseline data file. This file is used as a reference for comparison against the current test data.',
|
|
1884
|
+
group: 'compare'
|
|
1885
|
+
})
|
|
1886
|
+
.option('compare.saveBaseline', {
|
|
1887
|
+
type: 'boolean',
|
|
1888
|
+
default: false,
|
|
1889
|
+
describe:
|
|
1890
|
+
'Determines whether to save the current test data as the new baseline. Set to true to save the current data as baseline for future comparisons.',
|
|
1891
|
+
group: 'compare'
|
|
1892
|
+
})
|
|
1893
|
+
.option('compare.testType', {
|
|
1894
|
+
describe:
|
|
1895
|
+
'Selects the statistical test type to be used for comparison. Options are mannwhitneyu for the Mann-Whitney U test and wilcoxon for the Wilcoxon signed-rank test.',
|
|
1896
|
+
choices: ['mannwhitneyu', ' wilcoxon'],
|
|
1897
|
+
default: 'mannwhitneyu',
|
|
1898
|
+
group: 'compare'
|
|
1899
|
+
})
|
|
1900
|
+
.option('compare.alternative', {
|
|
1901
|
+
choices: ['less', ' greater', 'two-sided'],
|
|
1902
|
+
default: 'less',
|
|
1903
|
+
describe:
|
|
1904
|
+
'Specifies the alternative hypothesis to be tested. Options are less for one-sided test where the first group is expected to be less than the second, greater for one-sided test with the first group expected to be greater, or two-sided for a two-sided test.',
|
|
1905
|
+
group: 'compare'
|
|
1906
|
+
})
|
|
1907
|
+
.option('compare.wilcoxon.correction', {
|
|
1908
|
+
type: 'boolean',
|
|
1909
|
+
describe:
|
|
1910
|
+
'Enables or disables the continuity correction in the Wilcoxon signed-rank test. Set to true to enable the correction.',
|
|
1911
|
+
default: false,
|
|
1912
|
+
group: 'compare'
|
|
1913
|
+
})
|
|
1914
|
+
.option('compare.wilcoxon.zeroMethod', {
|
|
1915
|
+
choices: ['wilcox', ' pratt', 'zsplit'],
|
|
1916
|
+
describe:
|
|
1917
|
+
'Specifies the method for handling zero differences in the Wilcoxon test. wilcox discards all zero-difference pairs, pratt includes all, and zsplit splits them evenly among positive and negative ranks.',
|
|
1918
|
+
default: 'zsplit',
|
|
1919
|
+
group: 'compare'
|
|
1920
|
+
})
|
|
1921
|
+
.option('compare.mannwhitneyu.useContinuity', {
|
|
1922
|
+
type: 'boolean',
|
|
1923
|
+
default: false,
|
|
1924
|
+
describe:
|
|
1925
|
+
'Determines whether to use continuity correction in the Mann-Whitney U test. Set to true to apply the correction.',
|
|
1926
|
+
group: 'compare'
|
|
1927
|
+
})
|
|
1928
|
+
.option('compare.mannwhitneyu.method', {
|
|
1929
|
+
choices: ['auto', ' exact', 'symptotic'],
|
|
1930
|
+
escribe:
|
|
1931
|
+
'Selects the method for calculating the Mann-Whitney U test. auto automatically selects between exact and asymptotic based on sample size, exact uses the exact distribution of U, and symptotic uses a normal approximation.',
|
|
1932
|
+
default: 'auto',
|
|
1933
|
+
group: 'compare'
|
|
1934
|
+
});
|
|
1873
1935
|
parsed
|
|
1874
1936
|
.option('mobile', {
|
|
1875
1937
|
describe:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export async function getBaseline(id, compareOptions) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(
|
|
7
|
+
await fs.readFile(
|
|
8
|
+
resolve(
|
|
9
|
+
join(compareOptions.baselinePath || process.cwd(), `${id}.json`)
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
);
|
|
13
|
+
} catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/*
|
|
18
|
+
async function getBaselineFromInternet(url) {
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(url);
|
|
21
|
+
return response.json();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
log.error('Could not fetch', error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function getBaselineFromFile(path) {}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
export async function saveBaseline(json, options) {
|
|
31
|
+
|
|
32
|
+
}*/
|
|
33
|
+
export async function saveBaseline(json, name) {
|
|
34
|
+
return fs.writeFile(resolve(name), JSON.stringify(json));
|
|
35
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { execa } from 'execa';
|
|
6
|
+
import { Stats } from 'fast-stats';
|
|
7
|
+
import intel from 'intel';
|
|
8
|
+
import { decimals } from '../../support/helpers/index.js';
|
|
9
|
+
const log = intel.getLogger('sitespeedio.plugin.compare');
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
class Metric {
|
|
14
|
+
constructor(name, values) {
|
|
15
|
+
this.name = name;
|
|
16
|
+
this.stats = new Stats().push(values);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getName() {
|
|
20
|
+
return this.name;
|
|
21
|
+
}
|
|
22
|
+
getValues() {
|
|
23
|
+
return this.stats.data;
|
|
24
|
+
}
|
|
25
|
+
getStats() {
|
|
26
|
+
return this.stats;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runStatisticalTests(data) {
|
|
31
|
+
let extras = '';
|
|
32
|
+
try {
|
|
33
|
+
const { stdout } = await execa(
|
|
34
|
+
process.env.PYTHON || 'python',
|
|
35
|
+
[join(__dirname, 'statistical.py')],
|
|
36
|
+
{
|
|
37
|
+
input: JSON.stringify(data)
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
extras = stdout;
|
|
41
|
+
const results = JSON.parse(stdout);
|
|
42
|
+
log.verbose('Result from the python script %j'.results);
|
|
43
|
+
return results;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
log.error(error);
|
|
46
|
+
log.error(extras);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getStatistics(arrayOfValues) {
|
|
51
|
+
return new Stats().push(arrayOfValues);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getExtras(data) {
|
|
55
|
+
const metrics = {};
|
|
56
|
+
const results = {};
|
|
57
|
+
|
|
58
|
+
for (const run of data.extras) {
|
|
59
|
+
for (const name of Object.keys(run)) {
|
|
60
|
+
if (!metrics[name]) {
|
|
61
|
+
metrics[name] = [];
|
|
62
|
+
}
|
|
63
|
+
metrics[name].push(run[name]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const [metricName, values] of Object.entries(metrics)) {
|
|
68
|
+
results[metricName] = new Metric(metricName, values);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getTimings(data) {
|
|
75
|
+
const timingMetrics = {
|
|
76
|
+
ttfb: [],
|
|
77
|
+
loadEventEnd: [],
|
|
78
|
+
firstContentfulPaint: [],
|
|
79
|
+
fullyLoaded: []
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (const run of data.browserScripts) {
|
|
83
|
+
timingMetrics['ttfb'].push(run.timings.ttfb);
|
|
84
|
+
timingMetrics['loadEventEnd'].push(run.timings.loadEventEnd);
|
|
85
|
+
timingMetrics['firstContentfulPaint'].push(
|
|
86
|
+
run.timings.paintTiming['first-contentful-paint']
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const run of data.fullyLoaded) {
|
|
91
|
+
timingMetrics['fullyLoaded'].push(run);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const results = {};
|
|
95
|
+
for (const [metricName, values] of Object.entries(timingMetrics)) {
|
|
96
|
+
if (!results.timings) {
|
|
97
|
+
results.timings = {};
|
|
98
|
+
}
|
|
99
|
+
results.timings[metricName] = new Metric(`${metricName}`, values);
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getUserTimings(data) {
|
|
105
|
+
const userTimingMetrics = {};
|
|
106
|
+
for (const run of data.browserScripts) {
|
|
107
|
+
if (run.timings.userTimings) {
|
|
108
|
+
const { marks, measures } = run.timings.userTimings;
|
|
109
|
+
|
|
110
|
+
for (const mark of marks) {
|
|
111
|
+
if (!userTimingMetrics[mark.name]) {
|
|
112
|
+
userTimingMetrics[mark.name] = [];
|
|
113
|
+
}
|
|
114
|
+
userTimingMetrics[mark.name].push(decimals(mark.startTime));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const measure of measures) {
|
|
118
|
+
if (!userTimingMetrics[measure.name]) {
|
|
119
|
+
userTimingMetrics[measure.name] = [];
|
|
120
|
+
}
|
|
121
|
+
userTimingMetrics[measure.name].push(decimals(measure.startTime));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const results = {};
|
|
127
|
+
for (const [metricName, values] of Object.entries(userTimingMetrics)) {
|
|
128
|
+
if (!results.userTimings) {
|
|
129
|
+
results.userTimings = {};
|
|
130
|
+
}
|
|
131
|
+
results.userTimings[metricName] = new Metric(`${metricName}`, values);
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getElementTimings(data) {
|
|
137
|
+
const elementTimingMetrics = {};
|
|
138
|
+
for (const run of data.browserScripts) {
|
|
139
|
+
if (run.timings.elementTimings) {
|
|
140
|
+
for (const [name, timing] of Object.entries(run.timings.elementTimings)) {
|
|
141
|
+
if (!elementTimingMetrics[name]) {
|
|
142
|
+
elementTimingMetrics[name] = [];
|
|
143
|
+
}
|
|
144
|
+
elementTimingMetrics[name].push(timing.renderTime);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const results = {};
|
|
150
|
+
for (const [metricName, values] of Object.entries(elementTimingMetrics)) {
|
|
151
|
+
if (!results.elementTimings) {
|
|
152
|
+
results.elementTimings = {};
|
|
153
|
+
}
|
|
154
|
+
results.elementTimings[metricName] = new Metric(`${metricName}`, values);
|
|
155
|
+
}
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getGoogleWebVitals(data) {
|
|
160
|
+
const googleWebVitalsMetrics = {};
|
|
161
|
+
for (const run of data.googleWebVitals) {
|
|
162
|
+
for (const [name, value] of Object.entries(run)) {
|
|
163
|
+
if (!googleWebVitalsMetrics[name]) {
|
|
164
|
+
googleWebVitalsMetrics[name] = [];
|
|
165
|
+
}
|
|
166
|
+
googleWebVitalsMetrics[name].push(value);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const results = {};
|
|
171
|
+
for (const [metricName, values] of Object.entries(googleWebVitalsMetrics)) {
|
|
172
|
+
if (!results.googleWebVitals) {
|
|
173
|
+
results.googleWebVitals = {};
|
|
174
|
+
}
|
|
175
|
+
results.googleWebVitals[metricName] = new Metric(`${metricName}`, values);
|
|
176
|
+
}
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getVisualMetrics(data) {
|
|
181
|
+
const DO_NOT_USE = new Set([
|
|
182
|
+
'VisualProgress',
|
|
183
|
+
'videoRecordingStart',
|
|
184
|
+
'VisualComplete85',
|
|
185
|
+
'VisualComplete95',
|
|
186
|
+
'VisualComplete99'
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
const visualMetrics = {};
|
|
190
|
+
for (const run of data.visualMetrics) {
|
|
191
|
+
for (const [name, value] of Object.entries(run)) {
|
|
192
|
+
if (!DO_NOT_USE.has(name)) {
|
|
193
|
+
if (!visualMetrics[name]) {
|
|
194
|
+
visualMetrics[name] = [];
|
|
195
|
+
}
|
|
196
|
+
visualMetrics[name].push(value);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const results = {};
|
|
202
|
+
for (const [metricName, values] of Object.entries(visualMetrics)) {
|
|
203
|
+
if (!results.visualMetrics) {
|
|
204
|
+
results.visualMetrics = {};
|
|
205
|
+
}
|
|
206
|
+
results.visualMetrics[metricName] = new Metric(`${metricName}`, values);
|
|
207
|
+
}
|
|
208
|
+
return results;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getCDPPerformance(data) {
|
|
212
|
+
const metricsToKeep = new Set([
|
|
213
|
+
'JSEventListeners',
|
|
214
|
+
'LayoutCount',
|
|
215
|
+
'RecalcStyleCount',
|
|
216
|
+
'LayoutDuration',
|
|
217
|
+
'RecalcStyleDuration',
|
|
218
|
+
'ScriptDuration',
|
|
219
|
+
'V8CompileDuration',
|
|
220
|
+
'TaskDuration',
|
|
221
|
+
'TaskOtherDuration',
|
|
222
|
+
'JSHeapUsedSize'
|
|
223
|
+
]);
|
|
224
|
+
const cdpPerformance = {};
|
|
225
|
+
for (const run of data.cdp.performance) {
|
|
226
|
+
for (const name of Object.keys(run)) {
|
|
227
|
+
if (metricsToKeep.has(name)) {
|
|
228
|
+
if (!cdpPerformance[name]) {
|
|
229
|
+
cdpPerformance[name] = [];
|
|
230
|
+
}
|
|
231
|
+
cdpPerformance[name].push(decimals(run[name]));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Convert to Metric objects
|
|
237
|
+
const results = {};
|
|
238
|
+
for (const [metricName, values] of Object.entries(cdpPerformance)) {
|
|
239
|
+
if (!results.cdp) {
|
|
240
|
+
results.cdp = {};
|
|
241
|
+
}
|
|
242
|
+
results.cdp[metricName] = new Metric(`${metricName}`, values);
|
|
243
|
+
}
|
|
244
|
+
return results;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function getCPU(data) {
|
|
248
|
+
const cpuMetrics = {
|
|
249
|
+
tasks: [],
|
|
250
|
+
totalDuration: [],
|
|
251
|
+
lastLongTask: [],
|
|
252
|
+
beforeFirstContentfulPaint: [],
|
|
253
|
+
beforeLargestContentfulPaint: []
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
for (const run of data.cpu) {
|
|
257
|
+
const longTasks = run.longTasks;
|
|
258
|
+
cpuMetrics['tasks'].push(longTasks['tasks']);
|
|
259
|
+
cpuMetrics['totalDuration'].push(longTasks['totalDuration']);
|
|
260
|
+
cpuMetrics['lastLongTask'].push(longTasks['lastLongTask']);
|
|
261
|
+
cpuMetrics['beforeFirstContentfulPaint'].push(
|
|
262
|
+
longTasks['beforeFirstContentfulPaint'].totalDuration
|
|
263
|
+
);
|
|
264
|
+
cpuMetrics['beforeLargestContentfulPaint'].push(
|
|
265
|
+
longTasks['beforeLargestContentfulPaint'].totalDuration
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const isEmpty = Object.values(cpuMetrics).every(arr => arr.length === 0);
|
|
270
|
+
if (isEmpty) {
|
|
271
|
+
return {}; // Return an empty object if no data
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const results = {};
|
|
275
|
+
for (const [metricName, values] of Object.entries(cpuMetrics)) {
|
|
276
|
+
if (!results.cpu) {
|
|
277
|
+
results.cpu = {};
|
|
278
|
+
}
|
|
279
|
+
results.cpu[metricName] = new Metric(`${metricName}`, values);
|
|
280
|
+
}
|
|
281
|
+
return results;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function getRenderBlocking(data) {
|
|
285
|
+
const renderBlockingMetrics = {
|
|
286
|
+
beforeFCPms: [],
|
|
287
|
+
beforeLCPms: [],
|
|
288
|
+
beforeFCPelements: [],
|
|
289
|
+
beforeLCPelements: []
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
for (const run of data.renderBlocking) {
|
|
293
|
+
renderBlockingMetrics['beforeFCPms'].push(
|
|
294
|
+
run.recalculateStyle.beforeFCP.durationInMillis
|
|
295
|
+
);
|
|
296
|
+
renderBlockingMetrics['beforeFCPelements'].push(
|
|
297
|
+
run.recalculateStyle.beforeFCP.elements
|
|
298
|
+
);
|
|
299
|
+
renderBlockingMetrics['beforeLCPms'].push(
|
|
300
|
+
run.recalculateStyle.beforeLCP.durationInMillis
|
|
301
|
+
);
|
|
302
|
+
renderBlockingMetrics['beforeLCPelements'].push(
|
|
303
|
+
run.recalculateStyle.beforeLCP.elements
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check if all arrays in renderBlockingMetrics are empty
|
|
308
|
+
const isEmpty = Object.values(renderBlockingMetrics).every(
|
|
309
|
+
arr => arr.length === 0
|
|
310
|
+
);
|
|
311
|
+
if (isEmpty) {
|
|
312
|
+
return {}; // Return an empty object if no data
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const results = {};
|
|
316
|
+
for (const [metricName, values] of Object.entries(renderBlockingMetrics)) {
|
|
317
|
+
if (!results.renderBlocking) {
|
|
318
|
+
results.renderBlocking = {};
|
|
319
|
+
}
|
|
320
|
+
results.renderBlocking[metricName] = new Metric(`${metricName}`, values);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return results;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function getMetrics(data) {
|
|
327
|
+
const userTimings = getUserTimings(data);
|
|
328
|
+
const elementTimings = getElementTimings(data);
|
|
329
|
+
const visualMetrics = getVisualMetrics(data);
|
|
330
|
+
const rb = getRenderBlocking(data);
|
|
331
|
+
const gWV = getGoogleWebVitals(data);
|
|
332
|
+
const cdp = getCDPPerformance(data);
|
|
333
|
+
const cpu = getCPU(data);
|
|
334
|
+
const timings = getTimings(data);
|
|
335
|
+
const extras = getExtras(data);
|
|
336
|
+
return {
|
|
337
|
+
...extras,
|
|
338
|
+
...timings,
|
|
339
|
+
...cpu,
|
|
340
|
+
...cdp,
|
|
341
|
+
...visualMetrics,
|
|
342
|
+
...gWV,
|
|
343
|
+
...rb,
|
|
344
|
+
...elementTimings,
|
|
345
|
+
...userTimings
|
|
346
|
+
};
|
|
347
|
+
}
|