npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

playwright-self-healing

v1.0.0

Published

Self-healing locator framework for Playwright. Automatically recovers from broken selectors using intelligent DOM comparison — while ensuring real bugs are never masked.

Readme

🩹 playwright-self-healing

CI npm version License: MIT

Self-healing locator framework for Playwright. Automatically recovers from broken selectors using intelligent DOM comparison — while ensuring real bugs are never masked.

When your UI changes and selectors break, this framework finds the correct element by comparing six dimensions of similarity against stored snapshots. It heals what it can, flags what it's unsure about, and fails on what it should.


Why?

UI test suites are fragile. A developer renames a CSS class, changes an ID, or restructures a component — and suddenly 30 tests fail even though the application works perfectly. You spend hours updating selectors instead of finding real bugs.

Self-healing fixes this by making your locators resilient to cosmetic UI changes, while keeping your assertions strict so real regressions are always caught.

Design Philosophy

| Principle | How it works | |---|---| | Heal locators, not assertions | Only element-finding is healed. expect() failures always surface as real bugs. | | Confidence-based decisions | Every heal has a score. High confidence: auto-heal. Medium: flag for review. Low: fail normally. | | Full transparency | Every healing event is logged with a detailed breakdown of what changed and why. | | No vendor lock-in | Your tests are standard Playwright. Remove the wrapper and they still work. |


Quick Start

1. Install

npm install playwright-self-healing

2. Initialize

npx self-healing-init

This creates the snapshot database and adds .healing-data/ to your .gitignore.

3. Add the Reporter

In your playwright.config.ts:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  reporter: [
    ['html'],
    ['playwright-self-healing/reporter'],
  ],
  // ... rest of your config
});

4. Write Tests

Replace page with healablePage for locator operations:

import { test, expect } from 'playwright-self-healing/fixtures';

test('user can log in', async ({ healablePage }) => {
  await healablePage.goto('/login');

  // Self-healing locators — resilient to selector changes
  await healablePage.locator('#email').fill('[email protected]');
  await healablePage.locator('#password').fill('secret');
  await healablePage.locator('#submit-btn').click();

  // Assertions use expect() and are NEVER healed
  const greeting = healablePage.locator('.welcome-msg');
  await expect(await greeting.resolved()).toHaveText('Welcome!');
  await expect(healablePage.page).toHaveURL('/dashboard');
});

5. Run

npx playwright test

On the first run, the framework snapshots every element. On subsequent runs, if a selector breaks, healing kicks in automatically.


How It Works

    locator('#submit-btn')
            |
            v
    +----------------+
    | Try original   |--- Found --->  Snapshot for future healing
    | selector       |                          Done
    +-------+--------+
            |
          Not found
            |
            v
    +----------------+
    | Load stored    |--- No snapshot --->  Fail normally
    | snapshot       |    (must pass once)
    +-------+--------+
            |
            v
    +----------------+
    | Scan current   |
    | page DOM for   |
    | candidates     |
    +-------+--------+
            |
            v
    +----------------+
    | Score each     |    >= 75%  ->  Auto-heal + log
    | candidate by   |---
    | similarity     |    50-74% ->  Flag for review
    |                |---
    +----------------+    < 50%  ->  Fail normally

Similarity Scoring

Each candidate element is scored across six dimensions:

| Dimension | Default Weight | What it compares | |---|---|---| | Text Content | 30% | Visible text (Levenshtein distance) | | Attributes | 25% | id, data-testid, name, type, etc. (weighted Jaccard) | | DOM Structure | 15% | Parent chain, sibling context, child index | | Tag Name | 10% | Element type, with partial credit for similar tags | | Visual Position | 10% | Bounding box proximity and size | | ARIA | 10% | Role and label attributes |

All weights are configurable per your application's characteristics.


Configuration

Pass configuration through playwright.config.ts:

export default defineConfig({
  use: {
    healingConfig: {
      confidenceThreshold: 0.80,
      maxHealsPerTest: 2,
      verbose: true,
    },
  },
});

Options

| Option | Type | Default | Description | |---|---|---|---| | enabled | boolean | true | Enable/disable healing globally | | confidenceThreshold | number | 0.75 | Minimum score to auto-heal | | reviewFloorThreshold | number | 0.50 | Below this, healing is rejected entirely | | maxHealsPerTest | number | 3 | Max heals per individual test | | maxHealsPerRun | number | 15 | Max heals across the entire run | | dbPath | string | .healing-data/snapshots.db | SQLite database location | | verbose | boolean | false | Print detailed healing info to console | | domContextDepth | number | 3 | Levels of parent/sibling context to capture | | captureScreenshots | boolean | false | Store element screenshots in snapshots | | noHealPatterns | string[] | ['assert','expect',...] | Selector patterns that are never healed | | similarityWeights | object | (see above) | Weight for each scoring dimension |


API Reference

Fixtures

import { test, expect } from 'playwright-self-healing/fixtures';

healablePage

