What are Locators in Playwright?

In UI automation testing, the first step before interacting with a webpage element is identifying the element uniquely. This identification process is done using Playwright locators.

A locator tells Playwright which element on the page you want to interact with.

For example, when a test script clicks a login button, Playwright must first locate the button element.

Typical actions performed using locators include:

  • Clicking buttons
  • Entering text in input fields
  • Selecting values in dropdowns
  • Verifying text on the page
  • Hovering over menus
  • Drag and drop operations

Example:

await page.locator('#username').fill('admin');

In this example:

  • page represents the current browser page
  • locator('#username') identifies the element
  • fill() types text into the input field

Why Playwright Locators Are Better Than Traditional Selectors

Playwright introduced a modern locator system designed to make tests more reliable.

1. Auto Waiting

Playwright automatically waits for elements to be:

  • present in the DOM
  • visible
  • stable
  • enabled

This eliminates many flaky test failures.

Example:

await page.locator('#login').click();

Playwright will automatically wait until the login button is clickable.


2. Retry Mechanism

If an element is not immediately available, Playwright keeps retrying until timeout.

This helps when dealing with dynamic applications like React or Angular.


3. Readable Test Code

Locators like getByRole() and getByText() make tests easy to understand.

Example:

await page.getByRole('button', { name: 'Login' }).click();

Even non-technical stakeholders can understand this test.


Types of Playwright Locators

Playwright provides multiple locator strategies.

Each strategy has its own use case.


1. ID Locator

ID locators identify elements using the id attribute in HTML.

Example HTML:

<input id="username">

Playwright code:

await page.locator('#username').fill('admin');

Advantages

  • Fast
  • Unique
  • Reliable

Best Practice

Always use id selectors when available because they are usually unique.


2. CSS Selectors

CSS selectors are widely used in UI automation.

Example:

await page.locator('.login-btn').click();

HTML:

<button class="login-btn">Login</button>

Attribute Based CSS

Example:

await page.locator('input[name="email"]').fill('test@gmail.com');

HTML:

<input name="email">

Parent Child CSS

await page.locator('.form-container button').click();

This finds a button inside .form-container.


3. XPath Locators

XPath is another way to identify elements.

Example:

await page.locator('//button[text()="Login"]').click();

When XPath is Useful

  • Complex DOM structures
  • No unique attributes available
  • Navigating between parent and child elements

Disadvantages

  • Slower than CSS
  • Harder to maintain
  • Can break easily if DOM structure changes

4. Text Locators

Playwright allows locating elements based on visible text.

Example:

await page.getByText('Login').click();

This finds an element that displays the text Login.

When to Use

  • Buttons
  • Links
  • Menu items

5. Role Locators (Highly Recommended)

Playwright supports locating elements based on ARIA roles.

Example:

await page.getByRole('button', { name: 'Login' }).click();

Advantages

  • Very stable
  • Accessible testing
  • Recommended by Playwright

Common roles:

  • button
  • textbox
  • checkbox
  • link
  • heading

6. Placeholder Locator

Many input fields contain placeholder text.

Example HTML:

<input placeholder="Enter email">

Playwright:

await page.getByPlaceholder('Enter email').fill('test@gmail.com');

7. Label Locator

Forms often associate labels with input fields.

Example HTML:

<label>Password</label>
<input type="password">

Playwright:

await page.getByLabel('Password').fill('123456');

8. TestId Locator (Best Practice)

Modern applications often include test attributes specifically for automation.

Example HTML:

<button data-testid="login-button">Login</button>

Playwright code:

await page.getByTestId('login-button').click();

Why TestId Is Best

  • Independent of UI changes
  • Stable tests
  • Recommended for automation frameworks

Locator Chaining in Playwright

1. What is Locator Chaining?

Locator Chaining in Playwright means finding a child element inside another element by linking locators together.

Instead of searching the entire page for an element, you first locate a parent element and then search within that parent for the required child element.

This helps make tests:

  • More reliable
  • More readable
  • Less ambiguous
  • Easier to maintain

Basic Idea

Instead of this:

page.locator('button')

You narrow the search like this:

page.locator('#login-form').locator('button')

Here:

  • #login-form → parent locator
  • button → child locator

Playwright will only search for the button inside the login form, not across the whole page.


