CU-18: New-user onboarding wizard (4-step) and schedule-setup prompt

Priority: P2 Accounts/sessions: A FRESH throwaway Calendo account created during this run via a Gmail plus-alias (ravikantguptaofficial+onb-<RUNID>@gmail.com). Also requires the persisted Gmail session for ravikantguptaofficial@gmail.com (to read the email-verification message that lands in the plus-alias inbox). No P3/Outlook needed. Parallel-safe: Yes — this suite creates and destroys its own isolated account and touches nothing on P1's real Calendo data, calendars, or bookings. Exclusive (rewrites global host availability?): No. It only edits the throwaway account's own availability, never P1's. Estimated time: ~22 minutes. L3 reality checks: One light L3: confirm the verification email actually arrived in Gmail (required to get past the email-verification gate). No calendar-event L3 (this suite intentionally does NOT connect a real calendar).

Goal

This suite proves that a brand-new person who signs up for Calendo is correctly guided through first-run setup: the email-verification gate, the multi-step onboarding wizard (calendar connect, weekly schedule, meeting location, AI assistant intro), and the "your schedule has no hours" nudge for users with empty availability. It also proves that completing/dismissing each of these is remembered server-side so the user is never nagged twice. This matters because onboarding is the first impression and the highest-drop-off moment of the product; a broken or repeating wizard kills activation.

Preconditions

Test data

