Locators & Auto-Waiting
Write resilient selectors using accessibility-first locators, and stop writing explicit waits forever thanks to Playwright's actionability checks.
Locators & Auto-Waiting video demo
A practical screen-share style walkthrough for chapter 2, showing how the Playwright idea works in TypeScript and Java.
Briefing focus
The locator priority
This is a structured lesson briefing. Real video/audio can be added later as a media source.
Estimated time
10 min
- 1The locator priority
- 2Auto-waiting explained
- 3Web-first assertions
Transcript brief
Write resilient selectors using accessibility-first locators, and stop writing explicit waits forever thanks to Playwright's actionability checks. The video brief explains the mental model first, then demonstrates the workflow using the course code samples, and finishes with reliability checks you can apply in CI.
Key takeaways
- Translate the concept into a maintainable Playwright test.
- Understand the TypeScript and Java equivalents without changing the test intent.
- Avoid the common source of flaky or slow end-to-end tests for this topic.
The locator priority
Playwright recommends accessibility-first locators because they double as accessibility checks and survive most UI changes.
// Role + accessible name — first choice
await page.getByRole('button', { name: 'Sign in' }).click();
// Label text — great for inputs
await page.getByLabel('Email').fill('alex@qatraining.uk');
// Visible text content
await page.getByText('Forgot password?').click();
// Last resort — a stable test id
await page.getByTestId('quiz-submit').click();
// Chain locators for scope
const row = page.getByRole('row', { name: 'Alex Smith' });
await row.getByRole('button', { name: 'Edit' }).click();Auto-waiting explained
Before every action, Playwright runs a series of actionability checks and re-evaluates them until they pass or the timeout expires. This is why you almost never need an explicit `waitFor`.
| Action | Built-in checks |
|---|---|
| click / dblclick | attached · visible · stable · enabled · receives events |
| fill / type | attached · visible · enabled · editable |
| check / uncheck | attached · visible · stable · enabled · receives events |
| hover | attached · visible · stable · receives events |
| press / focus | attached · focusable |
Stop using sleep()
If you find yourself reaching for waitForTimeout(1000) — stop. Either use an assertion (await expect(...).toBeVisible()) or rely on the action's built-in auto-wait. Fixed timeouts are the #1 cause of flaky E2E tests.
Web-first assertions
Playwright's expect() built-in assertions retry automatically until the matcher passes or the timeout expires. This is the magic that eliminates the ‘try/retry’ helpers you wrote in Selenium.
await expect(page.getByRole('alert')).toHaveText(/saved/i);
await expect(page.getByTestId('cart-count')).toHaveText('3');
await expect(page).toHaveTitle(/dashboard/i);
await expect(page.getByRole('button', { name: 'Submit' })).toBeEnabled();Real-life scenario · Healthcare portal
Killing 300 explicit waits in one afternoon
Situation. A healthcare provider's E2E suite had 300+ custom waitFor helpers around SPA navigations. We replaced them with retrying expect() assertions and role-based locators. 14 flaky tests → 0. Maintenance burden dropped from 6h/week to 30 min/week.
Lesson. Auto-wait + retrying assertions are Playwright's killer features. Adopt them and most ‘flaky’ tests disappear.