What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
For reliable Playwright tests, identify elements the way a user or assistive technology would: use getByRole() with an accessible name for controls, or getByLabel() for labeled fields. Then narrow repeated matches by meaningful context until the locator identifies exactly the intended element. Playwright’s auto-waiting helps an element become ready; it cannot make the wrong or ambiguous locator correct.
How Playwright locators work
A locator is a query that Playwright resolves when it is used. If the DOM changes between uses, Playwright can resolve it again against the current page. The official Playwright locator guide calls locators “the central piece of Playwright’s auto-waiting and retry-ability.” This makes locators more resilient to page updates than holding on to a particular element instance, but locator quality still depends on what the query describes.
As an Amazon Associate I earn from qualifying purchases.
For an operation that expects one target, Playwright’s strictness is a useful check: multiple matches produce an error instead of silently choosing one. Aim for a locator that communicates both the target and, where needed, the context that makes it unique.
Choose a locator that matches what the test should verify
| Target or test intent | Locator to start with | What it expresses |
|---|---|---|
| Button, link, or another semantic control | getByRole(role, { name }) |
The element’s accessible role and name, which are part of the interface contract. |
| Form field with an associated label | getByLabel() |
The label users rely on to identify the control. |
| Non-interactive content identified by visible copy | getByText() |
Text on the page; exact strings and regular expressions are supported, and whitespace is normalized. |
| Input with meaningful placeholder text | getByPlaceholder() |
The placeholder attribute. It is a targeting option, not a substitute for providing a proper label in accessible UI. |
| Image or element whose alternative text or title is the intended contract | getByAltText() or getByTitle() |
The relevant attribute value. |
| Explicit internal testing contract | getByTestId() |
A test ID deliberately assigned by the application. It does not check that the user-facing role or name is correct. |
| Structure itself is the behavior under test, or no suitable built-in fits | locator() with CSS or XPath |
A structural query. Avoid tying it to incidental classes or long, deeply nested paths when a meaningful locator is available. |
Playwright recommends role locators because they reflect how users and assistive technology perceive a page. Use a test ID when a stable internal contract is what the test needs; use role or text when the interface property itself should be checked. There is no universally best locator form independent of the element and test intent. See the official Playwright best-practices guide for related guidance.
Build a locator that identifies one intended element
Start with role and accessible name
For an interactive control with a meaningful accessible name, make the role and name explicit:
await page.getByRole('button', { name: 'Save' }).click();
This tests more than the existence of some button: it targets the control users encounter as “Save.” If the name is not distinctive, scope the locator to the relevant section or use another meaningful distinction.
Use a field’s label when filling a form
When a form control has an associated label, target that label rather than relying on a position or incidental DOM detail:
Recommended Free Tools
await page.getByLabel('Email').fill('[email protected]');
Scope repeated items by their content
Lists and cards commonly contain repeated controls such as “Add to cart.” First locate the intended item by a meaningful heading, then find the control within that item. The has filter is evaluated relative to the outer locator, so keep the inner locator scoped to the item:
const card = page
.getByRole('listitem')
.filter({ has: page.getByRole('heading', { name: 'Product 2' }) });
await expect(card).toHaveCount(1);
await card.getByRole('button', { name: 'Add to cart' }).click();
The count assertion makes the uniqueness requirement explicit. Use it when exactly one match is part of the test’s contract, rather than as a substitute for a locator that distinguishes the intended item.
Use a test ID when an internal contract is the right contract
A test ID can remain stable when copy or page structure changes. That is useful when the test needs a deliberate internal target, but it can also let a test pass after the visible name or semantic role regresses. For example:
await page.getByTestId('checkout-submit').click();
If users depend on the button’s name or role, test that user-facing property with a role/name locator instead of relying only on the test ID.
Free tools Windows power users keep installed
One-click scans. No signup required.
Reserve positional and structural selectors for intentional cases
first(), last(), and nth() select by current position. A change in page order can make the test act on a different element without making the locator fail. Use position only when order is itself what the test verifies, or when no better distinguishing locator exists. Similarly, CSS and XPath are appropriate when structure is the intended contract or semantic locators do not fit, but long chains based on incidental classes and nesting are coupled to implementation details.
Understand strict mode and auto-waiting
A strict mode violation means a single-target operation found more than one matching element. Add a meaningful accessible name, scope to a dialog, card, row, or other relevant parent, or filter by distinguishing text or a child locator. If the test depends on there being exactly one match, assert toHaveCount(1). Do not silence ambiguity with first() unless selecting the first item is actually the behavior being tested.
Rank #4
For a click, Playwright waits for a unique target that is visible, stable, enabled, and able to receive events. If those checks do not pass before the timeout, the action fails. The actionability guide describes these checks. Waiting can address transient readiness; it does not fix a selector that identifies the wrong element or several elements.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshoot locator failures
The action times out
- Check that the locator targets the intended element and that the page reached the expected state.
- For a click, determine which required condition is failing: uniqueness, visibility, stability, event reception, or enabled state.
- Increase a timeout only when the application legitimately needs more time. A timeout by itself does not show that a longer wait is the right fix.
Playwright reports a strict mode violation
- Inspect what the locator matches, then add a distinguishing name or scope it to a meaningful parent such as the relevant dialog or card.
- Use a filter such as
hasTextorhasto express the distinction that matters. - Assert a count of one if uniqueness is an explicit test invariant. Use positional selection only if position is part of the intended contract.
A redesign breaks the test
Check whether the locator relies on an implementation detail such as a CSS class or a deep DOM path. Replace it with a role/name or another meaningful user-facing property when that is the contract under test. If the test needs an internal target instead, have the application provide a deliberate test ID contract.
The test passes but misses a visible regression
Review whether a test ID is the only thing the assertion verifies. Since a test ID is not user-facing, it may remain unchanged when copy or role changes. If the visible name or semantic role matters to users, assert that property with a user-facing locator.
Best Value
Or skip the browser setup
For an actual website screenshot rather than an in-browser Playwright test, ScreenshotNeo is a website screenshot API and MCP server. A single GET request can return an image or PDF; it handles consent banners and removes supported popups and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and each response identifies the page verdict and billing status. Its MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
See the ScreenshotNeo API documentation for parameters and response details. Example cURL request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