2. Why Locator Chaining is Important

Modern web pages often contain multiple elements with the same selector.

For example if you navigate to the product page in demo site:

Playwright locators

If you try to click on add to cart button for playwright book by writing:

page.locator('button:has-text("Add to Cart")')

Playwright may find multiple buttons, which can cause:

  • strict mode errors
  • wrong element interactions

Locator chaining solves this problem by scoping the search.

Example:

await page
  .locator('.product')
  .filter({ hasText: 'Playwright Book' })
  .locator('button')
  .click();

Now Playwright only searches inside the product card.


3. Basic Syntax of Locator Chaining

Syntax

page.locator('parent_locator').locator('child_locator')

Example

await page
      .locator('#login-form')
      .locator('#username')
      .fill('admin');

Explanation:

  1. Find element with id login-form
  2. Inside it find #username
  3. Fill the textbox

4. Real World Example

HTML Structure

<div class="product">
   <h2>iPhone 15</h2>
   <button>Add to Cart</button>
</div>

<div class="product">
   <h2>Samsung S24</h2>
   <button>Add to Cart</button>
</div>

If you write:

await page.locator('button:has-text("Add to Cart")').click();

Playwright will not know which product button to click.

Using Locator Chaining

await page
     .locator('.product', { hasText: 'iPhone 15' })
     .locator('button')
     .click();

Steps:

  1. Find product container containing text iPhone 15
  2. Inside that container locate button
  3. Click the button

Now the script interacts with the correct product.


5. Multiple Level Locator Chaining

Locator chaining can go multiple levels deep.

Example HTML

<div id="dashboard">
   <div class="card">
       <div class="card-body">
           <button>Submit</button>
       </div>
   </div>
</div>

Playwright Code

await page
     .locator('#dashboard')
     .locator('.card')
     .locator('.card-body')
     .locator('button')
     .click();

Flow:

dashboard
   ↓
card
   ↓
card-body
   ↓
button

Playwright searches step by step inside each parent.


6. Locator Chaining with getBy Locators

Locator chaining also works with Playwright’s recommended locators.

Example:

await page
     .getByRole('form')
     .getByLabel('Email')
     .fill('test@example.com');

Example 2:

await page
     .getByTestId('product-card')
     .getByRole('button', { name: 'Add to Cart' })
     .click();

This approach is more readable and stable.


7. Locator Chaining with Filters

Playwright allows filtering elements using has() and hasText().

Example

await page
     .locator('.product', { hasText: 'iPhone' })
     .locator('button')
     .click();

Or

await page
     .locator('.product')
     .filter({ hasText: 'iPhone' })
     .locator('button')
     .click();

Meaning:

  1. Find product containing text iPhone
  2. Inside that product click button

8. Locator Chaining vs Single Complex Selector

Without Chaining

page.locator('#login-form input[name="username"]')

With Chaining

page.locator('#login-form').locator('input[name="username"]')

Why chaining is better

Without ChainingWith Chaining
Harder to readEasy to understand
Large selectorsStructured approach
Harder to debugEasy to debug
FragileMore stable

9. Locator Chaining in Page Object Model

Locator chaining works very well with Page Object Model (POM).

Example:

class ProductsPage {

  constructor(page){
      this.page = page;
      this.productCard = page.locator('.product-card');
  }

  async addProduct(productName){
      await this.productCard
           .filter({ hasText: productName })
           .locator('button')
           .click();
  }
}

Usage:

await productsPage.addProduct('iPhone 15');

Benefits:

  • Cleaner code
  • Reusable locators
  • Easy maintenance

10. Best Practices for Locator Chaining

a. Use meaningful parent locators

Good:

page.locator('.product-card').locator('button')

Bad:

page.locator('div').locator('button')

b. Avoid too many levels

Too deep chaining can make tests fragile.

Bad:

page.locator('#a').locator('#b').locator('#c').locator('#d')

c. Prefer Playwright locators

Best approach:

page.getByTestId('product-card')
    .getByRole('button', { name: 'Add to Cart' })


Best Practices for Locators

Use this priority order:

1️⃣ getByRole
2️⃣ getByTestId
3️⃣ id
4️⃣ CSS selectors
5️⃣ XPath (last option)

Leave a Comment

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