Skip to contentPlayForge

What does strict mode mean for Playwright locators?

Written by Jatin Thacker, Automation Architect · Updated

Strict mode means a locator used for an action, or for an assertion about one element, must match exactly one element; if it matches two or more, Playwright throws a strict mode violation instead of guessing. Fix it by making the locator say which element you mean, for example with exact: true or by finding the element inside its card.

This answer is one piece of Chapter 9: Locators I: What the User Sees.

  • getByRole and the Accessible Name
  • getByText, getByLabel, getByPlaceholder, getByTestId
  • Strict Mode — One Locator, One Element

Locators are strict: any operation that implies a single target element throws when more than one element matches. That covers actions such as click() and fill(), and single-element assertions such as toBeVisible(). Operations about many elements, such as count() and toHaveCount(), are fine.

Why it exists. Without it, a click on an ambiguous locator would land on whichever element came first, and the test would pass while doing the wrong thing. The error lists every match with a suggested locator for each, which is often the quickest fix.

A common cause is a name that is part of another name. By default getByRole('heading', { name: 'Halcyon Burr Grinder' }) matches the name as a case-insensitive substring, so it finds both 'Halcyon Burr Grinder' and 'Halcyon Burr Grinder Mini'.

The fixes, best first:

  1. Say exactly which one: { name: 'Halcyon Burr Grinder', exact: true }.
  2. Find the container, then act inside it: filter the product cards by that exact heading and click the card's own 'Add to cart' button.
  3. Use .first() or .nth() only when position really is the requirement, such as 'the newest row'. Otherwise the test quietly picks a different element when the order changes.
strict.spec.js(https://practice.learnplayforge.com)
import { test, expect } from '@playwright/test'; test('strict mode: one element, or an error', async ({ page }) => { await page.goto('/catalog.html?mock=static&seed=42&autologin=1'); // The name matches as a substring: 'Halcyon Burr Grinder' and 'Halcyon Burr Grinder Mini' const grinder = page.getByRole('heading', { name: 'Halcyon Burr Grinder' }); await expect(grinder.click()).rejects.toThrow(/strict mode violation/); await expect(expect(grinder).toBeVisible()).rejects.toThrow(/strict mode violation/); await expect(grinder).toHaveCount(2); // Fix 1: say exactly which one await expect(page.getByRole('heading', { name: 'Halcyon Burr Grinder', exact: true })).toBeVisible(); // Fix 2: find the card, then act inside it const card = page.getByTestId('product-card') .filter({ has: page.getByRole('heading', { name: 'Halcyon Burr Grinder', exact: true }) }); await card.getByRole('button', { name: 'Add to cart' }).click(); await expect(page.getByTestId('cart-row')).toHaveCount(1); });
Output
$ npx playwright test strict.spec.js
  ok 1 [chromium] › strict.spec.js:3:1 › strict mode: one element, or an error (1.3s)
  1 passed (2.5s)

# The unscoped click on its own fails like this:
Error: locator.click: Error: strict mode violation: getByRole('heading', { name: 'Halcyon Burr Grinder' }) resolved to 2 elements:
    1) <h3>Halcyon Burr Grinder</h3> aka getByRole('heading', { name: 'Halcyon Burr Grinder', exact: true })
    2) <h3>Halcyon Burr Grinder Mini</h3> aka getByRole('heading', { name: 'Halcyon Burr Grinder Mini' })
Test yourself
There are 5 buttons on the page with text 'Delete'. What happens when you call await page.getByRole('button', { name: 'Delete' }).click()?

Practised in the full course: the drill View details — North.

See the chapter

AI can write the test now. The job is knowing whether it checks anything. That's what the course teaches, one chapter at a time.

163 lessons · 226 drills · start free

← All questions

Training a team? Talk to us.