use-case· 17 min read· by Pramod Dutta

Automate SaaS Onboarding Flow Testing End to End

SaaS onboarding flow testing in plain English: drive trial signup through the aha-moment with one BrowserBash markdown test, replay video, and A/B-proof steps.

Your activation rate is the number that decides whether the company lives, and it hangs on a sequence of screens no traditional test suite covers well: trial signup, the welcome wizard, the first project, the empty-state nudge, and the single moment where a new user finally sees the value they came for. That whole path is what this guide treats as a first-class test. Done right, SaaS onboarding flow testing means you can prove, on every deploy, that a stranger can go from a landing page to their aha-moment without hitting a dead end — and you can do it with one plain-English test, a real browser, and a replay video you can scrub when something breaks.

The reason this path is under-tested is not laziness. It is that onboarding is the most-edited surface in any SaaS product. Growth teams ship A/B variants weekly. PMs reorder steps. A "skip for now" link appears, then disappears. Recorded automation that pinned itself to last month's wizard breaks the moment the experiment flips, and the test that was supposed to guard activation becomes the test everyone mutes. This article walks through a different approach using BrowserBash, a free, open-source CLI, where you describe the journey in English and an AI agent re-reads the live page on every run — so an onboarding variant that would shatter a recorded flow is something the agent simply adapts to.

Why onboarding flows are the hardest thing to keep green

A login test crosses one screen. A checkout test crosses three or four. An onboarding flow can cross a dozen, and each one is a moving target that a different team owns.

Think about what a real activation journey touches. The marketing site renders the signup CTA, and growth A/B tests its copy and color. The signup form might be email-and-password today and a magic link tomorrow. After signup there is usually an email verification gate, then a multi-step wizard that asks for your role, your team size, your use case — fields that PMs add and remove constantly. Then a product tour with tooltips, a "create your first thing" prompt, sample data seeding, and finally the screen where the product clicks: the dashboard populates, the first report renders, the integration connects. That last screen is the aha-moment, and it is the only step that actually matters for activation.

Every one of those steps is independently shippable, and most are owned by people who have never heard of your test suite. The growth engineer who flips an experiment does not know that variant B moves the "Continue" button into a slide-over panel. The PM who adds a "What's your company size?" step does not know your recorded flow expected the dashboard two clicks sooner. This is why onboarding automation rots faster than anything else you own: the surface changes weekly, and it changes from outside the QA team's line of sight.

The A/B variant problem, specifically

Self-healing recorders like the ones in Autify, Mabl, or Testim are built to survive small DOM drift — a renamed class, a moved element. They are genuinely good at that. What they struggle with is structural change: an experiment that doesn't just rename a button but inserts a whole new step, swaps a single-page form for a three-step wizard, or routes 50% of traffic down a different path entirely. A recorded flow assumes a fixed sequence of actions. When the sequence itself is the variable, healing a single selector does not save you.

That is the exact gap an intent-driven agent fills. If your test says "complete any onboarding questions, then create your first project," the agent reads whatever wizard is actually on screen — two steps or five, panel or modal — and works through it. The structure is allowed to change because you never hardcoded the structure.

What "end to end" really means for activation

There is a temptation to define the onboarding test as "signup works." That is necessary and nowhere near sufficient. A user who signs up and then bounces off an empty dashboard never activated. The test has to reach the value moment.

So draw the boundary honestly. The journey starts where a real trial starts — the marketing CTA or the /signup page — and it ends at a verifiable signal that the user reached value. That end signal is product-specific and it is the most important assertion in the whole test. For a project-management tool it might be "the board shows the task I just created." For an analytics product it might be "the dashboard renders at least one chart with data." For a CRM it might be "the contact I imported appears in the list." Pick the concrete, on-screen fact that proves activation, and make the agent confirm it.

Everything between the two endpoints is allowed to be fuzzy. You do not assert that the wizard has exactly four steps, because that count is an experiment. You do not assert the tour tooltip copy, because growth rewrites it. You assert the endpoints and the irreversible facts in between — an account got created, a verification happened, a first artifact exists — and you let the agent tolerate the rest.

Writing the onboarding journey as one markdown test

BrowserBash markdown tests are committable *_test.md files where each list item is a step. They support @import composition and {{variables}} templating, and any variable you mark as a secret is masked as ***** in every log line. After each run BrowserBash writes a human-readable Result.md. That format is a natural fit for an onboarding flow, because the journey reads like the script you would hand a new hire doing manual QA.

