agentic-browser-testing

Goal-driven E2E testing where a browser agent (Playwright MCP / computer-use) reads a natural-language goal and explores the app via the accessibility tree to assert outcomes — no pre-written script. Covers when intent-driven beats scripted, making agent runs deterministic (pinned model, temperature

By petrkindlmann · 653 installs

npx skills add petrkindlmann/qa-skills --skill agentic-browser-testing

Source repository · Upstream listing

<objective A scripted Playwright test breaks the moment a button moves or a class renames; writing one for a dashboard that changes weekly is a maintenance treadmill. This skill stands up a goal driven browser agent instead: it reads a natural language goal, explores the app via the accessibility tree (Playwright MCP browser snapshot ), and asserts the outcome against an explicit oracle. The failure mode it prevents is the one that makes teams distrust agents — an agent that reports "success" while stuck on the login page because nothing forced it to prove where it landed. You leave with a deterministic, CI gated agent run and a graduation path to a durable scripted test once the flow stabilizes. </objective Quick Route Situation Go to Stand up a goal driven run from scratch Discovery + references/setup.md Decide agentic vs scripted for a given flow Fit: Intent Driven vs Scripted Agent passes one run, fails the next Determinism "How does it click without screenshots?" Interaction Model Runs are slow / burning tokens Cost and Latency Agent reports false success Success Assertion (the Oracle) Flow is stable — make it permanent Graduation → references/graduation and ci.md Block a merge on the goal CI Gating → references/graduation and ci.md Canvas / no accessibility tree Canvas Fallback → references/graduation and ci.md Discovery Questions First, check .agents/qa project context.md in the project root and skip anything it already answers (stack, environments, seed/reset tooling, model access). 1. Which flow, and how often does its UI change? Fast changing/experimental UI favors intent driven; a stable critical path (login) favors scripted. This decides the whole approach. 2. Is there a seeded fixture and a way to reset state? Determinism is impossible without seeded data and a per run reset. If neither exists, that is step zero. 3. Can you deep link past auth to a seeded entry point? Re driving login every run is the biggest avoidable cost; a seeded entry URL scopes the goal and cuts steps. 4. What is the unambiguous success oracle? Specific account text, a /dashboard URL, an order number — plus a forbidden state. "No error" is not an oracle. 5. Does the target render to canvas / WebGL? No accessibility tree means snapshot first won't work; plan the vision fallback or instrument the canvas with ARIA. 6. Which model and budget? Pin a model id and a step budget up front; tier cheap steps to Haiku 4.5 / Sonnet 4.6 and reserve Opus 4.8 for genuinely ambiguous flows. Core Principles 1. Intent, not instructions — but only where churn earns it. The agent reads a goal and finds its own path through the accessibility tree, so it survives a moved button or renamed class that would break a selector. That resilience costs 2 5x the time and money of a scripted run, so spend it on fast changing UI and hard to locate flows, not on stable critical paths. 2. An agent run is untrustworthy until it is deterministic. Same goal, same seeded app must produce the same verdict. That requires temperature 0, a pinned model id, seeded data with a reset, a bounded step budget, and an explicit pass/fail assertion. Without these you have a coin flip, not a test. 3. The oracle lives outside the agent. Never let the LLM self grade "looks good." Success is a checkable assertion against the final browser snapshot — specific expected text, a URL, AND a forbidden state negative check — evaluated by your harness, not the model. 4. Accessibility tree first, pixels last. browser snapshot returns roles, refs, and accessible names (~200 400 tokens) and is deterministic and cheap. Screenshots, pixel coordinates, vision, and OCR are a scoped last resort for canvas only, never the default. 5. Graduation is the goal, not perpetual agent runs. Once a flow is stable, promote the run to a durable scripted tests/ .spec.ts with role based locators. An agent that has been green for two weeks should become a fast, free regression test — keep the agent for exploration, not for guarding a settled path. Fit: Intent Driven vs Scripted The decision is per flow, not per project. Run risk based testing first if you need the risk map; this table is the routing rule once you have it. Flow characteristic Use Why Stable, high frequency critical path (login, payment) Scripted + pinned ( playwright automation ) Runs every PR; must be fast, free, and deterministic. No upside to re exploring it. Fast changing / experimental UI (a dashboard that churns weekly, a redesign in flight) Agentic / intent driven Selectors would break constantly; a goal survives layout churn. Hard to locate flow you can't reliably select Agentic The agent finds the control by role/name instead of you reverse engineering a selector. Exploratory smoke / "does the happy path still work at all" Agentic One NL goal covers a lot of ground without a maintained script. Anything in CI that must never falsely pass Scripted, OR agentic with a hard oracle Non determinism is a false pass risk you must actively cap. The rule, stated plainly: keep stable critical paths scripted and pinned; point intent driven agents at fast changing UI and exploratory smoke. Do not move everything to the agent — it is slower, costlier, and non deterministic, and not every test should be agentic. The Interaction Model (accessibility tree first) Playwright MCP is not computer use with screenshots and pixel coordinates. It is accessibility tree first: 1. browser navigate to the seeded entry URL. 2. browser snapshot returns the accessibility tree — each interactive element as a role , a stable ref , and its accessible name (from ARIA/labels). ~200 400 tokens. 3. The agent picks an element by ref and calls browser click or browser type . 4. browser wait for waits on text appearing/disappearing — never a fixed sleep. 5. Re browser snapshot after the DOM changes; assert against that tree. Why not screenshots: the snapshot is token efficient (thousands of tokens cheaper than an image), deterministic (text refs, not fuzzy pixel matching), and needs no vision model or OCR. Feeding screenshots as the primary input makes the run slower, pricier, and flakier. browser take screenshot is for human evidence only, never as the assertion input. See references/setup.md for the MCP registration, the full tool table, and the goal prompt. Determinism: making a run trustworthy in CI A run that passes once and fails the next with no app change is not yet a test. The fix is never "just retry" or bumping temperature for "smarter" exploration — that adds variance. Pin the variables instead: Lever Setting Model Pinned model id (e.g. claude haiku 4 5 20251001 ), never latest Sampling temperature 0 — no creative wandering in CI Data Seeded fixture + reset/seed the database before every run Scope Bounded step budget ( maxSteps ), e.g. 18 — exceeding it FAILS, never auto retries Oracle Explicit pass/fail verdict asserted against the snapshot Evidence Assert on the accessibility tree , never a screenshot diff Avoid: temperature: 0.7 or 1 for exploration, retry until pass loops, waitForTimeout sleeps, and screenshot based assertions. Each one hides flakiness rather than removing it. Full harness config in references/setup.md . Success Assertion: the Oracle (where agents fail silently) This is the sharpest failure mode: the agent reports success while stuck on the login page, because "page loaded / no error / looks good" was accepted as success and the LLM was allowed to self grade. Force an explicit oracle the harness checks — never the agent. For the goal "sign in as an existing user and confirm the dashboard shows the right account name" : The positive checks (specific account name + /dashboard URL) prove where it landed; the negative check (must NOT be on the login page) is what kills the false pass. "No error," "didn't crash," "screenshot looks correct," and "trust the agent" are not success criteria. Cost and Latency Agent runs are 2 5x slower and pricier than scripted tests — a step is an LLM round trip, the dominant cost. Cut spend without losing coverage by going smaller , not bigger : Step budget — keep maxSteps low and enforced; fewer round trips, less drift. Model tiering — Haiku 4.5 / Sonnet 4.6 for cheap navigation steps; reserve Opus 4.8 for genuinely ambiguous exploration. Don't run the biggest model on every step. Prompt caching — cache the static system prompt, tool schemas, and goal; they repeat every run. Scope via a seeded entry point — one narrow goal per run, deep linked past login instead of re driving it each time. Snapshot over screenshots — the a11y snapshot is ~200 400 tokens; a full page screenshot is thousands. Default to snapshot. Backwards moves to reject: "use a bigger model / Opus 4.8 for every step," "raise the step limit," "screenshot every step," and running with no budget at all. See references/setup.md . Graduation and CI Gating Promote a stabilized goal into a durable scripted test, and gate merges on the verdict. Both are detailed in references/graduation and ci.md ; the essentials: Graduate with Playwright Test Agents (planner / generator / healer, shipped in Playwright v1.56.0 ). npx playwright init agents loop=claude . The planner writes a Markdown test plan to specs/<flow .md ; the generator turns it into tests/<flow .spec.ts with role based locators ( getByRole , getByLabel , getByText ) verified against the live DOM; the healer repairs broken locators. This is the promotion path — not "keep running it as an agent," not recorded clicks, not page.locator('xpath=...') , not data testid only. Gate CI so a failed goal exits non zero and emits a machine readable verdict ( {"passed": true false} in result.json ); the GitHub Actions job parses the boolean and exit 1 s on false. State is seeded/ephemeral and reset per run, with a step budget and a timeout cap. Never continue on error: true , never "always exit 0," never a prose verdict a human reads. Canvas with no accessibility tree: prefer instrumenting the canvas with ARIA; as a scoped last resort enable caps=vision to unlock browser mouse click xy for that flow only. browser snapshot will not work on a raw canvas, but don't make coordinates the default and don't abandon agentic testing. Migrating a brittle script to a goal (honest tradeoffs) Converting an 80 line script that re types login and walks 6 hardcoded steps into a single NL goal with an explicit success assertion is a real win for a churning flow — but state the downsides honestly: Non determinism / false pass risk — the run could pass falsely; that's why the hard oracle and the negative check are non negotiable. Cost/latency — 2 5x slower; bound it with a step budget and a seeded entry point. Not every test should be agentic — keep stable paths scripted, and plan to graduate this one back to a scripted test once it stabilizes. Reject the over promise: it is not "strictly better with no downsides," do not "migrate everything," and never drop the assertions to make it pass. Anti Patterns 1. Reflexively writing a scripted Playwright test "Browser test" pattern matches to codegen, so the default is page.goto / page.locator / await expect(page...) / hunting data testid . That misses the entire point. A goal driven agent reads NL intent and explores via browser snapshot — no pre written selectors. 2. "Use the agent for everything" Over selling the new toy. The agent is 2 5x slower and non deterministic. Stable critical paths