Products
DeckSpaceLive DeckVoice$19 DeckTest$29 Claude Accounts$14 DeckStudioSoon
More
Docs Changelog Pricing Community Download Buy — $29 →
Home / DeckTest / Guides / Testing an OTP signup

Automating a signup flow that sends a verification email

A signup test is straightforward right up to the moment the product sends an email. The browser part is mechanical: open the page, fill three fields, submit. Then the flow leaves the browser. The next thing the test needs is a six-digit code sitting in a mailbox, and nothing on the page can reach it.

The short answer: drive the browser half in a real Chrome, read the verification message out of a real mailbox over IMAP, anchor the wait to a timestamp taken before you submit the form, and type the code back into the same session. The anchor is the part people skip, and it is the part that decides whether a passing run means anything. The rest of this page is why each of those four is load-bearing, and where the approach stops working.

That one gap is why so many signup flows are covered by a test that stops at "account created" and never touches the part users complain about: the code that arrives late, the second code that invalidates the first, the verify link pointing at the wrong host, the dashboard that loads empty behind the wall.

This page explains where a scripted browser test stops, what the three usual workarounds cost, and what DeckTest does instead — including which of those costs it still asks you to carry. If you have not set the app up yet, the quickstart covers installation first.

Where a scripted signup test stops

A browser test can assert only what the browser can reach. Verification mail is delivered out of band, to a mail server your test has no handle on. Three questions therefore fall outside the test entirely.

So the test is either cut short before the email, stubbed past it, or given a second system to talk to. Each of those has a price.

The three usual workarounds, and what each costs

A mail API. You sign the test up with an address at a service that exposes received mail over HTTP, then poll it for the message. It puts another vendor in the path of your signup mail, adds an account and a key to rotate, and gives your test an address that is not the kind your users have. Products that filter disposable domains will send your test down a branch real users never see.

A catch-all inbox you poll over IMAP. No third party, and the address looks like a real one. This is the right shape — it is the shape DeckTest uses — but a hand-rolled version has three details to get right before it is trustworthy. Plus-addressed mail is filed by some hosts into a subfolder rather than the inbox, so an INBOX-only search finds nothing. Verification mail lands in Junk often enough that the folder scan has to include it. And the mail credentials now live wherever your tests run, which is a decision to make on purpose rather than by accident.

The classic failure here is the stale code. If you fetch the newest verification message without anchoring to a timestamp captured before the form was submitted, the test will eventually pass using a code from an earlier run. The anchor also has to tolerate clock drift, and it is the server's received-time, not the sender's Date header, that is authoritative. DeckTest anchors on that received-time and allows for drift between your machine and the mail server, because this is the failure that makes a green run meaningless.

A test-only bypass in the product. A flag that fixes the code to 000000, or auto-verifies the account, or exposes the code on a debug route. It is the cheapest to build and the most expensive to own, because the path you test is no longer the path you ship. Everything the email step can break — a template that renders the wrong variable, a provider that delays past expiry, a link built with the wrong base URL — is precisely what the bypass skips. A bypass also has a way of remaining reachable in production long after anyone remembers it exists.

What DeckTest does instead

DeckTest is a Mac app plus a small bridge extension you load once into your own Chrome. The app runs a local relay bound to 127.0.0.1, authenticated with a token header, and your Claude connects to it over MCP and drives that browser. It is your real Chrome — the profile you are already signed into — not a clean automation profile.

The browser half of the run happens there. The email half does not happen in the browser at all. DeckTest reads the message over IMAP from a mailbox you nominate, using credentials held in a config file on your Mac. No third-party inbox sits in the path, and nothing in the product is bypassed — the address is a real address, the message is the one your product sent, and the code is the one a user would type.

  1. Fills and submits the signup form in your Chrome, using a fresh address.
  2. Polls the nominated mailbox over IMAP for a message that arrived after a timestamp stamped before the submit.
  3. Pulls the code, or the verification link, out of that message.
  4. Types the code back into the field in the same browser and submits it.
  5. Carries the session through to the logged-in state and keeps testing past it.

The limit is the mailbox credentials. The email step reads a mailbox over IMAP, so it needs a host, a user and a password in a config file before the first run that contains an email step. Set that up deliberately: there is no hosted inbox to fall back on, and a run that reaches an email step without credentials stops with an error rather than skipping the step. Point it at a mailbox you keep for testing, not your primary personal account.

