Public
TinyFish → AI → Playwright test generator
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.

playwright-gen

Public HTTP val that turns a URL into a Playwright .ts test file using TinyFish (real-browser HTML fetch) + an OpenAI-compatible LLM (selector & assertion generation).

Endpoint

POST https://khawjaahmad--6bc5243a4d5d11f1ab91ee650bb23af1.web.val.run

Headers:

  • X-Auth-Key: <AUTH_KEY> — required
  • Content-Type: application/json

Body:

{ "url": "https://example.com/login" }

Response: text/plain containing a Playwright TypeScript test file. Save as *.spec.ts and run.

GET / returns a small JSON usage blurb.

Environment variables

Set these in the val's settings (already created with placeholders — replace before first use):

KeyPurpose
AUTH_KEYShared secret gating the endpoint. Generate a long random string.
TINYFISH_API_KEYhttps://agent.tinyfish.ai/api-keys
LLM_TOKENBearer token for your OpenAI-compatible provider
LLM_BASE_URLBase URL ending in /v1 (OpenAI, OpenRouter, Groq, Together, Gemini-OpenAI, ...)
LLM_MODELModel name passed to chat/completions

Why an auth key on a public val?

The val is public so its source is visible, but the source doesn't contain secrets — they live in env. The AUTH_KEY check prevents random callers from running up your TinyFish + LLM bills via your public URL. Without it, anyone who finds the endpoint can drain your accounts.

Pipeline

  1. Auth check (X-Auth-Key)
  2. TinyFish Fetch with format: "html" (timeout 20s)
  3. Aggressive cleaning (linkedom):
    • Drop <script>, <style>, <noscript>, <svg>, <iframe>, <canvas>, comments, hidden elements
    • Drop framework attrs (ng-*, v-*, _ngcontent-*, data-react*, data-svelte-h, data-astro*)
    • Drop on* handlers, inline style, srcset, integrity, nonce
    • Drop hashed/CSS-in-JS/BEM class names; keep up to 4 meaningful classes
    • Keep data-testid / data-test / data-qa / data-cy (the Playwright testid hooks)
    • Collapse whitespace; progressively prune <footer>, <aside>, <nav>, then empty wrapper <div>s
    • Hard cap at ~120 KB; truncate with marker if still too big
  4. OpenAI-compatible chat/completions with a strict system prompt enforcing the locator priority order and the anti-pattern list.
  5. Return the model output as plain text (text/plain).

Anti-bot handling

If TinyFish returns a Cloudflare / PerimeterX / hCaptcha challenge page, the cleaner would produce garbage. The val detects challenge markers in title + first 4 KB and returns:

{ "error": "target_blocked_by_anti_bot", "detail": "..." }

Error codes

StatuserrorWhen
400invalid_jsonBody wasn't JSON
400invalid_urlurl missing or not http(s)
401unauthorizedMissing/wrong X-Auth-Key
405method_not_allowedNot GET or POST
500server_misconfiguredAn env var is missing
502target_blocked_by_anti_botPage returned an anti-bot challenge
502tinyfish_failedTinyFish errored or timed out
502empty_after_cleaningCleaned HTML was too small to be useful
502llm_failedLLM call errored or timed out

Curl example

curl -sS -X POST "https://khawjaahmad--6bc5243a4d5d11f1ab91ee650bb23af1.web.val.run" \ -H "X-Auth-Key: $AUTH_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://playwright.dev/"}' \ > playwright-dev.spec.ts