|

Playwright Locators Masterclass: All 18 Strategies With Real Code Examples

Playwright offers 18 different locator strategies β€” but most engineers only use 3 or 4. Choosing the right locator is the difference between a test suite that survives UI changes and one that breaks every sprint.

🎭 Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.

Contents

Tier 1: Semantic Locators (Always Prefer These)

These locators are based on accessibility roles and visible content. They are the most resilient to DOM changes.

// 1. getByRole β€” BEST choice, ARIA-based
await page.getByRole('button', { name: 'Submit' }).click();

// 2. getByLabel β€” for form inputs with labels
await page.getByLabel('Email address').fill('test@example.com');

// 3. getByText β€” matches visible text
await page.getByText('Welcome back').click();

// 4. getByPlaceholder β€” for inputs with placeholder text
await page.getByPlaceholder('Search...').fill('playwright');

// 5. getByAltText β€” for images
await page.getByAltText('Company logo').click();

// 6. getByTitle β€” for elements with title attribute
await page.getByTitle('Close dialog').click();

Tier 2: Test ID Locators (Best for Dynamic Content)

// 7. getByTestId β€” uses data-testid attribute
await page.getByTestId('submit-button').click();

// Configure custom test ID attribute
// playwright.config.ts: use: { testIdAttribute: 'data-qa' }

πŸš€ Level Up Your Playwright

From locators to CI pipelines β€” build a production-grade Playwright + TypeScript framework step by step.

Tier 3: CSS and XPath (Use Sparingly)

// 8. CSS selector
await page.locator('.btn-primary').click();

// 9. CSS with pseudo-classes
await page.locator('button:has-text("Submit")').click();
await page.locator('div:visible').click();

// 10. XPath
await page.locator('xpath=//button[@type="submit"]').click();

// 11. CSS with nth-child
await page.locator('li:nth-child(3)').click();

Tier 4: Advanced Locators

// 12. filter() β€” chain conditions
await page.getByRole('listitem').filter({ hasText: 'Active' }).click();

// 13. .and() β€” combine locators
const button = page.getByRole('button').and(page.getByText('Save'));

// 14. .or() β€” match either
const cta = page.getByRole('button', { name: 'Buy' }).or(page.getByRole('button', { name: 'Purchase' }));

// 15. frameLocator() β€” for iframes
await page.frameLocator('#payment-iframe').getByRole('button', { name: 'Pay' }).click();

// 16. Shadow DOM β€” pierces automatically
await page.locator('my-component').getByRole('button').click();

// 17. Layout-based (left-of, right-of, above, below, near)
await page.getByText('Price').locator('xpath=..').getByRole('textbox').fill('100');

// 18. has/hasNot filtering
await page.locator('div', { has: page.getByText('Error') }).screenshot();

Locator Decision Tree

  1. Does the element have a visible role? Use getByRole()
  2. Is it a labeled form field? Use getByLabel()
  3. Does it have unique visible text? Use getByText()
  4. Does your team use data-testid? Use getByTestId()
  5. None of the above? Use CSS selector as last resort
  6. Never use XPath unless absolutely necessary

πŸŽ“ Master Playwright End to End

Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.