A wrapper around Playwright's Page with self-healing capabilities:

// CSS selectors
healablePage.locator('#my-id')
healablePage.locator('.my-class')
healablePage.locator('button[type="submit"]')

// Playwright shorthand methods
healablePage.getByRole('button', { name: 'Submit' })
healablePage.getByTestId('login-btn')
healablePage.getByText('Click me')
healablePage.getByLabel('Email')
healablePage.getByPlaceholder('Enter email')

healableLocator.resolved()

Returns the underlying Playwright Locator after healing resolution. Use for assertions:

const btn = healablePage.locator('#btn');
await expect(await btn.resolved()).toHaveText('Submit');
await expect(await btn.resolved()).toBeVisible();

healablePage.page

Access the raw Playwright Page for non-healing operations:

await expect(healablePage.page).toHaveURL('/dashboard');
await expect(healablePage.page).toHaveTitle('My App');

Standalone Usage (without fixtures)

For custom test setups or integration with other frameworks:

import { createHealablePage } from 'playwright-self-healing/fixtures';

const healable = createHealablePage(page, {
  testFile: __filename,
  testName: 'my test',
  config: { verbose: true, confidenceThreshold: 0.80 },
});

Direct API Access

For advanced use cases:

import {
  HealingEngine,
  SnapshotStore,
  createConfig,
} from 'playwright-self-healing';

const config = createConfig({ verbose: true });
const store = new SnapshotStore(config.dbPath);
const engine = new HealingEngine(config, store);

const result = await engine.attemptHeal(page, '#old-selector', 'test.ts', 'my test');
console.log(result.healed, result.confidence, result.explanation);

Safety Guardrails

Assertions Are Never Healed

The framework draws a hard line: element-finding can be healed, assertions cannot. If expect(element).toHaveText('Submit') fails because the text is now 'Delete', that's a real bug.

Heal Limits

If too many locators break in a single test (maxHealsPerTest: 3) or across a run (maxHealsPerRun: 15), the framework stops healing and fails normally. This prevents cascading fixes that might mask a larger problem.

No-Heal Patterns

Selectors containing words like "assert", "expect", or "verify" are never healed.

The Review Tier

When confidence is between 50% and 75%, the heal is flagged as "needs review" rather than auto-applied. The test continues, but your team is alerted to verify the healing was appropriate.


Reporting

Console Output

✅ [HEALED] Login > should log in
   "#login-btn" → "#submit-button" (87.3% confidence)

⚠️ [NEEDS REVIEW] Navigation > sidebar links
   ".nav-settings" → "a:nth-of-type(3)" (62.1% confidence)

HTML Dashboard

After a run, find the report at .healing-data/reports/healing-report.html with summary cards, event details, and score breakdowns.

JSON Report

Machine-readable report at .healing-data/reports/healing-report.json for CI integration.


CI Integration

GitHub Actions

- name: Run tests
  run: npx playwright test

- name: Upload healing report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: healing-report
    path: .healing-data/reports/

Failing CI on Review-Needed Heals

REVIEWS=$(jq '.summary.reviewNeeded' .healing-data/reports/healing-report.json 2>/dev/null || echo "0")
if [ "$REVIEWS" -gt "0" ]; then
  echo "Healing events need manual review"
  exit 1
fi

Extending

Custom Similarity Weights

healingConfig: {
  similarityWeights: {
    textContent: 0.40,    // Increase for content-heavy apps
    attributes: 0.20,
    structure: 0.20,
    tagName: 0.05,
    visualPosition: 0.10,
    ariaRole: 0.05,
  },
}

LLM Integration

The HealingResult object contains full context (snapshots, candidates, scores, DOM diffs) that can be passed to an LLM for advanced triage decisions.


Comparison with Other Tools

| Feature | playwright-self-healing | Healenium | Testim | mabl | |---|---|---|---|---| | Open source | MIT | Apache 2.0 | Proprietary | Proprietary | | Playwright support | Native | Selenium only | Yes | Yes | | Transparent scoring | Full breakdown | Partial | Black box | Black box | | Configurable thresholds | All tunable | Limited | No | No | | Review tier | 3-tier system | Binary | No | No | | Infrastructure | None (SQLite) | PostgreSQL + backend | Cloud | Cloud | | Vendor lock-in | None | Low | High | High |


FAQ

Q: What happens on the first run? The framework snapshots every element but can't heal anything yet. Tests run normally.

Q: Can this mask real bugs? It's designed not to. Assertions are never healed. Confidence thresholds, the review tier, and heal limits add multiple layers of protection.

Q: Does it slow down my tests? Negligibly. Snapshot capture adds ~5-10ms per element. Healing only triggers on failure.

Q: What about dynamic content? Multi-dimensional scoring handles this. Even if an ID changes, text, structure, and position still identify the element.

Q: Can I migrate incrementally? Yes. Change the import in individual test files. Tests using the regular page fixture continue working unchanged.


Contributing

See CONTRIBUTING.md for development setup and guidelines.

License

MIT