Playwright Locators Guide: getByRole, getByText, getByLabel, CSS & XPath

Published: (February 9, 2026 at 03:46 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Introduction

Flakiness is the silent killer of test confidence. A test suite that passes one moment and fails the next destroys trust.

At the heart of most test instability lies poor element selection. Your test clicks a button using div.btn-primary, then fails when someone updates the CSS. It finds an element by its position in the DOM, then breaks when a new feature adds content above it. It waits exactly 2 seconds for data to load, then times out when the API is slow. Every UI update becomes a cascade of test failures, turning maintenance into a full‑time job.

Playwright locators address these fundamental problems through a different approach: auto‑waiting that eliminates timing issues, strict matching that prevents ambiguous selections, and user‑facing selectors that remain stable across implementation changes. Instead of brittle CSS selectors and XPath queries, Playwright encourages you to find elements the way users do, by their role, text, and labels.

This guide explores the entire spectrum of Playwright locators, from the highly recommended user‑facing selectors like getByRole to the necessary fallbacks of CSS and XPath, empowering you to build a test automation suite that is not just functional but truly reliable.

Playwright Locator Strategy: Think Like a User, Not the DOM

Traditional test frameworks force you to think like a computer: find an element by its position in the DOM, its CSS class, or its internal ID. But applications are constantly in flux. Classes change with styling updates. DOM structures shift with framework upgrades. IDs get regenerated in each build. If your tests are coupled tightly to any of these, any UI update triggers cascading failures.

The most common sources of locator flakiness include:

  • Timing issues – Elements that aren’t ready when you try to interact with them. Modern web apps are dynamic, with components rendering asynchronously, data loading from APIs, and animations changing element positions. A button might exist in the DOM but be disabled until data loads. A modal might be present but still sliding into view.
  • Ambiguous selections – Multiple elements matching your selector. This is especially problematic in lists, grids, and repeated components. Your test might pass locally, where there’s one “Delete” button, but fail in staging, where there are twenty.
  • Implementation coupling – Selectors tied to implementation details rather than user‑visible behavior. When you select by div.btn-primary-new-style, you’re testing the implementation, not the user experience. Every refactor becomes a test maintenance burden.
  • Dynamic content – Elements that change based on data, user state, or time. A dashboard that shows different widgets based on user permissions. A form that reveals fields progressively. Content that updates in real‑time.

Playwright addresses these issues with its modern, opinionated approach to element selection. Unlike traditional methods that query the DOM directly, Playwright locators represent a paradigm shift in how we write automated tests.

Playwright Auto‑Waiting Explained

One of the most common sources of test flakiness is timing. When apps are dynamic, with elements rendered, hydrated, and enabled asynchronously, a traditional test script might try to click a button before it’s interactive, leading to failure.

Playwright’s auto‑waiting mechanism solves this. When you perform an action on a locator, Playwright automatically performs a series of checks before executing the action. Playwright auto‑waits so actions run only when the locator resolves to exactly one visible element, stable, receives events, and is enabled (for fill, also editable).

test('auto-waiting prevents timing issues', async ({ page }) => {
  // No need for explicit waits
  await page.goto('/dashboard');

  // Playwright waits for button to be actionable
  await page.getByRole('button', { name: 'Load Report' }).click();

  // Even with dynamic content loading
  await expect(page.getByRole('table', { name: 'Sales Data' }))
    .toBeVisible();
});
0 views
Back to Blog

Related posts

Read more »

Playwright Codegen

Playwright & Codegen – Como gravar testes de integração em .NET Playwright é uma das bibliotecas de teste end‑to‑end mais completas do mercado: suporta os prin...