Playwright Auto-Wait & Selectors
Playwright's auto-wait mechanism is its most powerful feature — it eliminates 80% of the flakiness that plagues Selenium tests. Understanding HOW auto-wait works, and which locator strategies make tests most readable and maintainable, is essential for writing professional Playwright test suites.
Auto-Wait Explained + Playwright Locators
import { test, expect, Locator } from '@playwright/test';
// ══════════════════════════════════════════════════════════════
// AUTO-WAIT: What it checks before EVERY action
// ══════════════════════════════════════════════════════════════
// Before page.click(selector), Playwright waits until:
// - Element exists in DOM
// - Element is visible (not hidden/display:none)
// - Element is enabled (not disabled)
// - Element is stable (not animating/moving)
// - Element receives the click (no overlay blocking it)
// This replaces 90% of explicit waits from Selenium.
// ══════════════════════════════════════════════════════════════
// PLAYWRIGHT LOCATOR STRATEGIES (priority order)
// ══════════════════════════════════════════════════════════════
test('locator examples', async ({ page }) => {
await page.goto('/app');
// 1. ROLE-BASED (most semantic, most resilient)
await page.getByRole('button', { name: 'Sign In' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('alice@test.com');
await page.getByRole('heading', { name: 'Dashboard' });
await page.getByRole('link', { name: 'Forgot Password' }).click();
// 2. TEXT (for static text content)
await page.getByText('Welcome, Alice').waitFor();
await page.getByText('Order confirmed').click();
// 3. LABEL (finds input associated with the label — best for forms)
await page.getByLabel('Email address').fill('alice@test.com');
await page.getByLabel('Password').fill('Test@1234');
// 4. PLACEHOLDER
await page.getByPlaceholder('Search products...').fill('laptop');
// 5. TEST ID (robust, add data-testid attrs in HTML)
await page.getByTestId('submit-button').click();
await page.getByTestId('product-list').locator('.item').first();
// 6. CSS/XPath (use as last resort)
await page.locator('.product-card:nth-child(2)').click();
await page.locator('//button[contains(@class, "primary")]');
// CHAINING LOCATORS (scope to a container)
const cart = page.locator('[data-testid="shopping-cart"]');
const firstItem = cart.locator('.cart-item').first();
await firstItem.locator('.remove-btn').click();
// FILTERING LOCATORS
const products = page.locator('.product-card');
await products.filter({ hasText: 'iPhone' }).click(); // Find iPhone card specifically
// COUNT ELEMENTS
const count = await products.count();
expect(count).toBeGreaterThan(0);
});Common Mistakes
- Using CSS class locators from UI frameworks — Tailwind/MUI generate dynamic class names; stick to role or data-testid
- Not using chained locators — page.locator('.remove-btn') on a page with 10 items is ambiguous; scope to cart.locator('.remove-btn').first()
- Mixing Playwright and manual await waits — if you're manually awaiting element visibility, you're likely using the wrong locator strategy
- Using getByText for partial matches — getByText('Order') matches any element containing 'Order'; use { exact: true } for precision
Tip
Tip
Practice Playwright AutoWait Selectors in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Test CRUD + errors + auth.
Practice Task
Note
Practice Task — (1) Write a working example of Playwright AutoWait Selectors from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Playwright AutoWait Selectors is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- Playwright's auto-wait mechanism is its most powerful feature — it eliminates 80% of the flakiness that plagues Selenium tests.
- Using CSS class locators from UI frameworks — Tailwind/MUI generate dynamic class names; stick to role or data-testid
- Not using chained locators — page.locator('.remove-btn') on a page with 10 items is ambiguous; scope to cart.locator('.remove-btn').first()
- Mixing Playwright and manual await waits — if you're manually awaiting element visibility, you're likely using the wrong locator strategy