feat(Page): Support Page.getMetrics and metrics event. (#939)

Provides access to the current page performance metrics.
Allows to push page metrics from the page JavaScript with console.timeStamp()

Fixes #309
This commit is contained in:
Alexei Filippov 2017-10-10 14:50:38 -07:00 committed by Andrey Lushnikov
parent 77d3c2972f
commit b82d3197ed
3 changed files with 127 additions and 0 deletions

View File

@ -25,6 +25,7 @@
+ [event: 'framedetached'](#event-framedetached)
+ [event: 'framenavigated'](#event-framenavigated)
+ [event: 'load'](#event-load)
+ [event: 'metrics'](#event-metrics)
+ [event: 'pageerror'](#event-pageerror)
+ [event: 'request'](#event-request)
+ [event: 'requestfailed'](#event-requestfailed)
@ -49,6 +50,7 @@
+ [page.exposeFunction(name, puppeteerFunction)](#pageexposefunctionname-puppeteerfunction)
+ [page.focus(selector)](#pagefocusselector)
+ [page.frames()](#pageframes)
+ [page.getMetrics()](#pagegetmetrics)
+ [page.goBack(options)](#pagegobackoptions)
+ [page.goForward(options)](#pagegoforwardoptions)
+ [page.goto(url, options)](#pagegotourl-options)
@ -326,6 +328,15 @@ Emitted when a frame is navigated to a new url.
Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.
#### event: 'metrics'
- <[Object]>
- `title` <[string]> The title passed to `console.timeStamp`.
- `metrics` <[Object]> Object containing metrics as key/value pairs. The values
of metrics are of <[number]> type.
Emitted when the JavaScript code makes a call to `console.timeStamp`. For the list
of metrics see `page.getMetrics`.
#### event: 'pageerror'
- <[string]> The exception message
@ -637,6 +648,27 @@ If there's no element matching `selector`, the method throws an error.
#### page.frames()
- returns: <[Array]<[Frame]>> An array of all frames attached to the page.
#### page.getMetrics()
- returns: <[Object]> Object containing metrics as key/value pairs.
- `Timestamp` <[number]> The timestamp when the metrics sample was taken.
- `DocumentCount` <[number]> Number of documents in the page.
- `FrameCount` <[number]> Number of frames in the page.
- `JSEventListenerCount` <[number]> Number of events in the page.
- `NodeCount` <[number]> Number of DOM nodes in the page.
- `LayoutCount` <[number]> Total number of full or partial page layout.
- `RecalcStyleCount` <[number]> Total number of page style recalculations.
- `LayoutDuration` <[number]> Combined durations of all page layouts.
- `RecalcStyleDuration` <[number]> Combined duration of all page style recalculations.
- `ScriptDuration` <[number]> Combined duration of JavaScript execution.
- `TaskDuration` <[number]> Combined duration of all tasks performed by the browser.
- `JSHeapUsedSize` <[number]> Used JavaScript heap size.
- `JSHeapTotalSize` <[number]> Total JavaScript heap size.
- `FirstMeaningfulPaint` <[number]> Timestamp of the first meaningful paint event.
- `DomContentLoaded` <[number]> Timestamp of the DOM content loaded event.
- `NavigationStart` <[number]> Timestamp of the navigation start event.
> **NOTE** All timestamps are in monotonic time: monotonically increasing time in seconds since an arbitrary point in the past.
#### page.goBack(options)
- `options` <[Object]> Navigation parameters which might have the following properties:
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout.

View File

@ -40,6 +40,7 @@ class Page extends EventEmitter {
client.send('Page.enable', {}),
client.send('Runtime.enable', {}),
client.send('Security.enable', {}),
client.send('Performance.enable', {})
]);
if (ignoreHTTPSErrors)
await client.send('Security.setOverrideCertificateErrors', {override: true});
@ -87,6 +88,7 @@ class Page extends EventEmitter {
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
client.on('Security.certificateError', event => this._onCertificateError(event));
client.on('Inspector.targetCrashed', event => this._onTargetCrashed());
client.on('Performance.metrics', event => this._emitMetrics(event));
}
_onTargetCrashed() {
@ -304,6 +306,37 @@ class Page extends EventEmitter {
return this._networkManager.setUserAgent(userAgent);
}
/**
* @return {!Promise<!Object>}
*/
async getMetrics() {
const response = await this._client.send('Performance.getMetrics');
return this._buildMetricsObject(response.metrics);
}
/**
* @param {*} event
*/
_emitMetrics(event) {
this.emit(Page.Events.Metrics, {
title: event.title,
metrics: this._buildMetricsObject(event.metrics)
});
}
/**
* @param {?Array<!{name: string, value: number}>} metrics
* @return {!Object}
*/
_buildMetricsObject(metrics) {
const result = {};
for (const metric of metrics || []) {
if (supportedMetrics.has(metric.name))
result[metric.name] = metric.value;
}
return result;
}
/**
* @param {!Object} exceptionDetails
*/
@ -776,6 +809,26 @@ class Page extends EventEmitter {
}
}
/** @type {!Set<string>} */
const supportedMetrics = new Set([
'Timestamp',
'DocumentCount',
'FrameCount',
'JSEventListenerCount',
'NodeCount',
'LayoutCount',
'RecalcStyleCount',
'LayoutDuration',
'RecalcStyleDuration',
'ScriptDuration',
'TaskDuration',
'JSHeapUsedSize',
'JSHeapTotalSize',
'FirstMeaningfulPaint',
'DomContentLoaded',
'NavigationStart',
]);
/** @enum {string} */
Page.PaperFormats = {
letter: {width: 8.5, height: 11},
@ -844,6 +897,7 @@ Page.Events = {
FrameDetached: 'framedetached',
FrameNavigated: 'framenavigated',
Load: 'load',
Metrics: 'metrics',
};
/**

View File

@ -749,6 +749,47 @@ describe('Page', function() {
}));
});
describe('Page.getMetrics', function() {
it('should get metrics from a page', SX(async function() {
await page.goto('about:blank');
const metrics = await page.getMetrics();
checkMetrics(metrics);
}));
it('metrics event fired on console.timeStamp', SX(async function() {
const metricsPromise = new Promise(fulfill => page.once('metrics', fulfill));
await page.evaluate(() => console.timeStamp('test42'));
const metrics = await metricsPromise;
expect(metrics.title).toBe('test42');
checkMetrics(metrics.metrics);
}));
function checkMetrics(metrics) {
const metricsToCheck = new Set([
'Timestamp',
'DocumentCount',
'FrameCount',
'JSEventListenerCount',
'NodeCount',
'LayoutCount',
'RecalcStyleCount',
'LayoutDuration',
'RecalcStyleDuration',
'ScriptDuration',
'TaskDuration',
'JSHeapUsedSize',
'JSHeapTotalSize',
'FirstMeaningfulPaint',
'DomContentLoaded',
'NavigationStart',
]);
for (const name in metrics) {
expect(metricsToCheck.has(name)).toBeTruthy();
expect(metrics[name]).toBeGreaterThanOrEqual(0);
metricsToCheck.delete(name);
}
expect(metricsToCheck.size).toBe(0);
}
});
describe('Page.goto', function() {
it('should navigate to about:blank', SX(async function() {
const response = await page.goto('about:blank');