playwright-performance-reporter
Version:
Measure and publish performance metrics from browser dev-tools when running playwright
135 lines (134 loc) • 4.42 kB
JavaScript
import { nativeChromiumPlugins, } from '../plugins/index.js';
export class NetworkActivityObserver {
options;
name = 'networkActivity';
plugins = [
nativeChromiumPlugins.networkDomainPlugin,
];
includeDetailsInSampling;
requestStartTimes;
requestMethods;
networkActivities;
subscriptions;
isEnabled;
constructor(options) {
this.options = options;
this.includeDetailsInSampling = options?.includeDetailsInSampling ?? false;
this.requestStartTimes = new Map();
this.requestMethods = new Map();
this.networkActivities = [];
this.subscriptions = [];
this.isEnabled = false;
}
/**
* @inheritdoc
*/
async onStart(accumulator, developmentTools) {
this.common(accumulator, developmentTools, true);
}
/**
* @inheritdoc
*/
async onSampling(accumulator, developmentTools) {
this.common(accumulator, developmentTools, this.includeDetailsInSampling);
}
/**
* @inheritdoc
*/
async onStop(accumulator, developmentTools) {
this.common(accumulator, developmentTools, true);
}
/**
* Common function for all lifecycle hooks
*/
common(accumulator, client, includeDetails) {
if (!this.isEnabled) {
this.setupListeners(client);
}
const result = {
...this.calculateAggregatedMetrics(),
networkActivities: includeDetails ? [...this.networkActivities] : [],
};
Object.assign(accumulator, result);
}
/**
* Setup network event listeners
*/
setupListeners(client) {
this.isEnabled = true;
// Listen for request start
this.subscriptions.push(this.setupRequestWillBeSent(client), this.setupResponseReceived(client), this.setupLoadingFinished(client), this.setupLoadingFailed(client));
}
/**
* Setup requestWillBeSent event listener
*
* @param client
*/
setupRequestWillBeSent(client) {
return client.Network.requestWillBeSent(cdpResponse => {
this.requestStartTimes.set(cdpResponse.requestId, Date.now());
this.requestMethods.set(cdpResponse.requestId, cdpResponse.request.method);
});
}
/**
* Setup responseReceived event listener
*
* @param client
*/
setupResponseReceived(client) {
return client.Network.responseReceived(cdpResponse => {
const { requestId } = cdpResponse;
const startTime = this.requestStartTimes.get(requestId);
if (!startTime) {
return;
}
const activity = {
requestId,
url: cdpResponse.response.url,
method: this.requestMethods.get(requestId) ?? 'UNKNOWN',
status: cdpResponse.response.status,
timestamp: startTime,
};
activity.duration = Date.now() - startTime;
this.networkActivities.push(activity);
});
}
/**
* Setup loadingFinished event listener
*
* @param client
*/
setupLoadingFinished(client) {
return client.Network.loadingFinished(cdpResponse => {
const { requestId } = cdpResponse;
const activity = this.networkActivities.find(a => a.requestId === requestId);
if (activity) {
activity.transferSize = cdpResponse.encodedDataLength;
}
});
}
/**
* Setup loadingFailed event listener
*
* @param client
*/
setupLoadingFailed(client) {
return client.Network.loadingFailed(cdpResponse => {
const { requestId } = cdpResponse;
const activity = this.networkActivities.find(a => a.requestId === requestId);
if (activity) {
activity.duration = Date.now() - (this.requestStartTimes.get(requestId) ?? Date.now());
}
});
}
/**
* Calculate aggregated metrics from collected activities
*/
calculateAggregatedMetrics() {
return {
totalNetworkRequests: this.networkActivities.length,
totalNetworkTransferSize: this.networkActivities.reduce((sum, a) => sum + (a.transferSize ?? 0), 0),
totalNetworkDuration: this.networkActivities.reduce((sum, a) => sum + (a.duration ?? 0), 0),
};
}
}