grafana / xk6-browser

The browser module adds support for browser automation and end-to-end web testing via the Chrome Devtools Protocol to k6.
https://grafana.com/docs/k6/latest/javascript-api/k6-browser/
GNU Affero General Public License v3.0
344 stars 41 forks source link

Adding the ability to work with Performance CDP API #1532

Open ankur22 opened 3 weeks ago

ankur22 commented 3 weeks ago

Feature Description

A question that is being asked more frequently is how does my website or component affect the web browser (chrome)? So what metrics can we bring about from chrome to show the end user?

Suggested Solution (optional)

Fortunately there is CDP API that can perform this action, which is the performance API. It works by:

  1. Calling Performance.enable
  2. Running the some k6 browser APIs
  3. Calling either Performance.getMetrics to retrieve the metrics
  4. Or calling Performance.disable to end it.

This will result in the following:

[
  { name: 'Timestamp', value: 139939.973695 },
  { name: 'AudioHandlers', value: 0 },
  { name: 'AudioWorkletProcessors', value: 0 },
  { name: 'Documents', value: 4 },
  { name: 'Frames', value: 1 },
  { name: 'JSEventListeners', value: 51 },
  { name: 'LayoutObjects', value: 1712 },
  { name: 'MediaKeySessions', value: 0 },
  { name: 'MediaKeys', value: 0 },
  { name: 'Nodes', value: 1845 },
  { name: 'Resources', value: 4 },
  { name: 'ContextLifecycleStateObservers', value: 19 },
  { name: 'V8PerContextDatas', value: 6 },
  { name: 'WorkerGlobalScopes', value: 0 },
  { name: 'UACSSResources', value: 0 },
  { name: 'RTCPeerConnections', value: 0 },
  { name: 'ResourceFetchers', value: 4 },
  { name: 'AdSubframes', value: 0 },
  { name: 'DetachedScriptStates', value: 4 },
  { name: 'ArrayBufferContents', value: 0 },
  { name: 'LayoutCount', value: 3 },
  { name: 'RecalcStyleCount', value: 7 },
  { name: 'LayoutDuration', value: 0.019831 },
  { name: 'RecalcStyleDuration', value: 0.005009 },
  { name: 'DevToolsCommandDuration', value: 0.009659 },
  { name: 'ScriptDuration', value: 0.012199 },
  { name: 'V8CompileDuration', value: 0.000039 },
  { name: 'TaskDuration', value: 0.083739 },
  { name: 'TaskOtherDuration', value: 0.037002 },
  { name: 'ThreadTime', value: 0.067871 },
  { name: 'ProcessTime', value: 0.192818 },
  { name: 'JSHeapUsedSize', value: 2970160 },
  { name: 'JSHeapTotalSize', value: 5275648 },
  { name: 'FirstMeaningfulPaint', value: 0 },
  { name: 'DomContentLoaded', value: 139939.96493 },
  { name: 'NavigationStart', value: 139939.854245 }
]

Here's a PW script that can be used to test this:

// @ts-check
import { test, chromium } from '@playwright/test';

test('nav_to_test.k6.io', async () => {
  const browser = await chromium.launch()
  const context = await browser.newContext()
  const page = await context.newPage()

  const client = await page.context().newCDPSession(page);
  client.send('Performance.enable');

  await page.goto('https://test.k6.io', {waitUntil: 'networkidle'});
  // Requires some interaction to bring about INP and FID Web Vitals
  await page.locator('a[href="/news.php"]').click();

  const result = await client.send('Performance.getMetrics');
  console.log(result.metrics);

  await page.close({ runBeforeUnload: true })

  await browser.close()
});

In Playwright and Puppeteer, there ins't a dedicated API to perform such an action, instead they allow the use of CDPSession which can be used to run raw CDP requests. It's not clear what the security implications are of opening up a CDPSession like this.

The options for us are:

  1. Create a dedicated API to retrieve these performance metrics, basically a wrapper around the Performance CDP API.
  2. Implement CDPSession but only allow some of the API calls (e.g. only the Performance ones) to be made through it until it's clear what security implications it may have.

Already existing or connected issues / PRs (optional)

No response