VS Code Extensions
Playwright Locator Lens

Playwright Locator Lens

by usman-ghani123

Detects brittle Playwright locators and suggests resilient semantic alternatives. Like ESLint for Playwright best practices.

Downloads

109

Rating

— (0)

Version

0.0.3

Last updated

Apr 20, 2026

Like ESLint, but specifically for Playwright locator best practices.

Detects brittle Playwright locators as you write them, suggests resilient semantic alternatives with one-click auto-fixes, and scores your entire test suite on demand. Works with TypeScript, JavaScript, Java, C#, and Python projects.


Features

Mode 1 — Real-Time Analysis (automatic)

  • Activates automatically when you open any .ts, .js, .mjs, .java, .cs, or .py file that imports Playwright
  • Inline squiggly underlines appear instantly on bad/brittle locators
  • 💡 Lightbulb quick-fixes for improvable patterns (Rules 1–4) — one click to fix
  • Live resilience score updates in the status bar as you type: 🎭 Locator Score: 85/100 — Good

Mode 2 — Analyze Current File (on demand)

Run via Command Palette to get a detailed line-by-line report for the file you are currently editing.

Mode 3 — Full Workspace Audit (on demand)

Scan every Playwright file in the project and get an overall project health report.

Mode 4 — CLI (standalone, no VS Code required)

Run directly from the terminal against any file, glob pattern, or directory. Perfect for CI/CD pipelines. Supports --fix to auto-correct improvable locators in-place.


CLI Usage

The CLI works independently of VS Code — no editor needed.

# Analyze all Playwright files in the current directory
npx playwright-locator-lens check .

# Specific spec files only
npx playwright-locator-lens check tests/**/*.spec.ts

# All TypeScript files under tests/
npx playwright-locator-lens check tests/**/*.ts

# All JS files
npx playwright-locator-lens check tests/**/*.js

# Multiple patterns at once
npx playwright-locator-lens check tests/**/*.spec.ts src/**/*.ts

# CI mode — exit code 1 if overall score drops below 80
npx playwright-locator-lens check . --threshold 80

# Auto-fix all improvable locators in-place (Rules 1–4 only)
npx playwright-locator-lens check . --fix

# Preview what --fix would change without writing any files
npx playwright-locator-lens check . --fix --dry-run

# Fix and enforce a minimum score in CI
npx playwright-locator-lens check . --fix --threshold 80

--fix behaviour

--fix rewrites improvable locators (Rules 1–4) directly in your source files. XPath and brittle CSS chain issues (Rules 5–6) are intentionally excluded — those require human judgment.

Flag Effect
--fix Rewrites files in-place, then shows the post-fix score
--fix --dry-run Prints what would change, writes nothing

Single file output

📄 Playwright Locator Lens — File Report
==========================================
File:  tests/login.spec.ts
==========================================
Total locators:       8
✅ Resilient:         3
🟡 Improvable:        4   (auto-fix available in VS Code via 💡)
🔴 Brittle:           1   (manual refactor required)

🟡 Improvable locators:
  Line    6: Prefer getByTestId('submit-btn') over locator('[data-testid="submit-btn"]')  →  .getByTestId('submit-btn')
  Line    9: Prefer getByText('Sign in') over locator('text=Sign in')  →  .getByText('Sign in')
  Line   12: Prefer getByPlaceholder('Enter email') over locator('[placeholder="Enter email"]')  →  .getByPlaceholder('Enter email')
  Line   15: Prefer getByLabel('Close') over locator('[aria-label="Close"]')  →  .getByLabel('Close')

🔴 Brittle locators:
  Line   18: Brittle XPath detected: "//div[@class='menu']". Use getByRole() ...

==========================================
Score: 41/100 — Poor
==========================================

Multiple files output