The repeatable form of this is a YAML scenario — ask Claude to save the run as one and you get a plain-text file you can re-run before every deploy. Its code and link steps scan INBOX, its plus-address subfolders and Junk, and wait for a message that arrived after that pre-submit timestamp, with a default budget of 180 seconds. A run that never receives the mail records a blocker-severity bug naming the address it waited on, rather than hanging.

Reading the code out of the message

Extraction is rule-based, and the rules are worth knowing because they tell you when it will miss. A labelled code wins first: words like code, OTP, one-time, verification, verify, passcode, PIN or token followed within a short span by four to eight digits. Failing that, any standalone run of four to eight digits is a candidate, six-digit runs are preferred, and four-digit years are discarded so a copyright line cannot be mistaken for a code.

For magic links, a URL whose host matches the site under test wins, and among those a path containing verify, confirm, magic, token, activate, signup, validate or auth wins again.

Those rules are deliberate, not clever. Markup is stripped before matching, so a code rendered as one digit per table cell leaves no four-to-eight digit run in the text and will not be found.

What it checks once it is through the wall

Getting past verification is not the finish. The step after the code is where the interesting failures live, and a screenshot alone will not show them. Three kinds of evidence are available, and they are not all automatic.

The console and network capture is a window, not a session recording, and it does not watch the same browser as the rest of the run. It runs for a fixed number of seconds, eight by default, in a dedicated automation Chrome with its own profile and cookie jar, driven over the debugging protocol. Plan to reproduce the step you care about in that channel rather than expecting a transcript of the logged-in profile run.

What the report contains

Every run writes a folder: a human-readable report.md, a machine-readable run.json, and a screenshots directory.

The verdict is deliberately strict. Any step that is not a pass counts as a failure, including skipped and unknown ones; a single blocker or critical bug fails the run even when every step passed; and a run with no steps is never a pass. An empty report is not evidence that anything worked.

Requirements, and what it will not do

Captchas are the honest edge. A readable image or text captcha is extracted from the page for your Claude to read, but only on hosts you have added to the auto-solve scope, which out of the box contains only localhost and 127.0.0.1 — so add your staging host before the first run. Behavioural challenges are never faked. They are flagged and escalated to you as a desktop notification so you can clear the challenge yourself and let the run continue.

Test the real signup path, not a bypass.

$29 once, two Macs, 7-day refund. Needs macOS 11 or newer, Google Chrome, and the Claude you already pay for — no extra AI fees from us.

Get DeckTest — $29, once

Common questions

Can it test a flow that sends a magic link instead of a code?

Yes. The same message is scanned for a link as well as a code. A URL whose host matches the site under test is preferred, and among those, one whose path contains verify, confirm, magic, token, activate, signup, validate or auth. The run then follows that link in the same browser and carries on into the logged-in state.

Do I have to give DeckTest access to a mail account?

Yes, for the email step. DeckTest reads the verification message over IMAP, so it needs a host, a user and a password for a mailbox you nominate, kept in a config file on your Mac. There is no hosted inbox, no OAuth flow, and no fallback — a run with an email step and no credentials configured stops with an error rather than guessing. Use a mailbox you are content to keep test credentials for, not your primary personal account. The browser half of the run is separate: that drives your own logged-in Chrome and needs nothing extra.

What happens if the verification email never arrives?

In a saved YAML scenario the step waits for a message that arrived after a timestamp stamped before the form was submitted, with a default budget of 180 seconds. If nothing matching arrives, the step fails and the run records a blocker-severity bug titled "Verification email not received", with the address it was waiting on as evidence. It does not hang and it does not pass.

Is this a recorded script that breaks when the UI changes?

No. Your Claude reads the page in front of it and decides what to do next, using tools that read visible text and HTML and scan interactive elements for resilient selectors. You can still save a flow as a YAML scenario and re-run it before every deploy — the steps are fixed, the judgement is fresh each run.

Can I point it at localhost or a staging site?

Yes, and those are the intended targets. Out of the box the captcha auto-solve scope covers only localhost and 127.0.0.1, so add your staging host in Captcha settings before the first run or a readable captcha there will be flagged instead of read. Test what you own or are authorized to test.