Here is a realistic activation test for a generic SaaS trial. It signs up a fresh user, clears whatever onboarding wizard appears, creates a first project, and confirms the aha-moment.

browserbash testmd run ./onboarding_test.md \
  --record \
  --var EMAIL="trial+{{timestamp}}@example.com" \
  --secret SIGNUP_PASSWORD

And the onboarding_test.md itself:

# SaaS trial onboarding to aha-moment

- Open https://app.yourproduct.com/signup
- Enter "{{EMAIL}}" in the work email field
- Enter the password "{{SIGNUP_PASSWORD}}" and submit the signup form
- If an email verification screen appears, note it and continue if a "skip" or "verify later" option exists
- Complete any onboarding questions that appear (role, team size, use case) by choosing any valid option
- When the product asks you to create your first project, name it "Activation Smoke" and create it
- Verify the project board for "Activation Smoke" is visible and shows an empty-state or seeded content
- Confirm we reached the main dashboard for the new account

Read those steps the way the agent does. None of them name a CSS selector or an element ID. "Complete any onboarding questions that appear" is deliberately loose — it covers the two-step variant and the five-step variant equally. "If an email verification screen appears" handles the branch where verification is required versus the branch where the product lets you skip it. The only hard assertions are the ones that prove activation: a named project exists, the board renders, the dashboard loaded.

Why the secret masking matters here

Onboarding tests type credentials, just like login tests. The difference is that onboarding tests tend to run in more places — local dev, a nightly CI job, a pre-release smoke gate — which means the password has more chances to leak into shell history, pipeline logs, and archived run transcripts. Marking SIGNUP_PASSWORD as a secret means BrowserBash masks it as ***** everywhere it would otherwise print, so the credential never lands in a log line you forgot you were keeping.

Composing shared setup with @import

If you have several onboarding variants to test — a self-serve trial, an invited-teammate flow, an SSO trial — you do not want to repeat the signup boilerplate. Put the common opening steps in a signup_base_test.md and @import it at the top of each variant file. The variant files then only describe what is unique about that path. This keeps the activation assertions in one place and makes the experiment-specific differences obvious in the diff when someone reviews the test in a pull request.

Replay video: the part that saves you at 2 a.m.

The hardest onboarding failures are the ones that are real but rare. The signup succeeded, the wizard rendered, and then on step four the agent reported the project board never appeared. Was that a flaky network call? A genuine regression where the "create project" button does nothing? A new interstitial that stole focus? A static screenshot rarely tells you. A video does.

The --record flag captures a screenshot and a full .webm session video on any engine, recorded with ffmpeg. When the run fails you scrub the video and watch what the agent saw: the modal that popped up, the spinner that never resolved, the button that was there but disabled. On the builtin engine you additionally get a Playwright trace you can open in the trace viewer for a step-by-step, DOM-level replay.

For onboarding specifically, the video is worth more than for almost any other flow, because onboarding failures are so often about timing and state rather than a missing element. Sample data seeds asynchronously. The dashboard waits on a first API call. The verification email lands a beat late. Watching the replay is how you tell "the product is broken" apart from "the test moved too fast," and that distinction is the difference between filing a P1 and adding a wait.

If you want shareable history, browserbash connect plus --upload sends runs to a free, opt-in cloud dashboard with run history and per-run replay; free uploaded runs are kept 15 days. If you would rather keep everything local, browserbash dashboard gives you a fully local dashboard with no account at all. Both are optional — you never need an account to run a test.

# Nightly activation gate in CI, machine-readable output, video on disk
browserbash testmd run ./onboarding_test.md \
  --agent \
  --headless \
  --record \
  --var EMAIL="trial+$(date +%s)@example.com" \
  --secret SIGNUP_PASSWORD

How the AI agent tolerates A/B variants that break recorded flows

This is the crux, so let me be concrete about the mechanism rather than hand-waving "AI."

A recorded test is a fixed list of actions bound to a fixed map of the page: click the element at this path, type into the field with this ID, expect the next URL to be this. When an experiment changes the structure, the map is wrong and the playback derails. Self-healing helps when the element moved but the plan is still valid. It does not help when the plan itself is no longer valid — a new step in the middle, a different number of fields, a panel where a page used to be.

BrowserBash works the other way around. You give it an objective in English, and on every run the agent reads the live page, decides the next action from what is actually there, takes it, re-reads the result, and repeats until it reaches the goal or gives up. There is no stored map to go stale. If variant B inserts a "What brings you here today?" step that variant A never had, the step "complete any onboarding questions that appear" still covers it, because the agent is reacting to the page in front of it rather than replaying a recording of last week's page.

That is the honest reason an agent tolerates onboarding churn that wrecks a recorded Autify or Mabl flow. It is not magic and it is not free — see the model caveat below — but the architecture is genuinely better matched to a surface that changes structurally and often.

Where recorded tools still win

To be fair, the recorder model has real advantages and you should know them. A recorded test is faster to author for a non-engineer, it is deterministic in the sense that it does exactly the same thing every run, and it does not depend on a language model's judgment, so it cannot "decide" to do something surprising. For a stable, low-churn flow — a settings page that never changes, a legal-acceptance step locked by compliance — a recorded selector-based test is cheaper to run and easier to reason about. If your onboarding genuinely does not change month to month, you may not need an agent at all. The agent earns its keep precisely when the flow is volatile, which onboarding usually is.

A side-by-side on the activation use case

Here is how the common approaches line up specifically for testing a SaaS onboarding flow end to end. Pricing and internal model details for commercial tools are not always publicly specified, so where a fact is not public I say so rather than guess.

Capability for onboarding testing Recorded self-healing (Autify / Mabl / Testim) Hand-coded Playwright / Cypress BrowserBash (AI agent, plain English)
Survives renamed selector / moved button Yes, self-healing No, breaks until fixed Yes, re-reads page each run
Survives a new step inserted by an A/B test Often no, plan is fixed No, breaks Yes, agent adapts to live page
Authoring by a non-engineer Yes, recorder UI No, needs code Yes, plain English
Runs fully on your machine, no account Varies, mostly cloud Yes Yes, local is default
Cost of model / inference Vendor-priced, not always public None (no LLM) $0 on local models, or BYO key
Replay video of the run Usually yes (cloud) Via trace/video tooling Yes, --record .webm + trace
Machine-readable CI output Dashboard / API, varies Yes, native Yes, --agent NDJSON + exit codes
Deterministic, no LLM judgment Yes Yes No, agent makes choices

The table is not a clean sweep for any one tool, and it should not be. Coded Playwright is unbeatable on determinism and zero inference cost if you have the engineering hours to maintain it. Recorded tools win on no-code authoring for stable flows. BrowserBash wins on exactly the axis onboarding cares about most: tolerating structural change without a human re-recording the flow.

Putting it in CI as an activation gate

The point of automating this is to catch an activation regression before your users do. That means the onboarding test runs on every deploy to staging, ideally as a blocking gate on the release.

BrowserBash is built for that. The --agent flag emits NDJSON — one JSON event per line on stdout — so your pipeline reads structured events instead of scraping prose. Exit codes are unambiguous: 0 passed, 1 failed, 2 error, 3 timeout. A CI job can branch on the exit code directly: a 1 means activation broke and the deploy should stop; a 3 means the flow timed out and a human should look at the replay.

A practical pattern is to run the activation test against a fresh trial account on every staging deploy, with --record on, and --upload so the replay is one click away when the gate goes red. When it fails, the on-call engineer opens the video, watches the agent get stuck on step four, and within a minute knows whether a growth experiment broke the create-project button or whether the test just needs a wait. That loop — fail, watch, decide — is the whole value, and it is dramatically faster than re-running a recorder to reproduce.

You can read more patterns for wiring agent output into pipelines in the BrowserBash docs and guides, and there are worked examples of full journeys on the BrowserBash blog.

Choosing your model and provider (and the honest caveat)

BrowserBash is Ollama-first. It defaults to free local models with no API keys, and nothing leaves your machine; it auto-resolves a local Ollama install, then ANTHROPIC_API_KEY, then OPENROUTER_API_KEY. On local models you can guarantee a $0 model bill, which matters when you are running an onboarding test on every deploy and do not want a per-run inference charge.

Now the caveat, because onboarding is exactly the kind of flow where it bites. Very small local models — roughly 8B parameters and under — can be flaky on long, multi-step objectives, and an end-to-end onboarding journey is one of the longest objectives you will throw at the agent. A signup-to-aha-moment flow might be ten or twelve decisions deep, and a small model can lose the thread, skip the project-creation step, or misjudge whether the dashboard actually rendered. The sweet spot is a mid-size local model in the Qwen3 or Llama 3.3 70B class, or a capable hosted model for the hard flows. OpenRouter exposes genuinely free hosted options such as openai/gpt-oss-120b:free, and you can bring your own Anthropic Claude key when you want maximum reliability on a critical release gate.