📊 Playwright Locator Lens — Workspace Report
==========================================
Pattern: tests/**/*.spec.ts
Scanned: *.ts, *.spec.ts, *.js, *.spec.js, *.mjs, *.spec.mjs, *.java, *.cs, *.py
(excluding node_modules, dist, out, *.d.ts)
==========================================
Total files:           24
Total locators:       312
✅ Resilient:         187   (59%)
🟡 Improvable:         98   (31%)
🔴 Brittle:            27   (8%)

Worst files (lowest score first):
  tests/checkout.spec.ts    Score: 23/100 — Critical         🔴 3 brittle  🟡 8 improvable
  tests/login.spec.ts       Score: 41/100 — Poor             🔴 1 brittle  🟡 4 improvable
  tests/dashboard.spec.ts   Score: 55/100 — Needs Improvement               🟡 5 improvable

==========================================
Overall Score: 67/100 — Good
==========================================

CLI exit codes

Code Meaning
0 Clean — no brittle locators found, threshold met
1 Brittle locators found OR score is below --threshold
2 Usage error (unknown command, missing pattern, --dry-run without --fix)

Use in CI/CD (GitHub Actions example)

- name: Check Playwright locator quality
  run: npx playwright-locator-lens check . --threshold 70

# Or auto-fix before the quality gate
- name: Fix and enforce locator quality
  run: npx playwright-locator-lens check . --fix --threshold 80

VS Code Commands

You can also run commands from the Command Palette (Ctrl+Shift+P).

Analyze Current File

Analyses only the file currently open in the editor and prints a line-by-line breakdown in the Output Channel.

Playwright Locator Lens: Analyze Current File

Audit Workspace

Scans all Playwright files in the workspace and prints a project-wide summary in the Output Channel.

Playwright Locator Lens: Audit Workspace

Detection Rules

