selfcure
selfcure × Playwright

Fragile locators in Playwright?
See your frontend's testability score.

A page.locator() that worked yesterday throws strict mode violation: resolved to 2 elements today. The locator isn't the problem — the frontend never gave it a unique hook. selfcure reads your source, scores every component for testability, flags locators that resolve to multiple nodes, and ships data-testid fixes as a Pull Request — the preventive step before Playwright Test Agents take over.

Root cause

"Resolved to 2 elements" is a frontend bug

Playwright's strict mode is doing you a favor: it refuses to guess which of two matching nodes you meant. But the fix belongs in the frontend, not in a longer CSS chain. Two patterns cause most of it:

Weak locator

No data-testid, id, or role-accessible name, so you fall back to .locator('.btn') — which breaks the moment styling changes.

Ambiguous locator

The best available locator matches a sibling too. Strict mode throws; non-strict silently picks the wrong node. selfcure catches this at the AST level.

selfcure scores and fixes both before your suite — or the Planner / Generator / Healer agents — ever run.

Before / after

From CSS guesswork to getByTestId

✗ Fragile — strict mode violation waiting to happen

// resolves to 2 elements → throws, or picks wrong node
await page.locator('.btn-primary').click()
await page.locator('form input').first().fill('a@b.com')

✓ Stable — unique by contract

// data-testid added by a selfcure PR
await page.getByTestId('submit').click()
await page.getByTestId('email').fill('a@b.com')

selfcure suggests the values, dedupes collisions across siblings, and opens the PR on GitHub or GitLab.

How it works

Preventive, then hand off to the agents

01 · SCORE

Measure testability

Every interactive element gets a 0–100 score on selector stability — a quantifiable metric for your tech lead.

02 · FLAG

Find ambiguity

selfcure detects locators that resolve to multiple nodes and proposes a unique data-testid — the strict-mode failure, prevented.

03 · HAND OFF

Feed the agents

Once the PR lands, @playwright/mcp + Planner / Generator / Healer work against a frontend they can actually target.

Get going

Score your frontend in two commands

# From your app's root — Node 20+
npx @selfcure/cli init
npx @selfcure/cli lint   # score + flag ambiguous locators

Connect it to your AI editor (free)

# MCP server — Cursor, Claude Code, VS Code, Windsurf
npx @selfcure/cli mcp

Your AI assistant can query the testability score and ambiguity findings directly — the same data the Playwright agents benefit from.

Team tier

The Team plan adds a web dashboard with score history, automatic PR comments, and branch comparison — proof that your frontend is getting more testable over time.

Read the getting-started guide →

FAQ

Playwright + selfcure

Does selfcure replace Playwright Test Agents?

No — it feeds them. selfcure is preventive: it makes the frontend testable so the Planner, Generator, and Healer have stable selectors to work with.

Does it work with my React / Vue / Angular app?

Yes. The crawler parses .tsx, .jsx, .vue, Angular .component.ts, and plain .html.

How is this different from a longer CSS locator?

A longer locator is more brittle, not less. selfcure adds a stable data-testid at the source so the locator stays short and unique.

Is it really free?

The CLI, testability score, and MCP server are free and open source. Auto-fix, PR opening, and the history dashboard are paid.