The practical rule: prototype the onboarding test on whatever local model you have, but before you make it a blocking CI gate, run it a dozen times on a mid-size or hosted model and confirm it is stable. A flaky activation gate that cries wolf is worse than no gate.

Switching where the browser runs is a single --provider flag. The default local drives your own Chrome; cdp attaches to any DevTools endpoint; and browserbase, lambdatest, and browserstack run the browser in those clouds when you need a matrix of OS and browser versions for your onboarding flow.

# Run the onboarding flow on a hosted browser grid for cross-browser coverage
browserbash testmd run ./onboarding_test.md \
  --provider lambdatest \
  --record \
  --var EMAIL="trial+$(date +%s)@example.com" \
  --secret SIGNUP_PASSWORD

Who this is for

This approach earns its place if you are an activation-focused team — growth engineering, PLG, lifecycle — whose onboarding surface changes faster than your test suite can keep up. If a growth experiment regularly breaks your recorded flow, if you have muted your onboarding tests because they cry wolf, or if you want a single committable test that proves the trial-to-aha path on every deploy, the agent model fits.

It is less compelling if your onboarding is frozen by compliance and never changes, if you have a deep Playwright suite and the engineers to maintain it, or if you need bit-for-bit deterministic playback with no model in the loop. Be honest about which world you live in. For most SaaS teams shipping experiments weekly, onboarding is volatile, and that volatility is precisely what an intent-driven agent is good at. You can compare the tradeoffs against your stack on the BrowserBash features page, and there are real teardown examples on the case study page.

A pragmatic rollout plan

Start small and local. Write the onboarding_test.md for your single most important trial path and run it on your machine with --record. Watch the video, tighten the loose steps that are too loose and loosen the hard ones that are too rigid, and get it passing reliably three times in a row.

Then add the aha-moment assertion deliberately. This is the step people skip, and it is the one that matters. Do not stop at "signup worked" — push the test to the screen that proves value and assert a concrete on-screen fact there.

Next, run it across your live A/B variants. If you are bucketed into variant A locally, force variant B (via a query param, cookie, or test account) and run the same test unchanged. If it passes both without edits, your steps are at the right altitude. If it only passes one, your steps are too tied to that variant's structure — loosen them.

Finally, promote it to a staging gate with --agent and --headless, on a model you have verified is stable for long flows, with replay uploaded so failures are one click to diagnose. At that point you have an activation gate that survives the experiments your growth team will ship next week.

FAQ

What is SaaS onboarding flow testing?

It is the practice of automatically verifying the full path a new user takes from trial signup to their first moment of value, the aha-moment, rather than just checking that the signup form submits. A good onboarding test crosses every screen a real user hits — verification, the welcome wizard, first-project creation — and asserts a concrete fact that proves the user reached value, like a dashboard rendering with data.

How do you test an onboarding flow that has A/B variants?

Write the test as intent rather than a fixed sequence of clicks. With an AI-agent tool like BrowserBash you describe the goal in plain English, such as "complete any onboarding questions that appear, then create your first project," and the agent reads whatever variant is live and adapts. This survives structural changes — extra steps, reordered wizards, panels instead of pages — that break recorded flows pinned to one variant's layout.

Can I record a video of the onboarding test run?

Yes. BrowserBash's --record flag captures a screenshot and a full .webm session video on any engine, and the builtin engine also captures a Playwright trace you can open in the trace viewer. The video is especially useful for onboarding because so many failures are about timing and async state, and watching the replay tells you whether the product broke or the test just moved too fast.

Do I need a cloud account or API key to run onboarding tests?

No. BrowserBash runs locally by default, drives your own Chrome, and defaults to free local models with no API keys, so nothing leaves your machine. A free cloud dashboard with run history and replay is strictly opt-in via browserbash connect and --upload, and there is also a fully local dashboard with browserbash dashboard if you want run history without any account.

Ready to put an activation gate on your trial flow? Install with npm install -g browserbash-cli, write your first onboarding_test.md, and run it against a fresh trial account tonight. An account is optional — you can sign up for the free dashboard whenever you want shareable replays, but the CLI works the moment it installs.

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