# Severity Pattern detected Suggested fix Auto-fix
1 🟡 Warning locator('[data-testid="x"]') getByTestId('x') ✅
2 🟡 Warning locator('text=Sign in') getByText('Sign in') ✅
3 🟡 Warning locator('[placeholder="x"]') getByPlaceholder('x') ✅
4 🟡 Warning locator('[aria-label="x"]') getByLabel('x') ✅
5 🔴 Error XPath selectors (// or xpath=) Manual refactor ❌
6 🔴 Error Brittle CSS chains (nth-child, deep nesting, class chains) Manual refactor ❌
7 🟡 Warning Partial-match selectors (*=, ^=, $=) Manual refactor ❌

Rules 1–4 work with all supported languages and quote styles (single, double, and backtick template literals).

Partial-match selectors (Rule 7)

Partial-match CSS attribute operators produce a warning but no auto-fix because getByTestId(), getByLabel(), and getByPlaceholder() only do exact matching — swapping them would silently change your test's behaviour.

Locator Problem
locator('[data-testid*="btn"]') contains-match — cannot auto-fix
locator('[data-testid^="submit"]') starts-with match — cannot auto-fix
locator('[aria-label$="dialog"]') ends-with match — cannot auto-fix

Recommended fix: use an exact data-testid value, or switch to getByRole() / getByText().

Backtick / template literal support

All rules detect locators written with any quote style:

// All three are detected and fixed identically
page.locator('[data-testid="submit"]')       // single quote
page.locator("[data-testid='submit']")       // double quote
page.locator(`[data-testid="submit"]`)       // backtick

// Dynamic values in template literals are also handled
const id = 'submit-btn';
page.locator(`[data-testid="${id}"]`)
// 💡 Fix: .getByTestId(`${id}`)

Resilience Score

Locator Points
getByRole(…) / GetByRole(…) / get_by_role(…) 100
getByLabel(…) / GetByLabel(…) / get_by_label(…) 90
getByPlaceholder(…) / GetByPlaceholder(…) / get_by_placeholder(…) 85
getByText(…) / GetByText(…) / get_by_text(…) 80
getByTestId(…) / GetByTestId(…) / get_by_test_id(…) 75
locator('[data-testid]') / locator('[aria-label]') / locator('[placeholder]') / locator('text=') 40
XPath / complex CSS 0

Score = average points across all locators in the file.

Score Rating
90–100 Excellent
70–89 Good
50–69 Needs Improvement
30–49 Poor
0–29 Critical

Language & File Support

Language File types Import detected Method style
TypeScript *.ts, *.spec.ts import { test } from '@playwright/test' getByTestId()
JavaScript *.js, *.spec.js, *.mjs, *.spec.mjs import or require('@playwright/test') getByTestId()
Java *.java import com.microsoft.playwright.* getByTestId()
C# *.cs using Microsoft.Playwright GetByTestId()
Python *.py from playwright.sync_api import / from playwright.async_api import get_by_test_id()
Path Status
*.d.ts ❌ skipped
node_modules/** ❌ skipped
dist/**, out/** ❌ skipped

Analysis only runs when a Playwright import is detected in the file — this prevents false positives on unrelated source files in the same project.

TypeScript / JavaScript example

// 🟡 Detected — auto-fix available (all quote styles supported)
page.locator('[data-testid="submit"]')
page.locator("[data-testid='submit']")
page.locator(`[data-testid="submit"]`)
// 💡 Fix: .getByTestId('submit')

page.locator('text=Sign in')
// 💡 Fix: .getByText('Sign in')

// 🟡 Detected — partial match, no auto-fix
page.locator('[data-testid*="btn"]')
// ⚠️  contains-match — cannot safely replace with getByTestId()

// 🔴 Detected — manual refactor required
page.locator("//div[@class='menu']")
page.locator('div > form > input')

// ✅ Good — no warning
page.getByRole('button', { name: 'Submit' })
page.getByTestId('submit')

Java example

// 🟡 Detected — auto-fix available
page.locator("[data-testid='submit']");
// 💡 Fix: .getByTestId("submit")

// 🔴 Detected — manual refactor required
page.locator("//div[@class='menu']");
page.locator("div > form > input");

// ✅ Good — no warning
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit"));
page.getByTestId("submit");

C# example

C# uses PascalCase method names. The auto-fix applies GetByTestId, GetByText, etc.:

// 🟡 Detected — auto-fix available
await page.Locator("[data-testid='submit']").ClickAsync();
// 💡 Fix: .GetByTestId("submit")

await page.Locator("[aria-label='Close']").ClickAsync();
// 💡 Fix: .GetByLabel("Close")

// 🔴 Detected — manual refactor required
await page.Locator("//div[@class='menu']").ClickAsync();
await page.Locator("div > form > input").ClickAsync();

// ✅ Good — no warning
await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();
await page.GetByTestId("submit").ClickAsync();

Python example

Python uses snake_case method names. The auto-fix applies get_by_test_id, get_by_text, etc.:

# 🟡 Detected — auto-fix available
page.locator("[data-testid='submit']").click()
# 💡 Fix: .get_by_test_id("submit")

page.locator("[aria-label='Close']").click()
# 💡 Fix: .get_by_label("Close")

page.locator("[placeholder='Search']").fill("query")
# 💡 Fix: .get_by_placeholder("Search")

# 🔴 Detected — manual refactor required
page.locator("//div[@class='menu']").click()
page.locator("div > form > input").fill("value")

# ✅ Good — no warning
page.get_by_role("button", name="Submit").click()
page.get_by_test_id("submit").click()

Installation

From VS Code Marketplace:

  1. Open Extensions (Ctrl+Shift+X)
  2. Search for Playwright Locator Lens
  3. Click Install

From terminal:

code --install-extension usman-ghani123.playwright-locator-lens

Requirements

  • VS Code ^1.85.0 (for the extension)
  • Node.js ^18 (for the CLI)
  • A project that uses @playwright/test or playwright
  • No additional runtime dependencies — fully self-contained

License

MIT

Related extensions