Meaningful locators are part of reliable browser testing

Arni Mittal··1 min read

A test tied to a styling class can break when a page is restyled, which is what people usually mean by selector brittleness. A test tied to a control's role and label often expresses the intended action more clearly:

// Breaks when the class names change
await page.locator('.btn-primary.checkout-submit').click();

// Says what the user is doing
await page.getByRole('button', { name: 'Place order' }).click();

This isn't unique to AI tooling. The Playwright documentation includes locators based on how users identify elements, and if you have an existing suite, it's worth trying those before assuming a locator problem needs a new platform.

A readable locator can still be the wrong one

The remaining question is whether the target is the right one. Two buttons can share a label. A list of products might have a "Remove" button on every row. A replaced component may represent a different thing that happens to have the same name.

Context helps with this:

await page
  .getByRole('row', { name: 'Notebook' })
  .getByRole('button', { name: 'Remove' })
  .click();

So does checking the result. Successfully clicking something isn't the same as verifying the intended customer flow, so finish with an assertion on what should have happened, such as the row disappearing and the total changing.

The same applies to plain-English steps

In Zerocheck, you write that step as Click "Remove" in the "Notebook" row. The browser agent finds the control from the page, and a remembered target is only reused while it still looks like the same control. If it moves, the run shows the old and new target.

The English doesn't remove the need to be specific, and the final check still does the real verification.

Try it on your own app

US$500 in credits at sign up, with a dedicated instance and hands-on onboarding.

Book a setup call