Steps

  1. Action: Open the registration page at https://calendo.dev/auth/register.html. Expect: A "Create account" form with a Full name field (#name), Email field (#email), Password field (#password), and a "Create account" submit button (#submitBtn). [L1]
  1. Action: Fill the form — name = Onb Test <RUNID> (#name), email = ravikantguptaofficial+onb-<RUNID>@gmail.com (#email), password = the throwaway password (#password). Click "Create account" (#submitBtn). Expect: No inline error appears in the error box (#error stays hidden). The card content is replaced in-place with a verification gate that reads "Before continuing, we need to verify your email address." and shows your +onb-<RUNID> email in bold, plus a "resend it to you" link (#resendLink). This proves a fresh email/password account is created UNVERIFIED. Capture screenshot: cu18-01-verify-gate.png [L1]
  1. Action: Confirm the email-verification gate is enforced by trying to reach the dashboard directly: open https://calendo.dev/dashboard/. Expect: You are redirected to https://calendo.dev/auth/verify.html (the dashboard JS blocks unverified users). You do NOT see the dashboard or the onboarding wizard yet. This proves email verification gates onboarding. [L2]
  1. Action: Open Gmail at https://mail.google.com (already logged in as ravikantguptaofficial@gmail.com) and search for the verification email using the query: subject:(verify) onb-<RUNID> — if that returns nothing, broaden to to:ravikantguptaofficial+onb-<RUNID>@gmail.com. Expect: A recent Calendo verification email addressed to the +onb-<RUNID> alias, containing a verification link/button (the link targets https://calendo.dev/api/auth/verify-email?token=...). Capture screenshot: cu18-02-verify-email.png [L3]
  1. Action: Click the verification link/button inside that email. Expect: A Calendo "Email Verified" confirmation page appears ("Your email address has been verified successfully") with a "Continue Setup" button, and it auto-redirects after ~2 seconds to https://calendo.dev/onboarding/. [L1]
  1. Action: Observe the page you land on after the redirect (https://calendo.dev/onboarding/). Expect: The standalone "Connect your calendars" page — heading "Connect your calendars", a "Google Calendar" button (#googleBtn), an "Outlook / Microsoft 365" button (#outlookBtn), a "Continue to Dashboard" link (#continueBtn), and an "I'll do this later" skip link (.btn-skip). Note: this is the POST-VERIFY standalone onboarding page, distinct from the in-dashboard 4-step wizard you will see next. Do NOT click either calendar button (that would start a cold OAuth login). Capture screenshot: cu18-03-standalone-onboarding.png [L1]
  1. Action: Click "I'll do this later" (.btn-skip) to proceed to the dashboard WITHOUT connecting a calendar (this link points to /dashboard/). Expect: The browser navigates to https://calendo.dev/dashboard/. [L1]
  1. Action: Observe what the dashboard shows. Expect: Because onboarding is not yet marked complete server-side AND no calendar is connected, the dashboard does NOT render the normal app; instead the full-screen in-dashboard onboarding wizard overlay (#calendarOnboarding) is displayed and the main app layout (#appLayout) is hidden. This is the 4-step wizard. Capture screenshot: cu18-04-wizard-step1.png [L1]
  1. Action: Read the step indicator at the top of the wizard (#onboardingSteps). Expect: Exactly four labeled step pills: "1. Calendar", "2. Schedule", "3. Location", "4. AI Assistant" (.onb-step elements with data-step 1–4). Step 1 pill has the active class. This confirms the wizard is 4 steps with the expected titles. [L1]
  1. Action: Read the content of the Step 1 panel (#onb-step-1). Expect: A calendar emoji, heading "Connect your calendar", explanatory copy about preventing double-bookings, a "Connect Google Calendar" button (links to /api/calendars/connect/google), a "Connect Outlook Calendar" button (links to /api/calendars/connect/outlook), and a Privacy Policy link. Confirm there is NO "skip"/"next"/"I'll do this later" control on Step 1 — the only forward path is connecting a calendar. Record this observation. [L1]
  1. Action (CONDITIONAL — only if a calendar can be connected without a cold password prompt; otherwise SKIP to Step 18): Click "Connect Google Calendar". If you are immediately bounced into a Google account chooser that requires entering a password or approving consent for a brand-new account, STOP — do not enter credentials. Navigate back to https://calendo.dev/dashboard/ and proceed to Step 18, recording "Steps 2–4 walk-through BLOCKED-by-design: Step 1 requires a cold OAuth login for the throwaway account." Expect (only if connect somehow completes without a cold login): You return to the dashboard wizard, which now advances to Step 2 automatically (the dashboard resumes onboarding from step 2 when a calendar exists but onboarding is unfinished). [L1]
  1. Action (CONDITIONAL — only reached if Step 11 connected a calendar): On Step 2 (#onb-step-2, "When are you available to meet?"), inspect the weekly-hours editor (#onb-weekly-editor) and the timezone dropdown (#onb-timezone-select). The preset is Mon–Fri 09:00–17:00 already filled in. Leave the preset as-is. Click "Next" (#onb-save-schedule). Expect: The wizard advances to Step 3 and the preset weekly rules are saved to the default availability schedule via PUT /api/availability-schedules/:id. Capture screenshot: cu18-05-wizard-step2.png [L1]
  1. Action (CONDITIONAL): On Step 3 (#onb-step-3, "How would you like to meet with people?"), observe the five location preset cards (.onb-loc-opt): Zoom, Google Meet (preselected, blue border), Microsoft Teams, In-person, Phone call. Leave the default "Google Meet" selected. Click "Next" (#onb-save-location). Expect: The wizard advances to Step 4 and the chosen location is applied to the auto-created event types via PUT /api/event-types/:id. Capture screenshot: cu18-06-wizard-step3.png [L1]
  1. Action (CONDITIONAL): On Step 4 (#onb-step-4, "Meet your AI assistant"), observe the sparkle emoji, the "Meet your AI assistant" heading, the "You're all set! Your booking link is ready to share." copy, and the "Go to Dashboard" button (#onb-finish). Click "Go to Dashboard" (#onb-finish). Expect: The wizard overlay disappears, POST /api/onboarding/complete is called, the page reloads, and the normal dashboard app (#appLayout) renders with the sidebar and an Overview tab. Capture screenshot: cu18-07-wizard-step4-and-dashboard.png [L1]
  1. Action (CONDITIONAL — completion persistence): Reload https://calendo.dev/dashboard/ (hard refresh). Expect: The 4-step wizard does NOT reappear; the normal dashboard renders directly. This proves onboarding_completed persisted server-side. [L2]
  1. Action (CONDITIONAL — cross-session persistence): In a fresh browser tab/profile (or after clearing only the in-memory state, NOT the session cookie), open https://calendo.dev/api/auth/me. Expect: The JSON includes "onboarding_completed": 1. This confirms completion is stored on the user record, not in localStorage. [L2]
  1. Action (CONDITIONAL): If Steps 11–16 ran, you have a fully onboarded throwaway account; proceed to Step 19 (schedule-setup prompt) using this account. If Steps 11–16 were SKIPPED (calendar connect blocked), go to Step 18 first.
  1. Action (ALTERNATE completion-persistence path — required when Steps 11–16 were skipped): Because the wizard cannot be driven to completion without a cold OAuth login, verify the completion mechanism via the API the wizard itself calls. With the throwaway session active, confirm the PRE-completion state by opening https://calendo.dev/api/auth/me and noting "onboarding_completed": 0. Then mark onboarding complete exactly as the "Go to Dashboard" button does, by issuing the same authenticated request the app uses: in the browser DevTools console on https://calendo.dev/dashboard/ run await fetch('/api/onboarding/complete', {method:'POST', credentials:'include'}).then(r=>r.status). Expect: Status 200. Reload https://calendo.dev/dashboard/. Expect: The wizard no longer appears; the normal dashboard renders. Re-open https://calendo.dev/api/auth/me. Expect: "onboarding_completed": 1. This proves the completion flag is server-side and gates the wizard, even though the UI steps 2–4 could not be hand-driven. Record clearly in the report that steps 2–4 were verified structurally (Steps 9–10, 12–14 by inspection) but completion was triggered via the app's own endpoint, not by clicking through OAuth. Capture screenshot: cu18-08-onboarding-complete-api.png [L2]
  1. Action (schedule-setup empty-availability nudge — setup): The nudge only fires when the default schedule has ZERO available hours. A fresh account ships with Mon–Fri 09:00–17:00, so you must first empty it. In the dashboard, click the "Availability" sidebar tab (a[data-tab="availability"]). In the weekly editor, click each active day circle ([data-day-circle] with class active, days Mon–Fri) to toggle every day to "inactive", removing all windows. Then click "Save Schedule" (#saveScheduleBtn). Expect: A success toast; all seven days show "Unavailable". This makes the default schedule's rules empty. Capture screenshot: cu18-09-empty-availability.png [L1]
  1. Action: Reload https://calendo.dev/dashboard/ and wait ~2 seconds on the Overview tab. Expect: A native browser confirm() dialog appears reading approximately "Welcome! Your schedule has no available hours set. Would you like to set up your availability now?" with OK/Cancel. This is the schedule-setup nudge for users with empty availability. Capture screenshot: cu18-10-schedule-prompt.png [L1]
  1. Action: In that confirm dialog, click Cancel (the "dismiss" path). Expect: The dialog closes, you stay on the dashboard, and POST /api/schedule-setup/dismiss is fired in the background (sets schedule_setup_dismissed = 1). [L1]
  1. Action (dismissal persistence): Reload https://calendo.dev/dashboard/ again and wait ~2 seconds. Expect: The schedule-setup confirm dialog does NOT reappear (it was dismissed and the flag persisted). [L2]
  1. Action (dismissal persistence — server-backed): Open https://calendo.dev/api/auth/me. Expect: The JSON includes "schedule_setup_dismissed": 1. This proves the dismissal is stored on the user record and not just suppressed client-side. Capture screenshot: cu18-11-dismiss-persisted-api.png [L2]
  1. Action (positive-path sanity for the nudge — optional): If you want to also confirm the OK branch, you cannot re-trigger the dialog for this same user (it's dismissed). Skip re-triggering; the Cancel/persist path above is sufficient for this suite. Note in the report that the "OK → switch to Availability tab" branch was verified by code inspection only (line switchTab('availability')), not exercised, to avoid creating a second throwaway account. [L1]

L3 reality checks

Cleanup

Run cleanup whether the suite passed or failed, to keep the inbox/account list clean.

  1. Delete the throwaway Calendo account. Log in to https://calendo.dev/dashboard/ as the throwaway account if not already (use the throwaway email/password). Open the "Settings" sidebar tab (a[data-tab="settings"]), scroll to the red "Danger Zone" card, click "Delete My Account" (#deleteAccountBtn). In the first confirm dialog (#confirmDialog), click "Delete Everything" (#dialogConfirm). In the second confirm dialog, click "Yes, Delete My Account" (#dialogConfirm). Expect: Redirect to https://calendo.dev/auth/login.html?deleted=1. This calls DELETE /api/auth/delete-account, which removes the user and all their event types, bookings, availability schedules, booking pages, calendar connections, and sessions.
  2. Verify deletion: Open https://calendo.dev/api/auth/me. Expect: 401 Unauthorized (session gone / user deleted). Optionally attempt login at https://calendo.dev/auth/login.html with the throwaway email — expect "Invalid email or password".
  3. Gmail tidy (optional, host inbox hygiene): In Gmail, search onb-<RUNID> and move the verification email (and any onboarding/welcome mail) to Trash so the shared inbox stays clean for future runs. Do NOT delete unrelated mail.
  4. No calendar cleanup needed — no real calendar was connected and no events were created.

Pass/Fail criteria

The run PASSES only if ALL of the following are true:

The run is recorded as PARTIAL (not a hard FAIL) but still acceptable if: Step 1 of the wizard could not be completed without a cold OAuth login (expected for a throwaway account), provided the structural checks (Steps 9–10) passed and completion-persistence was verified via Step 18. The report MUST state this explicitly.

The run FAILS if: the verification email never arrives; an unverified account can reach the dashboard; the wizard does not appear for a new uncompleted user; the wizard reappears after completion (persistence broken); the empty-availability prompt never appears when availability is empty; the prompt reappears after being dismissed (dismissal not persisted); or /api/auth/me does not reflect the expected flag values.

Evidence to capture

Manual residue / cannot-verify