Free tools / Locator healer
Paste the HTML your locator used to work on, the locator, and the HTML as it is now. The healer finds the element that moved, then ranks the locators that will survive the next release, in Playwright and Selenium syntax. Built for Salesforce Lightning, works on any web app. Free, and nothing you paste leaves your browser.
01 // the html it used to work on
02 // the locator that broke
03 // the html now
04 // options
How it works. The healer builds a fingerprint of the element from the old HTML: tag, role, accessible name, label, test ids, attributes, neighbours and position. Then it scores every element in the new HTML against that fingerprint and picks the best match. Finally it ranks the ways to locate it, most durable first, in Playwright and Selenium syntax.
Everything runs in your browser. Nothing you paste is sent anywhere.
// how self-healing works //
A self-healing framework remembers more about an element than the selector you wrote. When the selector stops matching, it scores every element on the page against that memory, picks the best match, carries on, and tells you what to change. This page does the same job from a paste, so you can fix the test before the next run instead of during it.
// which locators last //
Every locator the healer proposes gets a score out of 100 for how long it is likely to live. Test ids and role-plus-name sit at the top because they describe what the element is for. Generated ids and positional XPath sit at the bottom because they describe where the renderer happened to put it.
Rule of thumb: when a locator breaks, replace it with one from higher up the ladder, not a fresh copy of the same kind.
// release weekend //
1
<input id="input-131" name="Amount">
2
class=”slds-input slds-input_counter”
3
<button data-aura-rendered-by="1205:0">Save</button>
4
//section/div[3]/button[2]
5
<label>Amount</label> → getByLabel(‘Amount’)
1
Generated ids
input-131 becomes input-247 after a re-render. Lightning, Aura and React number ids at run time, so anything keyed on them dies on the next page load.
2
SLDS classes
slds-input describes styling. A release can add slds-input_counter or rename a modifier with no functional change, and the CSS locator stops matching.
3
Aura render markers
data-aura-rendered-by changes on every page load. It looks like an id and is tempting to copy from DevTools. Never use it.
4
Position
Spring ’27 adds a release banner above the form. Every div[3] becomes div[4], and the XPath now clicks Save & New instead of Save.
5
What survives
The label text, the name attribute, and role plus visible name. The healer ranks these first, with exact matching so Save never matches Save & New.
// write it once //
The healer applies these when it ranks its suggestions. Apply them when you write the test, and there is less to heal.
01 //
Prefer test ids
Ask developers for data-testid on anything a test touches. It is the one attribute nobody changes by accident.
02 //
Then role and name
getByRole(‘button’, { name: ‘Save’ }) reads like the test case and survives CSS and DOM changes.
03 //
Fields by label
Find inputs by the label a user reads, never by the id behind it. Lightning renumbers the id, not the label.
04 //
Distrust generated ids
input-131, :r3:, ember42 and Aura render markers change every render. If it has a number in it, suspect it.
05 //
Classes describe style
slds-, css-hash and utility classes are shared by many elements and renamed without notice.
06 //
No positional XPath
div[3]/button[2] breaks the moment a banner, wrapper or extra button appears.
07 //
Match exactly
Use exact: true, or normalize-space()= in XPath, so Save does not match Save & New.
08 //
Scope, then search
Start from the card or section heading, then find inside it. Two Amount fields on a page stop being ambiguous.
09 //
Fix up the ladder
When a locator breaks, replace it with one from a higher rung, not a new copy of the one that just failed.
// the same four, three times a year //
01
The id changed
Lightning renumbers input-131 to input-247 after the upgrade. Every test keyed on the id fails at once, and the fix is the same hundred times over.
02
A banner appeared
Salesforce adds a notice above the form. Nothing you test changed, but every positional XPath below it shifts by one.
03
Buttons reordered
Save and Save & New swap places in the button group. div[3]/button[2] still finds a button. It just saves a duplicate record now.
04
Classes renamed
A new slds- modifier lands on the input. The CSS locator either matches nothing, or matches every input on the page.
// keep going //
Make the next release cheaper
// release calendar //
When does Spring ’27 reach your org?
Look up your instance’s preview and upgrade dates, then run your suite on the preview sandbox before production moves.
Check my dates →
// calculator //
What does locator maintenance cost per release?
Hours spent healing selectors, three times a year, in hours and money. Put a number on it.
Open the calculator →
// free tools //
More free tools for Salesforce QA teams
Calculators, playbooks and open-source tools in one place. No sign-up needed for the calculators.
Browse free tools →
// questions //
Questions about self-healing locators
What is a self-healing locator?
A locator backed by a fingerprint of the element: its tag, role, accessible name, label, attributes and neighbours. When the selector stops matching, the framework scores the page against that fingerprint, picks the best match, continues the run and logs a suggested fix. This page does the scoring and the suggestion from a paste of the old and new HTML.
Does Playwright have self-healing built in?
Is Healenium available for Playwright?
Why do Salesforce Lightning locators break so often?
How do I locate elements inside Lightning shadow DOM?
Does TestZeus need locators at all?
// next step //
Stop healing. Start describing.
TestZeus runs your tests from plain-English steps and finds each element the way a person does, on every run. When Salesforce re-renders, there is nothing to heal. Bring your Playwright or Selenium suite and import it.
// Start testing //







