Skip to content
| Marketplace
Sign in
Visual Studio Code>Linters>Playwright Locator LensNew to Visual Studio Code? Get it now.
Playwright Locator Lens

Playwright Locator Lens

Syed Muhammad Usman Ghani

|
1 install
| (0) | Free
Detects brittle Playwright locators and suggests resilient semantic alternatives. Like ESLint for Playwright best practices.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Playwright Locator Lens

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

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft