• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
khawjaahmad

khawjaahmad

playwright-selector-gen

MCP: Fetch URL DOM and generate unique Playwright selectors
Public
Like
playwright-selector-gen
Home
Code
2
README.md
H
main.ts
Environment variables
5
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
…
Viewing readonly version of main branch: v6
View latest version
README.md

Playwright Selector Generator

Endpoint: https://playwright.val.run

AI-powered service that fetches any URL's DOM and generates Playwright-native locators following the official Playwright locator priority. Each element gets its top 3 ranked locators (best → fallback).


Locator Priority

Follows the official Playwright recommendation:

RankMethodWhen UsedConfidence
1getByRole()ARIA role + accessible name — always firstHigh
2getByLabel()Form fields with associated <label>High
3getByPlaceholder()Inputs with placeholder textHigh
4getByText()Visible text contentMedium
5getByAltText()Images with alt attributeHigh
6getByTitle()Elements with title attributeMedium
7getByTestId()data-testid / data-cy attributesHigh
8locator() CSSShort CSS only — #id, [attr], max 2 levelsLow
9locator() XPathAbsolute last resortLow

Deep nested selectors like html > body > div > div > div > ... are never generated.


Quick Start

With AI (recommended)

Pass your API key to get model-analyzed, context-aware selectors:

curl -X POST https://playwright.val.run \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "apiKey": "your_zai_api_key", "apiUrl": "https://api.z.ai/api/anthropic/v1/messages", "model": "anthropic/claude-sonnet-4-20250514" }'

The AI analyzes each element's role, context, and surrounding DOM to pick the most resilient locators. It understands when getByRole('button', { name: 'Submit' }) is better than getByText('Submit') because the text might appear elsewhere.

Without AI (fallback)

If no apiKey is provided, the service uses deterministic heuristics — still following Playwright's priority order, but without contextual reasoning:

curl -X POST https://playwright.val.run \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}'

Environment variables

Instead of passing credentials in the body, you can set these env vars on the val:

Env VarPurposeDefault
ANTHROPIC_API_KEYZ.AI / Anthropic API key—
ANTHROPIC_URLAPI endpointhttps://api.z.ai/api/anthropic/v1/messages
ANTHROPIC_MODELModel identifieranthropic/claude-sonnet-4-20250514

Body params override env vars.


API

GET /

Health check.

{ "status": "ok", "service": "playwright-selector-gen" }

POST /

FieldTypeRequiredDescription
urlstringYesPage URL to analyze
filterstring[]NoOnly return elements matching these tags
apiKeystringNoAnthropic / Z.AI API key (enables AI mode)
apiUrlstringNoAPI endpoint URL
modelstringNoModel identifier

Response

{ "url": "https://example.com", "totalElements": 5, "elements": [ { "tag": "a", "text": "Learn more", "role": "link", "attributes": { "href": "https://iana.org/domains/example" }, "locators": [ { "method": "getByRole", "selector": "role=link[name=\"Learn more\"]", "playwrightCode": "page.getByRole('link', { name: 'Learn more' })", "confidence": "high" }, { "method": "getByText", "selector": "text=\"Learn more\"", "playwrightCode": "page.getByText('Learn more', { exact: true })", "confidence": "medium" }, { "method": "locator:css", "selector": "a[href=\"https://iana.org/domains/example\"]", "playwrightCode": "page.locator('a[href=\"https://iana.org/domains/example\"]')", "confidence": "medium" } ] } ] }

Using in Playwright Tests

import { test, expect } from '@playwright/test'; test('example page has working link', async ({ page }) => { await page.goto('https://example.com'); // Best: getByRole (rank 1) await page.getByRole('link', { name: 'Learn more' }).click(); // Fallback: getByText (rank 4) await page.getByText('Learn more', { exact: true }).click(); // Last resort: CSS locator (rank 8) await page.locator('a[href="https://iana.org/domains/example"]').click(); });

How It Works

  1. Fetch — The endpoint fetches the target URL's HTML with a browser-like User-Agent.
  2. Parse — DOM is parsed and walked. Interactive elements (a, button, input, select, etc.), landmarks (nav, header, main), headings, images, and elements with ARIA/test attributes are extracted.
  3. Enrich — Each element gets its implicit ARIA role, accessible name (from aria-label, associated <label>, alt, placeholder, or text content), and relevant attributes.
  4. Generate — If an API key is provided, the element data is sent to the AI model which produces the best 3 Playwright locators per element using full contextual understanding. Without an API key, a deterministic algorithm applies Playwright's priority chain.
  5. Return — Top 3 locators per element, ranked best → fallback, each with a ready-to-paste playwrightCode string.

Elements Scanned

Interactive: a, button, input, select, textarea, form, label, details, summary, dialog

Landmarks: nav, header, footer, main, aside, section, article

Content: img, h1–h6, table

By attribute: Any element with role, data-testid, data-test-id, data-cy, aria-label, tabindex, or contenteditable

Capped at 300 elements per request.


Error Responses

StatusMeaning
400Missing url in request body
405Method not allowed (use POST)
502Target URL could not be fetched
500Internal / AI API error
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.