E2E Locator Generator
Paste an HTML snippet or a whole page, pick an element and get resilient locators for Playwright, Cypress and Selenium, ranked from role and test id down to fragile positional selectors, with a uniqueness check for every candidate.
Private by design: anything you enter is processed in your browser only. It is never uploaded to or logged on our servers. Your HTML is parsed into an inert document: scripts and event handlers in it never run. Share links carry the HTML inside the link itself.
Interactive elements are shown in bold. The tree is built from a parsed copy of your HTML, not rendered.
Nothing selected.
About resilient end-to-end locators
Most flaky UI tests are not caused by the application but by locators that break when the page changes: a CSS class renamed during a redesign, an auto-generated id that differs between builds, or an nth-child path that shifts when a banner is added. A resilient locator describes the element the way a user or the test contract does (its role and accessible name, its label, or a dedicated test id), so it keeps working through refactors and fails only when the behaviour really changed.
This generator parses your HTML with the browser's DOMParser into an inert document, lists every element in a tree, and for the element you pick computes its implicit ARIA role (a <button> is a button, an <a href> a link, an <input type="email"> a textbox, a <select> a combobox), a simplified accessible name (from aria-labelledby, aria-label, an associated <label>, alt text or text content) and every other usable hook: test id, placeholder, title, id, name, text, classes and position. Each candidate is converted to idiomatic Playwright, Cypress and Selenium code and checked against the whole snippet. Non-unique candidates are flagged and ranked lower, and when an exact match or a scoping container (such as a product card with its own test id) makes a role locator unique, that variant is offered.
Ids that look generated (long digit runs, React :r1: ids, hash-like strings) and utility or CSS-module classes (px-4, css-1q2w3e, Button_root__x7Yz2) are recognised and demoted with an explanation. Because the document is not rendered, CSS visibility from stylesheets, shadow DOM and iframes are not taken into account; inline style="display:none", hidden and aria-hidden are. Always run the chosen locator against the real page, for example with Playwright's codegen and locator picker or the XPath & CSS selector tester.
Good locators make good steps: pair them with readable scenarios checked by the Gherkin formatter, clean up minified markup with the HTML beautifier before pasting it here, and match dynamic text in assertions with the regex tester.
Reference Guide
Recommended locator order. Playwright's documentation lists its built-in locators as getByRole, getByText, getByLabel, getByPlaceholder, getByAltText, getByTitle and getByTestId, and recommends prioritising role locators and user-facing attributes. Testing Library publishes an explicit priority, which is a good default for any framework:
| Priority | Playwright | Cypress / Selenium equivalent | When to use |
|---|---|---|---|
| 1 | getByRole('button', { name: 'Sign in' }) | cy.contains('button', 'Sign in'); XPath //button[normalize-space()='Sign in'] | Almost anything interactive or structural: buttons, links, inputs, headings, checkboxes |
| 2 | getByLabel('Email address') | cy.get('#email') via the label's for; XPath through //label | Form fields with a visible label |
| 3 | getByPlaceholder('you@example.com') | [placeholder="…"] | Inputs without a label (fix the missing label too) |
| 4 | getByText('Create an account') | cy.contains('p', '…'); XPath normalize-space() | Non-interactive text: paragraphs, messages, badges |
| 5 | getByAltText('Acme Store') | img[alt="…"] | Images and image inputs |
| 6 | getByTitle('Close') | [title="…"] | Elements identified only by a tooltip title |
| 7 | getByTestId('login-submit') | cy.get('[data-cy="…"]'); By.cssSelector("[data-testid='…']") | When text and role are ambiguous or change often (translations, dynamic copy). An explicit contract that never breaks on copy edits |
| Last resort | locator('css=…'), locator('xpath=…') | By.id, By.name, CSS, XPath | Stable hand-written ids and names are fine; avoid styling classes and positions |
Playwright's getByTestId reads data-testid by default; change it with use: { testIdAttribute: 'data-cy' } in playwright.config or selectors.setTestIdAttribute(). Cypress's best-practice guide uses data-cy, data-test or data-testid. Name, text, label, placeholder, alt and title matching in Playwright is case-insensitive and substring-based unless you pass { exact: true }.
Implicit ARIA roles of common HTML elements (used by getByRole):
| HTML | Implicit role | Accessible name from |
|---|---|---|
<button>, <input type="submit|button|reset|image"> | button | Text content / value / alt |
<a href>, <area href> | link | Text content (including image alt) / alt |
<a> without href | none (generic) | Not reachable with getByRole('link') |
<input> text, email, tel, url (or no type), <textarea> | textbox | Label, then title, then placeholder |
<input type="search"> | searchbox | Label |
Text-like input with a list attribute | combobox | Label |
<input type="password"> | none | Use getByLabel |
<input type="checkbox"> / radio | checkbox / radio | Label |
<input type="number"> / range | spinbutton / slider | Label |
<select> / with multiple or size > 1 | combobox / listbox | Label |
<option> | option | Text content |
<h1>…<h6> | heading (level: 1–6) | Text content |
<img alt="…"> / <img alt=""> | img / presentation | alt |
<ul>, <ol> / <li> | list / listitem | none |
<table>, <tr>, <td>, <th> | table, row, cell, columnheader | caption / text content |
<nav>, <main>, <aside> | navigation, main, complementary | aria-label |
<header> / <footer> (not inside article, aside, main, nav or section) | banner / contentinfo | aria-label |
<form> / <section> with an accessible name | form / region | aria-label, aria-labelledby |
<dialog>, <fieldset>, <hr>, <progress> | dialog, group, separator, progressbar | varies (<legend> for fieldset) |
An explicit role="…" attribute overrides the implicit role.
Locator anti-patterns:
| Avoid | Why it breaks | Instead |
|---|---|---|
/html/body/div[2]/div/form/div[3]/button | Any wrapper or sibling change shifts the path | Role + name, or a test id |
#input-38271, #\:r1\:, #ember412 | Generated ids change between builds, renders or sessions | Label or a stable test id |
.btn.btn-primary.px-4, .css-1q2w3e4 | Styling and CSS-in-JS classes change with design work and builds | Role, label or test id |
li:nth-child(3) button, cy.get('button').eq(2) | Order depends on data, sorting and feature flags | Scope to a container: getByTestId('product-EB-200').getByRole('button') |
.first() to silence a non-unique match | Hides ambiguity; clicks whatever happens to be first | Make the locator unique, or filter with hasText / has |
| Long exact text with punctuation or counts, e.g. "Cart (2)" | Copy edits, translations and dynamic numbers | Substring or regex, a test id, or a role name without the volatile part |
XPath with contains(@class, 'btn') | Also matches btn-secondary, nobtn and so on | Exact class token, or better a semantic locator |
Example input
<form id="login-form">
<label for="email">Email address</label>
<input type="email" id="email" name="email" placeholder="you@example.com">
<button type="submit" class="btn btn-primary" data-testid="login-submit">Sign in</button>
</form>
Selected: input#email
→ page.getByRole('textbox', { name: 'Email address' })
→ page.getByLabel('Email address')
→ driver.findElement(By.id("email"))
This sample loads automatically when you open the tool. Use Load Sample to bring it back.
Frequently Asked Questions
Prefer locators that match what users see: role with accessible name, label, placeholder or text, or a dedicated test id. Avoid long CSS chains and positional selectors, which break when the layout changes.
The generator writes locators for Playwright, Cypress and Selenium (Java and Python).
More than one element in the snippet matches it. Add a test id, use a more specific accessible name or scope the locator to a parent element.
Related Tools
All QA & Testing Tools →Pairwise Test Case Generator
Generate a minimal set of test cases that covers every pair...
OpenAPI Validator & Viewer
Validate OpenAPI 3.x and Swagger 2.0 specs in YAML or JSON,...
HAR File Viewer & Analyzer
Open HAR files from browser DevTools to inspect every reques...
Image Resizer
Resize images online using Lanczos, Bicubic, and Bilinear al...
Image Compressor
Compress and optimize JPG, PNG, and WebP images online witho...
Crop Image Tool
Crop image files with exact x/y coordinates and output dimen...