k6 icon indicating copy to clipboard operation
k6 copied to clipboard

Role based selector engine for k6 browser

Open ankur22 opened this issue 6 months ago • 1 comments

Feature Description

The current way of working with elements in a frontend/browser test is to use CSS or XPath selectors. Users are finding that these selectors are both cumbersome and not maintainable when a website changes. These selectors usually rely on the DOM structure which can quite easily change, especially since new frontend frameworks will usually "compile" down to HTML, while changing many of the attributes on elements in the process.

Another issue is when user want to be able to easily migrate from Playwright to k6, they find that difficult too since we don't have some core APIs that are ubiquitous among newer Playwright scripts.

We need a more maintainable way of working with selectors.

Suggested Solution (optional)

The proposal is to work with the role based selector engine, one that behaves exactly like in Playwright. This will involve two steps:

  1. Implement the role based selector engine in the injected script, like we have already done for the XPath and CSS selector engines.
  2. ~~Implement the FrameLocator class, which is essentially a wrapper around the various selector engines.~~ The one to focus on is getByRole, but it's worth exploring all the other APIs in that class too.

Already existing or connected issues / PRs (optional)

I have worked on a POC. I basically copied over the injected script from Playwright and pasted it into the k6 injected script. It does seem to work with the following test script:

import { browser } from 'k6/browser'

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      options: {
        browser: {
          type: 'chromium',
        },
      },
    },
  },
}

export default async function () {
  const page = await browser.newPage()

  await page.goto('https://quickpizza.grafana.com/')

  await page.locator(
    'role=button[name="Pizza, Please!"]'
  ).click();

  await page.waitForTimeout(2000);

  await page.close();
}

ankur22 avatar May 02 '25 14:05 ankur22