healx-playwright
v0.2.2
Published
Self-healing locator SDK for Playwright
Maintainers
Readme
healx-playwright
AI-powered self-healing locator SDK for Playwright.
When a Playwright locator times out (e.g., an element ID changed after a UI refactor), the SDK automatically:
- Captures a DOM snapshot of the current page
- Sends it to the HealX backend which uses ML (sentence-transformers, tree edit distance, softmax ranking) to find the best matching element
- Rebuilds a Playwright locator from the healed selector
- Retries the failed action transparently
Installation
npm install healx-playwrightPeer dependency: @playwright/test >= 1.40.0
Prerequisites
The HealX backend must be running:
# From the HealX project root
docker-compose up -d # PostgreSQL + Redis
alembic upgrade head # Run migrations
uvicorn src.main:app --reload # Start the API at http://localhost:8000Quick Start
Option 1: Zero-rewrite proxy (recommended)
Change one import and your existing tests get self-healing with no other code changes:
import { healProxyTest as test, expect } from 'healx-playwright';
test('login flow', async ({ page }) => {
await page.goto('https://myapp.com/login');
await page.locator('#email').fill('[email protected]'); // self-heals on timeout
await page.locator('#submit-btn').click(); // self-heals on timeout
});Works with page object models too -- any framework that receives page from fixtures automatically gets healing because the proxy wraps the built-in page fixture.
Option 2: Explicit fixture-based
Import healTest to get a healPage fixture with explicit locator string passing:
import { healTest as test, expect } from 'healx-playwright';
test('login flow', async ({ healPage, page }) => {
await page.goto('https://myapp.com/login');
await healPage.fill(
page.locator('#email'),
"page.locator('#email')",
'[email protected]',
{ context: 'Email input on login form' },
);
await healPage.click(
page.locator('#submit-btn'),
"page.locator('#submit-btn')",
{ context: 'Login submit button' },
);
});Option 3: Direct instantiation
import { test, expect } from '@playwright/test';
import { HealPage } from 'healx-playwright';
test('login flow', async ({ page }) => {
const healPage = new HealPage(page, {
backendUrl: 'http://localhost:8000',
timeout: 5000,
});
await page.goto('https://myapp.com/login');
await healPage.click(
page.locator('#submit-btn'),
"page.locator('#submit-btn')",
);
});Environment Variables
| Variable | Default | Description |
|---------------------|------------------------|------------------------------------------------------|
| HEALX_ENABLED | (unset = enabled) | Set to false to disable healing globally |
| HEALX_BACKEND_URL | http://localhost:8000 | HealX backend API URL |
Configuration
All options can be passed via healXConfig in fixtures or directly to constructors:
| Option | Type | Default | Description |
|---------------|-----------|--------------------------------------|------------------------------------------|
| backendUrl | string | HEALX_BACKEND_URL or localhost:8000 | HealX backend API URL |
| timeout | number | 3000 | Timeout (ms) before triggering healing |
| executionId | number | 1 | Execution ID for tracking healing events |
| enabled | boolean | HEALX_ENABLED !== 'false' | Global toggle for healing |
API Reference
Zero-Rewrite Proxy API
healProxyTest
Extended Playwright test that replaces the built-in page with a self-healing proxy. Tests use standard Playwright syntax unchanged:
import { healProxyTest as test, expect } from 'healx-playwright';
test('example', async ({ page }) => {
await page.locator('#btn').click(); // heals transparently
});wrapPage(page, config?)
Wraps a Playwright Page with a healing proxy. Use this when integrating with custom fixtures:
import { wrapPage } from 'healx-playwright';
const healingPage = wrapPage(page, { timeout: 5000 });Returns the raw page unchanged when config.enabled is false.
getHealingLog(page)
Returns the array of healing events from a proxied page:
import { getHealingLog } from 'healx-playwright';
const log = getHealingLog(page);
for (const event of log) {
console.log(`${event.originalLocator} -> ${event.healedLocator} (${event.confidence})`);
}skipHealing(locator)
Unwraps a single locator from the proxy. The returned locator will not trigger healing:
import { skipHealing } from 'healx-playwright';
await skipHealing(page.locator('#fragile')).click(); // no healing
await page.locator('#other').click(); // heals normallywithoutHealing(page, fn)
Disables healing for an entire block of actions:
import { withoutHealing } from 'healx-playwright';
await withoutHealing(page, async (rawPage) => {
await rawPage.locator('#el1').click(); // no healing
await rawPage.locator('#el2').fill('x'); // no healing
});
// healing resumes for subsequent actions on pageExplicit HealPage API
HealPage
Constructor: new HealPage(page: Page, config?: Partial<HealXConfig>)
Every method takes (locator, locatorStr, ...args, options?) where options.context is an optional semantic hint for the ML engine.
Mouse actions:
click(locator, locatorStr, options?)dblclick(locator, locatorStr, options?)hover(locator, locatorStr, options?)dragTo(locator, locatorStr, target, options?)
Form actions:
fill(locator, locatorStr, value, options?)type(locator, locatorStr, text, options?)-- usespressSequentiallyunder the hoodpress(locator, locatorStr, key, options?)check(locator, locatorStr, options?)uncheck(locator, locatorStr, options?)selectOption(locator, locatorStr, values, options?)
Focus / scroll:
focus(locator, locatorStr, options?)scrollIntoViewIfNeeded(locator, locatorStr, options?)
State queries:
waitFor(locator, locatorStr, options?)isVisible(locator, locatorStr, options?)isEnabled(locator, locatorStr, options?)isChecked(locator, locatorStr, options?)isDisabled(locator, locatorStr, options?)isEditable(locator, locatorStr, options?)isHidden(locator, locatorStr, options?)
Capture:
screenshot(locator, locatorStr, options?)
Healing log: Access healPage.healingLog to inspect all healing events.
healTest
Extended Playwright test fixture. Provides healPage and configurable healXConfig:
import { healTest as test } from 'healx-playwright';
test.use({ healXConfig: { backendUrl: 'http://my-backend:8000', timeout: 5000 } });
test('example', async ({ healPage, page }) => { /* ... */ });How It Works
Test Action (e.g. click)
|
v
[Try original locator] --success--> Done
|
TimeoutError
|
v
[Capture DOM snapshot via page.evaluate()]
|
v
[POST /api/v1/heal with { failing_locator, dom_snapshot, context }]
|
v
[HealX Backend: Redis cache -> PostgreSQL cache -> ML ranking engine]
|
v
[Receive healed_locator string + confidence_score]
|
v
[Rebuild Playwright Locator from string]
|
v
[Retry the original action with healed locator]Development
cd client-sdk
npm install
npx playwright install chromium
npm run build # Compile TypeScript
npm test # Run integration tests (needs backend running)
npm run test:headed # Run tests in headed modeLicense
MIT
