Dub End to End User Tests

App  app.dub.co
Run date  2026-05-22
Status  5 / 5 passed
01

Sign up for Dub via email + OTP

PASSED 10.4s

Create a brand-new Dub account using a disposable email address, verify it via the 6-digit OTP code, and land on the onboarding welcome screen.

Create a fresh disposable email inbox via AgentMail SDK
Navigate to app.dub.co/register
Type the email address into the Email field
Click on Sign Up
Type a strong password into the Password field
Click on Sign Up again
Wait for the OTP verification page
Read the 6-digit code from the inbox Spam folder
Type the 6-digit code into the OTP input boxes
Verify URL reaches /onboarding/welcome
01 · signup · 10.4s
Pass criteria
Browser URL ends with /onboarding/welcome AND the page shows "Welcome to Dub" with a Get started button.
Notes from discovery

Spam-folder trap: Dub's OTPs ship via Amazon SES and get flagged as spam by AgentMail. The lib/agentmail.ts helper was patched to default includeSpam:true; without it, every Dub signup attempt timed out with a misleading "INBOX EMPTY" diagnostic.

Two-step signup quirk: Dub's signup form renders email + password in one place but submits email-only first to reveal the password field. Naive "fill both then submit once" approaches fail silently.

02

Complete the onboarding wizard and reach the workspace dashboard

PASSED 12.5s

From a freshly signed-up account at /onboarding/welcome, walk through the 5-step Dub onboarding wizard and land on the workspace links dashboard.

Start at /onboarding/welcome
Click on Get started
Type a workspace name into the Name field
Click on Create workspace
Click on Continue with Dub Links
Click on I'll do this later on the custom-domain step
Click on start on the free plan
Wait for /onboarding/success interstitial
Click on Go to your dashboard
Verify URL reaches /<slug>/links
02 · onboarding · 12.5s
Pass criteria
URL matches https://app.dub.co/<slug>/links (no /onboarding/* prefix) AND the page shows "No links yet" with the Create link CTA. The slug is stable across reloads.
Notes from discovery

Hidden interstitial: the first run failed because we expected the free-plan click to land directly at /<slug>/links. Dub interposes /onboarding/success with a "Go to your dashboard" button — easy to miss from the codebase alone.

State chaining: Flow 02 starts by loading Playwright storageState saved by Flow 01. Cookies + localStorage from the post-signup state are enough to resume onboarding without re-auth.

03

Create the first short link in the new workspace

PASSED 7.6s

From the empty-state workspace dashboard, open the link-builder modal, paste a destination URL, submit, and confirm a new short link appears in the list.

Navigate to /<slug>/links
Verify empty-state "No links yet" visible
Press c to open the link-builder modal
Type a destination URL into the URL field
Press Enter to submit
Verify modal closed
Verify new link row appears in the list
03 · create link · 7.6s
Pass criteria
Modal closed (no input with name="url" in DOM) AND the page body contains the destination URL's host. URL footer reads "Viewing 1-1 of 1 link".
Notes from discovery

Two buttons named "Create link": the empty-state trigger and the modal's submit have identical text. Disambiguating via DOM queries is fragile; pressing Enter on the focused URL input sidesteps the problem entirely.

Keyboard shortcut from code: useKeyboardShortcut("c", () => setShowLinkBuilder(true)). Using c to open the modal is faster and matches power-user behavior.

04

Connect a custom domain to the workspace

PASSED 8.2s

Navigate to the Custom Domains section, add a new domain via the modal, and confirm it appears in the cards list ready for DNS configuration.

Navigate to /<slug>/links/domains
Click on Add Domain (capital D — opens the modal)
Type a domain name into the Your domain field
Wait for the green "ready to connect" indicator
Click on Add domain (lowercase d — the modal submit)
Verify modal closed
Verify domain card appears with Primary + Invalid badges
04 · add domain · 8.2s
Pass criteria
Modal closed (no name="slug" input in DOM) AND the page body contains the domain string we entered. List footer reads "Viewing 1-1 of 1 domain".
Notes from discovery

Three look-alike buttons: case-insensitive matching for "Add domain" matched the modal's TRIGGER button (capital "Add Domain") instead of the form submit (lowercase "Add domain"). The first run silently no-op'd — clicking the trigger while the modal was already open did nothing. Fix: drop the /i regex flag. Case is the disambiguator Dub chose.

"Invalid" status is the expected end state: the test never configures actual DNS records, so the domain stays in the "registered but not verified" half-state. The assertion is "form succeeded + domain appears in list" — not "verified".

05

View workspace analytics dashboard

PASSED 5.5s

Navigate to the Analytics view, confirm all chart sections render even at zero traffic, and verify the page is interactive by opening the date-range filter.

Navigate to /<slug>/analytics
Verify Clicks, Devices, Locations, Referrers, Top Links sections all rendered
Click on Last 24 hours date-range button
Verify date-range popover opens with preset options
05 · view analytics · 5.5s
Pass criteria
Page body contains at least 3 of: clicks, devices, locations, referrers, top links AND the date-range toggle opens its popover (popover reveals Last 7 days, Last 30 days, Month to Date, etc).
Notes from discovery

Read-only flow: no state mutation. dub-after-view-analytics.json is saved but mostly identical to the input state.

Why click a date-range toggle: the page-load assertion only proves the page didn't crash. Opening the popover proves the React app is hydrated and event handlers are wired up — a real reactivity check.