Your selector broke.
Your test didn't.
When a Playwright locator stops matching, healwright asks an AI model which element you meant, checks the answer against the live page, and caches it. The next run doesn't call the model at all.
- 1Run
- 2Break
- 3Scan
- 4Verify
- 5Heal
- 6Cache
Animated demo. A test clicks a Place order button using the selector #buy-now. A redesign ships and the selector matches nothing. Healwright scores the page's interactive elements, the model picks the button, the answer is checked for a single visible match with enough confidence, the click runs, and the fix is cached so the next run skips the model.
Most runs stop at step one
Healing only starts when your own locator fails. After the first heal the fix is cached, so the model is asked once per element, not once per run.
-
Your locator runs first
healwright tries your selector exactly as written. If the element shows up within a second, the action runs and nothing else happens.
quickTimeout: 1000 -
The cache is checked
A fix found earlier for this element on this page is tried next. Commit
.self-heal/healed_locators.jsonand your teammates and CI reuse it too.cacheFile -
The page is narrowed down
Up to 1,000 interactive elements are collected and scored against your description. Only the best 40 are sent to the model, so the right one isn't lost to DOM order.
maxCandidates: 40 -
The model suggests locators
It answers with up to three Playwright strategies, such as a test id, a role and name, or a label, each with a confidence score, in JSON that has to match a fixed schema.
AI_PROVIDER -
Every suggestion is checked
A suggestion is accepted only if it matches exactly one element, that element is visible, and its confidence clears your threshold.
minConfidence -
The action runs and the fix is saved
The working locator goes into the cache, and the heal is written to
heal_events.jsonlwith the model's reason and token count.reportFile
Keep healing honest
A heal that lands on the wrong element still passes. Decide how far the model is trusted, and keep a record of every heal.
In this example the model's best match scores 0.86.
Healed. The click runs and the test carries on.
The fix is cached for the next run and written to heal_events.jsonl with the model's reason and token count.
Fits the Playwright you already write
Wrap the page fixture once. After that every action takes a selector and a plain-English description, and when you leave the selector empty the description is enough.
Wrap the page fixture
One fixture file, then import test from it instead of
@playwright/test. Healing turns on with SELF_HEAL=1.
// fixtures.ts
import { test as base } from '@playwright/test';
import { createHealingFixture, HealPage } from 'healwright';
export const test = base.extend<{ page: HealPage }>(
createHealingFixture()
);
// SELF_HEAL=1 AI_API_KEY=… npx playwright test
Describe what you mean
click, dblclick, fill, check,
uncheck, hover, focus and
selectOption all take a selector and a description.
await page.heal.click('#buy-now', 'Place order button');
// No selector: the description is enough
await page.heal.fill('', 'Email field', 'ada@example.com');
// Controls that only appear on hover
await page.heal.hover('', 'Todo item');
await page.heal.click('', 'Delete button', { force: true });
Keep the locator style
heal.locator() reads like page.locator(), with the
description held in reserve for when the selector stops matching.
await page.heal
.locator('.new-todo', 'New todo input')
.fill('Buy milk');
await page.heal
.locator('.toggle-all', 'Mark all as done')
.check();
Get a real Locator back
heal.getLocator() returns a plain Playwright Locator, so
expect() assertions, .nth() and .filter() all
work on it.
const badge = await page.heal.getLocator(
'.cart-count',
'Cart item count'
);
await expect(badge).toHaveText('1');
Cloud models, or one on your own machine
Pick a provider with one environment variable. With Ollama the model runs locally, so page content never leaves your computer.
Behind a gateway? AI_BASE_URL points a built-in provider at
Azure, OpenRouter, vLLM or LiteLLM.
Watch a real run
Broken locators found and fixed in a live Playwright session.