Role, text, and test ID selectors are three of the most powerful patterns in Playwright. They let you express user intent clearly, whether you are selecting a button by its accessible name or a component by a stable data attribute.
Using Role and Text Selectors
Role-based selectors use the ARIA role and accessible name of elements. Text selectors match visible text nodes. These are ideal when the UI has clear labels and headings.
// role-text-testid.spec.ts
import { test, expect } from '@playwright/test';
test('checkout button using role and text', async ({ page }) => {
await page.goto('https://demo.myshop.com/cart');
// Role + name for the primary checkout button.
await page.getByRole('button', { name: 'Proceed to checkout' }).click();
// Verify we navigated to the checkout page.
await expect(page.getByRole('heading', { name: 'Checkout' })).toBeVisible();
});
page.getByText('Welcome back') work best when the text is unique and stable; avoid ambiguous phrases that appear many times.Using Data Test IDs
When you need a selector that is stable regardless of visual changes, data-testid attributes are a good option. They are ignored by end users but exposed in the DOM for tests.
// Example HTML
// <button data-testid="primary-checkout">Proceed to checkout</button>
// Playwright test
await page.getByTestId('primary-checkout').click();
Agreeing on a naming convention for test IDs helps keep them consistent across the app, such as entity-action or page-element-purpose.
Common Mistakes
Mistake 1 โ Overusing text selectors for everything
This can be fragile.
โ Wrong: Selecting form fields only by their placeholder text, which UX may change.
โ Correct: Combine labels, roles, or test IDs for critical elements.
Mistake 2 โ Adding test IDs without any convention
This becomes messy.
โ Wrong: Using random, inconsistent values like data-testid="x1" and "btn123".
โ Correct: Define a clear naming pattern and document it for the team.