The power of Chrome Devtools Protocol — Part IV
--
Part IV — Performance domain
In the previous article, we covered the powerful features of the Runtime domain using CDP and how to implement it in Selenium tests. In this article, we will cover the Performance domain and how we can capture performance metrics from the page during our test run.
You can find the complete code at https://github.com/germanbisogno/cdp-utils. Let’s jump into it!
Using Performance domain
The main purpose of the Performance domain is to get metrics from the page. We will be focusing on thegetMetrics
method will allow us to extract runtime metrics during the test execution.
Please, find below some benchmarks that can be used through this API; If you need more information about metrics, you can check the Puppeteer documentation here:
Timestamp
: The timestamp when the metrics sample was taken.Documents
: Number of documents on the page.Frames
: Number of frames on the page.JSEventListeners
: Number of events on the page.Nodes
: Number of DOM nodes on the page.LayoutCount
: The total number of full or partial page layouts.RecalcStyleCount
: The total number of page style recalculations.LayoutDuration
: Combined durations of all page layouts.RecalcStyleDuration
: Combined duration of all page style recalculations.ScriptDuration
: Combined duration of JavaScript execution.TaskDuration
: Combined duration of all tasks performed by the browser.JSHeapUsedSize
: Used JavaScript heap size.JSHeapTotalSize
: Total JavaScript heap size.
If you want to get other web vitals KPIs like LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift), or TBT (Total Blocking Time), you can use Google Lighthouse, which won’t be covered in this article.
How do we automate?
Let’s implement a classPerformance
written in Typescript, which we will extend again fromTraceOperations
, as in previous articles. All npm dependencies used in the last article will be required.
In this last class example, we used Performance.enable
to enable collecting and reporting metrics and Performance.disable
to disable and stop that. Also, we are adding getMetrics
method to get the metrics to return in our test or store them in a file.
NoticeProtocol.Performance.GetMetricsResponse
is the object that contains the metrics we will return in our getMetrics
method. See what this object looks like:
export interface GetMetricsResponse {
/**
* Current values for run-time metrics.
*/
metrics: Metric[];
}
Then, theMetric
interface has the following format:
/**
* Run-time execution metric.
*/export interface Metric {
/**
* Metric name.
*/
name: string; /**
* Metric value.
*/
value: number;
}
For more information about TypeScript definitions for this protocol, please check ‘types/protocol.d.ts.’
Developing a test!
Now let’s write a test that will use the Performance domain and get performance metrics within a user action. The startTrace
and stopTrace
methods come into action again.
As a result, this test will output two files, startTrace.json, and endTrace.json: the former to log the metrics when the tracing starts and the latter when the trace ends. After running the test, you will have two files whose content will look similar to the following:
{ metrics:
[ { name: ‘Timestamp’, value: 1760243.235006 },
{ name: ‘AudioHandlers’, value: 3 },
{ name: ‘Documents’, value: 9 },
{ name: ‘Frames’, value: 4 },
{ name: ‘JSEventListeners’, value: 1455 },
{ name: ‘LayoutObjects’, value: 1745 },
{ name: ‘MediaKeySessions’, value: 0 },
{ name: ‘MediaKeys’, value: 0 },
{ name: ‘Nodes’, value: 2778 },
{ name: ‘Resources’, value: 56 },
{ name: ‘ContextLifecycleStateObservers’, value: 17 },
{ name: ‘V8PerContextDatas’, value: 3 },
{ name: ‘WorkerGlobalScopes’, value: 0 },
{ name: ‘UACSSResources’, value: 0 },
{ name: ‘RTCPeerConnections’, value: 0 },
{ name: ‘ResourceFetchers’, value: 9 },
{ name: ‘AdSubframes’, value: 0 },
{ name: ‘DetachedScriptStates’, value: 1 },
{ name: ‘ArrayBufferContents’, value: 42 },
{ name: ‘LayoutCount’, value: 37 },
{ name: ‘RecalcStyleCount’, value: 115 },
{ name: ‘LayoutDuration’, value: 0.25534 },
{ name: ‘RecalcStyleDuration’, value: 0.092492 },
{ name: ‘DevToolsCommandDuration’, value: 0.14261 },
{ name: ‘ScriptDuration’, value: 0.839995 },
{ name: ‘V8CompileDuration’, value: 0.063373 },
{ name: ‘TaskDuration’, value: 2.691882 },
{ name: ‘TaskOtherDuration’, value: 1.298072 },
{ name: ‘ThreadTime’, value: 1.760892 },
{ name: ‘ProcessTime’, value: 2.734375 },
{ name: ‘JSHeapUsedSize’, value: 17165052 },
{ name: ‘JSHeapTotalSize’, value: 34803712 },
{ name: ‘FirstMeaningfulPaint’, value: 0 },
{ name: ‘DomContentLoaded’, value: 1760241.873843 },
{ name: ‘NavigationStart’, value: 1760241.082286 } ]
}
With this information, you can compare the status of the performance at any moment during your Test Automation.
By having these metrics, you can measure your test step duration using, for exampleTimeStamp
to take the initial and end time or for getting JSHeapUsedSize
which represents the memory usage by JavaScript or evaluates any other metric to ensure the expected performance of your app.
Conclusion
We went into the Performance domain and some of its different features. This domain will help to evaluate metrics from the page and perform the necessary assertions in your Test Automation using the benchmark values returned by this API.
And to wrap up, we completed a set of four articles about CDP and its powerful features. We have covered Tracing, Network, Runtime, and Performance domains.
I invite you to explore more domains by yourself and find how useful this protocol can be in your Test Automation!
I hope you have enjoyed this journey! See you next time!
Thanks for reading!