guide· 15 min read· by Pramod Dutta

AI End-to-End Testing: How Agents Run Full User Journeys

AI end-to-end testing explained: how AI agents run full multi-step user journeys versus scripted Playwright flows, returning one verdict per journey.

AI end-to-end testing flips the oldest assumption in browser automation: that a "test" is a fixed sequence of selectors and assertions someone wrote by hand. Instead of a 300-line Playwright flow that clicks #add-to-cart and waits for .cart-count, you give an agent a plain-English objective — "log in, add the blue hoodie to the cart, check out, and confirm the order succeeds" — and it drives a real browser step by step until it can return a single verdict for the whole journey. That sounds like a small change in input format. In practice it changes who writes tests, how they break, and what you do when one fails.

This guide is for SDETs and engineers who already know what an end-to-end test is and want a clear-eyed read on what AI agents actually do differently. I'll walk through how an agent executes a multi-step journey, where it genuinely beats a scripted flow, where a scripted flow still wins, and how BrowserBash composes journeys with @import and {{variables}} while returning one verdict per run. No hype. Agentic testing has real failure modes, and I'll name them.

What "end-to-end" means when an agent runs the journey

A traditional end-to-end test verifies a complete user path through your application — not a function, not a component, but the whole flow a real person would take. Sign up, confirm email, log in, search, add to cart, pay, see the confirmation. The value is that it exercises the integration of everything: frontend, API, database, third-party payment, redirects, session state. If any link in that chain breaks, the journey fails.

The scripted version of this is precise and brittle. You encode every step as an explicit instruction the runner executes literally: navigate to a URL, locate an element by selector, click it, wait for a condition, assert text. The runner has no idea what the journey means — it only knows the steps. Change the DOM and the script breaks even though the user experience is identical.

AI end-to-end testing keeps the goal (verify the full journey) but changes the mechanism. You describe the journey in natural language, and an agent — an LLM wired to a real browser — decides each next action by looking at the rendered page, the way a human tester reads a screen. It sees a "Checkout" button, reasons that clicking it advances the goal, clicks it, observes the result, and continues. The journey is still end-to-end. What's gone is the hand-authored map of how to get there.

The agent loop, concretely

Under the hood, an AI agent running a journey is a loop. It captures the current page state (a screenshot, the accessibility tree, or the DOM, depending on the engine), feeds that plus the objective and the history of what it has done to a model, and the model returns the next action: click here, type this, scroll, wait, or "the goal is met, here is the verdict." The browser executes the action, the page changes, and the loop runs again. This continues until the objective is satisfied, the agent concludes it cannot proceed, or a step/time budget runs out.

That loop is the whole difference. A Playwright script is a straight line you wrote in advance. An agent journey is a decision made fresh at every step against the live page. When the page matches your mental model, both produce the same clicks. When the page has drifted — a renamed class, a moved button, an extra interstitial — the script derails and the agent adapts.

Scripted Playwright flows versus AI agent journeys

Let me be fair to Playwright, because it is excellent and I still reach for it constantly. Playwright is fast, deterministic, debuggable, and free. Its auto-waiting and trace viewer are best-in-class. For a stable, high-frequency flow that you run ten thousand times a day, a well-written Playwright test is hard to beat: it runs in well under a second, never hallucinates, and tells you exactly which line failed.

The cost shows up over time, and it is a maintenance cost, not an authoring cost. The day you write a Playwright suite is cheap. The eighteen months after, where every redesign and every A/B test that swaps a data-testid sends an engineer back into the suite to repair locators, is where the money goes. I've watched a small QA team burn roughly a third of its week on selector triage that had nothing to do with real bugs.

Here is an honest side-by-side.

Dimension Scripted Playwright flow AI agent journey
How a step is defined Explicit selector + action + assertion Plain-English intent, resolved at runtime
Reaction to a moved/renamed element Breaks (selector miss) Usually adapts (reads rendered page)
Reaction to a real regression Fails (good) Fails (good)
Speed per run Sub-second to a few seconds Seconds to minutes (model calls per step)
Determinism High — same steps every time Lower — agent may take different paths
Authoring cost Higher (selectors, page objects) Lower (write the goal)
Maintenance cost High (locator churn) Low (no locators to rot)
Debuggability Excellent (trace, exact line) Improving (verdict + step log + video)
Best fit Stable, high-frequency core flows Changing UIs, broad coverage, exploratory checks

Notice the row that matters most: against a real regression, both fail, and that is correct. The agent isn't more lenient about bugs. The difference is entirely in how each tool reacts to cosmetic change that a user wouldn't even notice. A scripted flow treats a renamed CSS class as a failure; an agent treats it as a non-event and keeps going. That single property is why agentic journeys flatten the maintenance curve.

Where Playwright is still the right call

