sitespeed.io
Version:
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.
202 lines (184 loc) • 6.14 kB
JavaScript
import {
pushGroupStats,
setStatsSummary,
pushStats
} from '../../support/statsHelpers.js';
const METRIC_NAMES = ['transferSize', 'contentSize', 'requests'];
export class PageXrayAggregator {
constructor() {
this.urls = {};
this.stats = {};
this.groups = {};
}
addToAggregate(pageSummary, group) {
if (this.groups[group] === undefined) {
this.groups[group] = {};
}
let stats = this.stats;
let groups = this.groups;
for (const summary of pageSummary) {
let url = summary.url;
if (this.urls[url] === undefined) {
this.urls[url] = {};
}
// stats for the whole page
for (const metric of METRIC_NAMES) {
// There's a bug in Firefox/https://github.com/devtools-html/har-export-trigger
// that sometimes generate content size that is null, see
// https://github.com/sitespeedio/sitespeed.io/issues/2090
if (!Number.isNaN(summary[metric])) {
pushGroupStats(stats, groups[group], metric, summary[metric]);
pushStats(this.urls[url], metric, summary[metric]);
}
}
for (const contentType of Object.keys(summary.contentTypes)) {
for (const metric of METRIC_NAMES) {
// There's a bug in Firefox/https://github.com/devtools-html/har-export-trigger
// that sometimes generate content size that is null, see
// https://github.com/sitespeedio/sitespeed.io/issues/2090
if (!Number.isNaN(summary.contentTypes[contentType][metric])) {
pushGroupStats(
stats,
groups[group],
'contentTypes.' + contentType + '.' + metric,
summary.contentTypes[contentType][metric]
);
pushStats(
this.urls[url],
'contentTypes.' + contentType + '.' + metric,
summary.contentTypes[contentType][metric]
);
}
}
}
for (const responseCode of Object.keys(summary.responseCodes)) {
pushGroupStats(
stats,
groups[group],
'responseCodes.' + responseCode,
summary.responseCodes[responseCode]
);
pushStats(
this.urls[url],
'responseCodes.' + responseCode,
summary.responseCodes[responseCode]
);
}
/*
for (const responseCode of Object.keys(summary.responseCodes)) {
pushGroupStats(
stats,
groups[group],
'responseCodes.' + responseCode,
summary.responseCodes[responseCode]
);
}*/
// extras for firstParty vs third
if (summary.firstParty.requests) {
for (const metric of METRIC_NAMES) {
if (summary.firstParty[metric] !== undefined) {
pushGroupStats(
stats,
groups[group],
'firstParty' + '.' + metric,
summary.firstParty[metric]
);
pushStats(
this.urls[url],
'firstParty' + '.' + metric,
summary.firstParty[metric]
);
}
if (summary.thirdParty[metric] !== undefined) {
pushGroupStats(
stats,
groups[group],
'thirdParty' + '.' + metric,
summary.thirdParty[metric]
);
pushStats(
this.urls[url],
'thirdParty' + '.' + metric,
summary.thirdParty[metric]
);
}
}
}
// Add the total amount of domains on this page
pushGroupStats(
stats,
groups[group],
'domains',
Object.keys(summary.domains).length
);
// And the total amounts of cookies
pushGroupStats(stats, groups[group], 'cookies', summary.cookies);
for (const asset of summary.assets) {
pushGroupStats(stats, groups[group], 'expireStats', asset.expires);
pushGroupStats(
stats,
groups[group],
'lastModifiedStats',
asset.timeSinceLastModified
);
}
}
}
summarizePerUrl(url) {
// If we do not have a tested working page there's nothing to summarize
return this.urls[url] ? this.summarizePerObject(this.urls[url]) : {};
}
summarize() {
if (Object.keys(this.stats).length === 0) {
return;
}
const total = this.summarizePerObject(this.stats);
// This is an old bug we send total on the side and not in the group
// we send them both ways to be backward compatible
const summary = {
total,
groups: { total }
};
for (let group of Object.keys(this.groups)) {
summary.groups[group] = this.summarizePerObject(this.groups[group]);
}
return summary;
}
summarizePerObject(type) {
return Object.keys(type).reduce((summary, name) => {
if (
METRIC_NAMES.includes(name) ||
/(^domains|^expireStats|^lastModifiedStats|^cookies)/.test(name)
) {
setStatsSummary(summary, name, type[name]);
} else {
if (name === 'contentTypes') {
const contentTypeData = {};
for (const contentType of Object.keys(type[name])) {
// Here, type[name][contentType] is presumably an object
for (const [metric, stats] of Object.entries(
type[name][contentType]
)) {
setStatsSummary(contentTypeData, [contentType, metric], stats);
}
}
summary[name] = contentTypeData;
} else if (name === 'responseCodes') {
const responseCodeData = {};
for (const [metric, stats] of type.responseCodes.entries()) {
if (stats != undefined)
setStatsSummary(responseCodeData, metric, stats);
}
summary[name] = responseCodeData;
} else {
const data = {};
for (const [metric, stats] of Object.entries(type[name])) {
setStatsSummary(data, metric, stats);
}
summary[name] = data;
}
}
return summary;
}, {});
}
}