THE SHORT VERSION
Inspect the failing step before rewriting the locator. Check what matched, what the action waited for, and what happened next. A changed selector will not fix missing test data or a control the app never enables.
Start with what the failure tells you.
Select the symptom closest to your run. Each path gives you checks and a code example to adapt. These are investigation steps; they do not diagnose your test automatically.
Check that the expected UI is there.
A missing match can mean the locator changed, the page is in a different state, or the target is in another frame. Start with the screen at the failing step.
- Confirm the URL, account permissions, test data, and actions that open the target.
- Compare the actual role, accessible name, and attributes with the locator.
- Check the frame or container being searched. A correct selector in the wrong scope still misses.
Identify which control you mean.
A single-target action cannot choose between matching elements. Another Save button, a repeated card, or an overly broad name can make a previously unique locator ambiguous.
- Inspect all matches. Identify the specific dialog, row, or panel for this action.
- Narrow by that meaningful container, then locate the control within it.
- Use first() or nth() only when position is part of the requirement.
Read what the action is waiting for.
Finding one element is only part of a click. Playwright also checks that it is visible, stable, enabled, and able to receive the click.
- Look for a disabled state, moving element, or overlay in the action log and snapshot.
- Check which application step should make the control ready, such as completing required fields.
- Investigate the unmet condition before adding a sleep or forcing the click.
Verify the target and the result.
A click can succeed on the wrong control. It can also reach the right control before the app is ready to handle it, or be followed by a failed request.
- Check the action snapshot to see which element received the click.
- Inspect application readiness, console errors, and the relevant request or response.
- Assert the intended outcome so a completed click is not mistaken for a completed task.
const submit = page.getByRole('button', {
name: 'Place order',
exact: true,
});
// A snapshot of matches right now, not a wait.
console.log('Current matches:', await submit.count());
await expect(submit).toHaveCount(1);Adapt the sample names to your app. Use inside a test with page ready and expect imported from @playwright/test.
count() reports the current matches. The toHaveCount assertion retries until its assertion timeout; neither proves why the element was missing.
Match counts and retrying assertionsOne wrapper. A different result.
The selector below requires a button directly inside .checkout. Adding a layout wrapper preserves the visible action but breaks that relationship.
page.locator('.checkout > button')<form class="checkout">
<button type="button">Place order</button>
</form><form class="checkout">
<div class="actions">
<button type="button">Place order</button>
</div>
</form>Authored example. The counts describe this markup and the original selector.
Choose a dependency that matches the test’s intent.
If the requirement is to use the Place order button, its role and accessible name can express that action without depending on the extra wrapper. This example has one such button; use a meaningful container when your app has more than one.
const submit = page.getByRole('button', {
name: 'Place order',
exact: true,
});
await expect(submit).toHaveCount(1);
await submit.click();The name and role still need to stay meaningful. This removes the wrapper dependency; it does not make the locator immune to every UI change. Add an assertion for the result of placing the order.
See Playwright’s locator best practices.
Check what the locator depends on.
getByRoleA name or role changed.
Read the accessible name, including ARIA naming attributes. It may differ from the text you see. Confirm whether a copy, localization, or semantic change should also change the test.
getByTestIdThe test identity changed.
Check the attribute configured by testIdAttribute, the value in the current markup, and any repeated components. An ID is an agreement to maintain, not a uniqueness constraint enforced by Playwright.
CSS / XPathThe markup relationship changed.
Look for generated classes, renamed attributes, inserted wrappers, and reordered siblings. A short maintained attribute selector and a long positional path make different assumptions; inspect the one your test actually uses.
Use the locator comparison guide when you are ready to choose a replacement.
Right selector, wrong frame or DOM boundary?
For an iframe, enter its context with frameLocator() before locating the control. Ordinary page locators do not search inside another frame’s document.
Playwright locators generally work through open shadow roots. XPath does not pierce them, and closed shadow roots are unsupported. Check that boundary before making the selector longer.
Frame locators · Shadow DOM behaviorLocators resolve an up-to-date element when used. A React re-render alone does not mean you are holding a stale element; inspect which matching conditions or page state changed. How locators resolve elements.
Look at the page the test actually saw.
Your browser after a fresh reload may not show the failing state. Start with an existing failed trace, or capture a focused run with the same setup.
npx playwright test tests/checkout.spec.ts --trace onReplace tests/checkout.spec.ts with your test file. Keep the relevant project, environment, and test data. --trace on records this diagnostic run; use the trace artifact produced by the runner. Playwright CLI options.
- Open the failed action.Read the error and action log. Identify what matched and which condition prevented progress.
- Compare the snapshots.Check the surrounding DOM, visible state, and any action snapshot showing the click target.
- Follow the application response.Inspect relevant requests and console output. Keep the evidence that supports or contradicts your explanation.
Explore Playwright’s Trace Viewer.
During hydration, the page can look ready before it responds correctly. If the click lands but nothing happens, inspect application readiness. Keeping controls disabled until they can respond addresses the behavior directly.
Playwright’s hydration guidanceMake the change. Check the behavior.
Write down the condition you found and the change intended to address it. Then verify both the element you interact with and the outcome the test is meant to protect.
- Recreate the original setup, data, viewport, and action sequence.
- Check that the locator identifies the intended element, including repeated controls.
- Keep an assertion for the application result after the action.
- Rerun the failing case and relevant neighboring cases under the original conditions.
- Record what changed and why. A passing retry alone does not establish a fix.
If results vary without a locator change, investigate the broader causes of flaky Playwright tests. Timing, shared state, and services can require a different fix.
WHEN YOU CAN REPRODUCE IT IN THE BROWSER
Keep the element with the steps that led to it.
Inspect an element and review its selectors with our extension. When the issue depends on earlier actions, record that browser path and add context for the teammate investigating it.
Free extension for desktop Chrome. Try it before signing up.A Samelogic recording captures the browser path you reproduce. Use a Playwright trace for evidence from the automated test run. The extension’s current Playwright snippet uses CSS with page.locator().
A few common questions.
Does a timeout mean my locator is wrong?
No. The target may be absent, delayed, disabled, covered, or in a different frame or page state. Read the failing action’s log and inspect the matching element before deciding what to change.
Why does getByRole stop matching after a UI update?
Check the actual role and accessible name, which can differ from visible text. A renamed control, localization change, or added matching control can change the result. Confirm that the UI change is intentional before updating the test.
Why does a test ID still produce multiple matches?
A test ID does not enforce uniqueness. Repeated components can reuse the same value. Locate the intended row, panel, or dialog first, then find its control. Fix unintended duplicate IDs when the test contract requires uniqueness.
Should I add a sleep, increase the timeout, or force the click?
First establish what is preventing the action. A fixed sleep can miss slower runs, and force skips some click checks without proving the app is ready. Adjust a timeout only when the expected operation needs that time; preserve an assertion for the result.
Does a React re-render make a locator stale?
A Playwright locator resolves an up-to-date element when it is used. A re-render can still change the attributes, accessible name, number of matches, or application state. Investigate that change rather than assuming the locator holds an old DOM node.
Is a Samelogic recording the same as a Playwright trace?
No. A Playwright trace comes from an automated test run. Samelogic records a browser path you deliberately reproduce so you can replay it, inspect an affected element, and add context for a teammate. Use each as evidence of the run or path it actually captured.
Sources & further reading
The API guidance follows Playwright’s documentation. Code and markup are authored examples to adapt to your test.