If your flow is mission-critical, runs on every commit, and changes rarely — a payment authorization path, a login that gates everything else — a deterministic script is the better engineering choice. You want zero variance and sub-second feedback there. The honest framing is not "AI replaces Playwright." It is "use deterministic scripts for your stable spine, and use agents for the long tail of journeys that are too expensive to script and maintain." Many teams run both, and the best of them know which is which.

How an agent executes a multi-step journey end to end

Walk through a concrete journey: a store checkout. The objective you hand the agent is a single sentence — "Log in as a test user, add the first product on the catalog page to the cart, complete checkout with the saved address, and confirm the order succeeds." A scripted runner needs that broken into a dozen located steps. The agent needs only the sentence, because it discovers the steps as it goes.

It starts on the catalog page, recognizes a login requirement or a "Sign in" link, navigates to it, fills the credentials, and submits. It lands back on the catalog, identifies the first product, opens it, clicks "Add to cart," then finds its way to the cart and on to checkout. At checkout it selects the saved address, places the order, and reads the resulting page. When it sees "Thank you for your order!" it concludes the objective is met and returns a pass. If the confirmation never appears — say the payment step throws, or a required field blocks submission — it reports a fail with the step where things went sideways.

The key property is that the agent owns the navigation. You did not tell it the cart lives behind a bag icon in the top-right, or that checkout is two clicks away. It figured that out by reading the page. That is also exactly why very small local models struggle here, which I'll get to.

One verdict per journey, not a wall of step logs

This is the part most write-ups gloss over. A multi-step journey produces a lot of intermediate noise — actions taken, pages visited, elements read. What a CI gate or a human reviewer actually needs is one thing: did the journey pass or fail? BrowserBash collapses the whole journey into a single verdict plus structured results, so the merge gate depends on a machine-readable signal, not on someone reading prose. You get the detail when you want to debug, and a clean pass/fail when you just want the gate to do its job.

Composing journeys with @import and {{variables}}

Real journeys share steps. Almost every authenticated test begins with the same login. If you re-describe that login in twenty files, you've recreated the exact copy-paste problem that page objects were invented to solve. BrowserBash handles this with committable Markdown tests: *_test.md files where each list item is one step. They support @import composition so a shared flow lives in one file, and {{variables}} templating so the same journey runs against staging, production, or a teammate's branch by swapping values.

Here is a login flow you write once, in login_test.md:

And here is a checkout journey that imports it, in checkout_test.md:

When the login form changes, you fix login_test.md once and every journey that imports it is corrected. The {{baseUrl}}, {{user}}, and {{password}} placeholders are filled at runtime, so the same committed journey runs anywhere. Because password is marked as a secret, it is masked as ***** in every log line and in the human-readable Result.md the run writes afterward. That file reads like acceptance criteria, which means a product manager can review the coverage in a pull request — something that essentially never happens with a wall of Playwright assertions.

# Run a composed journey; secrets stay masked in logs and Result.md
browserbash testmd run ./checkout_test.md \
  --var baseUrl=https://staging.shop.example \
  --var user=qa@example.com \
  --secret password=s3cr3t

The composition matters more as your suite grows. A handful of shared building blocks — login, search, add-to-cart, checkout — recombine into dozens of full journeys without duplicating a single step. That is the same modularity page objects gave you, except the modules are readable English instead of selector-bound classes that break on a redesign.

Running it: from a one-off objective to a CI gate

For exploration, you don't need any file at all. You hand BrowserBash an objective on the command line and watch the agent drive your real Chrome:

# One-off journey against a live store, exploratory
browserbash run "Log in, add the first product to the cart, \
complete checkout, and confirm 'Thank you for your order!' appears"

By default this runs locally against your own Chrome with a free local model via Ollama — no API key, nothing leaves your machine, a genuine $0 model bill. When you're ready to make a journey part of your pipeline, you save it as a *_test.md file and run it in CI with agent mode, which emits NDJSON — one JSON event per line on stdout — so the job parses the run without scraping prose:

# CI-friendly: NDJSON events, headless, with a recorded video for failures
browserbash testmd run ./checkout_test.md --agent --headless --record

The exit codes are unambiguous and are the whole point of the gate: 0 passed, 1 failed, 2 error, 3 timeout. Your CI job branches on the exit code; it never reads English. The --record flag captures a screenshot and a full .webm session video via ffmpeg on any engine, so when a journey fails at 3 a.m. you watch exactly what the agent saw. On the builtin engine you also get a Playwright trace you can open in the trace viewer — the same debugging surface scripted Playwright users already know.

If you'd rather run the browser somewhere other than your laptop, one flag switches providers without rewriting the journey:

# Same journey, run on a cloud grid, results uploaded to the dashboard
browserbash testmd run ./checkout_test.md --provider lambdatest --upload

--upload is strictly opt-in and pushes run history, video recordings, and per-run replay to the optional free cloud dashboard (free uploaded runs are kept 15 days). Prefer to keep everything local? browserbash dashboard gives you a fully local dashboard with no account at all. The point is that the journey description never changes; only where it runs and where the results land do.

The honest caveats: where AI journeys get flaky

I'd be selling you something if I pretended agent journeys are magic. They have a specific, predictable failure mode, and you should design around it.

The biggest one is model capability. The agent's reliability on a long multi-step journey is bounded by the reasoning ability of the model driving it. Very small local models — roughly 8B parameters and under — can be flaky on long objectives. They lose the thread halfway through checkout, click the wrong thing, or declare victory early. The sweet spot is a mid-size local model in the Qwen3 or Llama 3.3 70B class, or a capable hosted model for genuinely hard flows. BrowserBash is Ollama-first and defaults to free local models, but it auto-resolves to your ANTHROPIC_API_KEY or OPENROUTER_API_KEY if you have one, and OpenRouter offers some genuinely free hosted models (such as openai/gpt-oss-120b:free) for when a tiny local model isn't cutting it. Match the model to the journey: a smoke test of a simple login is fine on a small local model; a six-step checkout with a flaky third-party payment iframe deserves a stronger one.

The second caveat is non-determinism. An agent may take a slightly different path on two runs — a different but equally valid route to the same goal. For the verdict, that usually doesn't matter; the journey passes either way. For ultra-tight, audit-grade flows where you need byte-identical behavior every run, a deterministic script is the better tool. Don't fight the grain of the technology.

Third, speed. Each step involves a model call, so an agent journey takes seconds to minutes where a Playwright flow takes under a second. That cost is fine for the dozens of journeys you couldn't afford to script before; it's wasteful for the three core flows you run on every single commit. Run those as scripts.

A practical way to combine both

The teams getting the most out of AI end-to-end testing don't pick a side. They keep deterministic scripts for the critical spine — login, payment authorization, the two or three flows that must never silently regress — and they use agent journeys for everything else: the long tail of secondary paths, the journeys that change too often to be worth scripting, and exploratory checks a developer adds inline with a feature. You get speed and determinism where it counts and coverage and low maintenance everywhere else. If you want to go deeper on the mechanics, the BrowserBash learn pages and the feature overview are good next stops.

Who AI end-to-end testing is for

This approach earns its keep in a few specific situations. If your UI changes often — an early-stage product, a team shipping daily, a design system mid-migration — agent journeys save you the selector triage that would otherwise eat your week. If you have broad surface area and a small team, you can cover far more user paths in plain English than you could ever afford to script and maintain. If you want non-engineers to read and sanity-check your coverage, a *_test.md file that reads like acceptance criteria does that in a way page objects never will. And if you're building CI for AI coding agents, the NDJSON output and clean exit codes give those agents a structured signal to act on without parsing prose.

It's a weaker fit if your application is stable and your flows rarely change, if you need sub-second feedback on a huge suite that runs constantly, or if you operate under regulatory constraints that demand fully deterministic, reproducible test execution. In those cases a mature scripted framework is the right call, and you should use one. An honest recommendation sometimes points away from the new thing — that's how you know it's honest. You can compare more deeply through the BrowserBash blog and the pricing page, which lays out exactly what's free versus opt-in.

FAQ

What is AI end-to-end testing?

AI end-to-end testing is verifying a complete user journey through a web application by giving an AI agent a plain-English objective instead of a hand-written script. The agent drives a real browser step by step, deciding each action by reading the rendered page, and returns a single pass/fail verdict for the whole journey. It tests the same integrated path a scripted end-to-end test would, but without selectors or page objects to author and maintain.

How is an AI agent journey different from a Playwright test?

A Playwright test is a fixed sequence of located steps you write in advance, so it runs fast and deterministically but breaks when the DOM changes, even cosmetically. An AI agent decides each step at runtime by looking at the live page, so it adapts to moved or renamed elements and needs no locator maintenance. Both correctly fail on a real regression; the difference is that the agent ignores cosmetic UI changes a user wouldn't notice, which dramatically lowers maintenance.

Can AI end-to-end testing run in CI?

Yes. BrowserBash has an agent mode that emits NDJSON — one JSON event per line on stdout — and returns unambiguous exit codes: 0 for passed, 1 for failed, 2 for error, and 3 for timeout. Your CI job branches on the exit code without reading any English text, and you can add --record to capture a screenshot and full session video for any journey that fails.

Are small local models reliable for multi-step journeys?

Not always. Very small local models around 8B parameters and under can lose the thread on long multi-step objectives, click the wrong element, or declare success early. The reliable sweet spot is a mid-size local model in the Qwen3 or Llama 3.3 70B class, or a capable hosted model for the hardest flows. Match the model to the difficulty of the journey rather than expecting one tiny model to handle everything.

Ready to run your first full journey in plain English? Install the CLI with npm install -g browserbash-cli, write a one-line objective, and watch an agent drive your real Chrome through login, cart, and checkout. It's free, open source, and runs locally with no account needed — and when you want run history and video replay, the optional dashboard sign-up is there when you need it.

Try it on your own appnpm install -g browserbash-cli
Start learning