Skip to main content

Free-first trial & claim — Manual Test Checklist

Everything here is done by clicking in the app. You do not need code, a database tool, or a browser developer console. If a step ever seems to need one, it belongs in Engineer checks at the end — skip it and say so in your report.

What this verifies. New accounts start on the Free plan. The free month is not given automatically any more — the app offers it, and the person accepts by entering a payment method. This checklist confirms the offer appears when it should, never appears when it should not, and that what the screens promise matches what is actually charged.


Before you start

What you need

BuildThe staging desktop build. The QA panel used below does not exist in the production app. Case 12 additionally needs the staging web app in a browser.
AccountsOne Google account is enough. Reset it between cases with the four steps in Getting a "fresh account" below.
PaymentStripe test card 4242 4242 4242 4242, any future expiry date, any 3-digit CVC. To test a refusal: 4000 0000 0000 0002. No real money moves on staging.
OptionalStripe dashboard access, for the steps marked (Stripe dashboard). Skip them if you do not have it.

The QA panel

Several steps use it. Open Settings → QA. Two groups matter:

  • QA → Entitlements — a Plan dropdown with an Apply button, Trial state buttons (Make eligible for $0 trial, Start 15-day trial, Trial ends in 1 day, Expire trial now), and Seed usage near limit with a Set count to limit - 2 button. Make eligible for $0 trial returns the signed-in workspace to Free, clears its usage, and restores the offer used by the sidebar's warm trial button. It refuses to run while a Stripe subscription is still active.
  • QA → Surfaces → Onboarding — a Replay button ("Replay onboarding (keep auth)") that walks the permission steps and the survey again on the same account.

One QA setting can change what you see here: QA → Entitlements → Checkout flow. Leave it on In-app fullscreen (the default) for these cases — set to Browser checkout, payment opens in your browser instead and the steps below will not match.

Getting a "fresh account" without collecting Google accounts

The app asks for the trial once, and remembers per account and per machine that it asked. Where a case says "fresh account", you do not need a brand-new Google address — reset the one you have:

  1. If you have set a plan through QA → Entitlements on it, set it back to Basic first (Plan dropdown → BasicApply). A QA-set plan leaves a placeholder subscription that deletion cannot cancel, and the delete fails with an error until you clear it.
  2. Settings → Account → Delete account → type DELETE ACCOUNTPermanently delete account.
  3. Quit the app and reopen it before signing in again. Skipping the restart leaves the app thinking onboarding is done, and you land in the workspace with no questions and no offer.
  4. Remove the app from your Google account at myaccount.google.com/connections — find the HabitatZero entry and remove its access. Without this, Google silently re-grants what it already approved, so the consent screen and the calendar permission prompt never appear and those parts go untested.
  5. Sign in with the same Google address. It behaves as a first run: new onboarding, new starter sidebar, and the offer is on the table again.

Signing out and back in without deleting does not reset anything — that is correct behaviour, and several cases below check it.

If you only need to retest the sidebar's Start your trial for $0 button or another offer surface, use QA → Entitlements → Make eligible for $0 trial instead of deleting the account. That control does not reset onboarding or the per-device welcome-offer record, so the full deletion steps above still apply wherever this checklist explicitly asks for a "fresh account".

On the web app, deleting the account is not enough. The "onboarding done" marker lives in that browser's own storage (focusflow.onboardingComplete in localStorage), not on the account — so a browser you have used before will skip straight to the workspace, no matter how fresh the account is. Use a private/incognito window for web runs, or clear the site's data first. Deleting on the desktop app cannot clear it either; they keep separate copies.

Which build to use

This matters, or none of the cases below will work. The trial prompts are switched on from the 5.0.0 release onward. Download the current staging build from latest-mac.dmg (Windows: latest-win.exe) and check Settings → About reads 5.0.1 or higher. Below that, the welcome offer, the launch popup, the template gate and the trial pill are all silent by design, and every case in sections A to E will look broken when it is behaving correctly.

Quick way to tell before you start: sign in on a fresh account and wait for the app to settle. If no offer appears at all, you are on the wrong build — stop and ask, rather than filing cases 1 to 12 as failures.

What is being tested

This checklist describes one configuration, the one shipping from 5.0.0 onward:

  • A new account starts on Free — no automatic trial.
  • The app offers a 30-day free month, accepted with a payment method.
  • Closing an offer is not an answer. The person has neither accepted nor refused, so the app may ask again later, politely.

If someone tells you the trial offer has been switched off, or that new accounts are getting an automatic trial again, stop — this checklist no longer matches the app and needs rewriting first.

Desktop and web only. iOS purchases through the App Store instead — no trial framing, price shown up front, no automatic welcome offer. That path is covered in apps/clients/docs/testing/manual-tests/appstore-purchase-tests.md in the repo, not here.


A — Being offered the trial

1. The offer arrives once, shortly after the first screen loads

Account: fresh.

Steps

  1. Sign in and complete onboarding.
  2. Wait for the main app to finish loading. The offer appears on top of it by itself — expect it to cover the workspace, so check anything about the sidebar after you close it.
  3. Close the offer by clicking outside it.
  4. Quit the app and reopen it.

Check

  • A full-screen offer appears about a second after the app finishes loading — not on top of a half-drawn window, and not during onboarding
  • It offers a free month
  • After closing it and reopening the app, it does not appear again on this computer

2. Closing the offer does not take the offer away

Account: continue from case 1.

Steps

  1. At the bottom of the sidebar, find the Basic Plan card and its Start your trial for $0 button.
  2. Click it.

Check

  • The offer is still available, with the same free month as before — closing it earlier did not withdraw it
  • Nothing in the app behaves as though you refused: no shortened countdown, no "you declined" wording anywhere

3. A different computer asks again — skip unless you happen to have one

Skip this case unless a second computer is already in front of you. Nothing else depends on it, and it is not worth arranging.

If you do have one, sign in there with the same account as case 1.

  • The offer appears again on the second computer. That is expected today — the app remembers per machine, not per person

There are two ways in, and they are not the same journey. Only the browser one leaves the page and comes back through Google, so run section B on both.

Way inDo this
Browser (how a real visitor arrives)Open the template page on the staging site, click Personalize this template, then Sign in with Google. That is the whole journey — the site sends you into the web app.
Desktop appWith the staging app installed, open the web app's template link and use the page's option to open the app.

Other template pages on that site work the same way — swap morning-pages in the address for another one if that page is ever taken down.

The desktop way in uses the web app's own landing page, which only appears when you are signed out: it leads with Download the desktop app and carries a small Already have the app? Open it link underneath. Use that link — the Download button fetches the production build.

The two ways in also differ in what you see first, which is deliberate: from the site you go straight into Google sign-in, while opening the web app's link directly shows a landing page for the template first, offering to download or open the app.

4. The offer appears when you use the template, and you always get your copy

Account: three runs per way in. Do the two closing runs first on one fresh account, then use a second fresh account for the accepting run — once a payment method is attached, the account can no longer be reset for another run.

Steps

  1. Open the link for your way in from the table above while signed out, and sign in.
  2. You land inside the app on that template's own screen. Nothing is yours yet — this is the template, not your copy.
  3. Click Use this template. The offer appears here, not before.
  4. Take a different exit each run: close with the X, click outside, and finally accept and pay.

Check

  • After signing in you land on the template's own screen — not on a generic home, and not on a copy. No copy exists yet, and no onboarding questions are asked
  • The offer appears only after you click Use this template, and is shown over that template's own picture rather than generic artwork
  • All three exits end with your copy open and editable — being asked never costs you the template
  • The two closing exits create the copy immediately
  • Exactly one copy exists per run — check the sidebar for duplicates
  • Using a second template in the same session does not ask again, whichever exit you took
  • Browser only: refreshing the page before clicking Use this template still creates no copy, and the address bar no longer carries the sign-in parameters
  • Browser only: the plain URL shows the download-first landing page before sign-in, and its "open the app" option launches the installed staging build

Account: fresh.

Steps

  1. Open one template link and wait for the offer.
  2. While it is on screen, open a second, different template link.
  3. Answer the offer once.

Check

  • Only one offer is on screen — the second link waits behind it, it does not stack a second copy
  • Answering once opens both templates, in the order you opened the links
  • Neither template is missing, and neither is duplicated

Worth running twice: this is a timing issue, so it can pass by luck.

6. The template ask replaces the ordinary one

Account: fresh, arriving through a template link rather than signing in normally.

Steps Complete the arrival, stay in the app a minute, then quit and reopen once.

Check

  • The ordinary full-screen offer does not appear on top of the template arrival — the template you came for is the ask
  • No offer pops up again during that session
  • A later ordinary launch behaves normally

7. No second offer over a payment screen

Account: fresh.

Steps

  1. Open the offer and start paying.
  2. While the payment screen is up — including the moment just before it appears — open a template link.
  3. Close or finish the payment.

Check

  • No second offer appears over the payment screen
  • The template opens once the payment screen closes
  • If the payment screen fails to open at all, the template still opens — it is never left stuck behind a screen that never came

C — The reminder popup

8. It waits for a second visit, and for you to have made something

Account: fresh.

Steps

  1. First launch: sign in, complete onboarding, close the welcome offer, quit.
  2. Second launch: wait for the workspace to settle, then quit.
  3. Launch once more.

Check

  • It never appears on the first launch — the app is used before it is sold
  • From the second launch onward it may appear, once the workspace has documents in it
  • It does not flash up and vanish while the app is still loading
  • When it does appear, it offers the same free month as the welcome offer did

The gate is "this workspace has documents", and onboarding seeds five starter documents, so a normal new account satisfies that from the start — you do not need to create anything by hand, and the popup appearing on launch two is correct. An account with a genuinely empty sidebar is not reachable through onboarding, so the empty-workspace branch is an engineer check.

9. It stops pestering

Account: continue from case 8.

Check

  • Having seen it once, reopening the app over the same day does not show it again
  • It appears at most three times ever for one account, spaced roughly a week apart. Testing the full week gap is an engineer check — instead confirm it does not reappear on repeated launches the same day
  • A different account on the same machine starts fresh and is not affected by the first account's count

D — Paying

10. Accepting costs nothing today and starts a real trial

Account: fresh, ideally arriving from a template link so the template resumes afterwards.

Steps Accept the offer, enter 4242 4242 4242 4242, confirm.

Check

  • The payment screen says $0 today and names the date of the first charge
  • That date matches the "your plan begins" date on the timeline shown with the offer
  • A success screen appears and the Basic Plan card disappears from the sidebar, with no reload. It hides rather than relabelling to Premium, because a trialing subscription already counts as premium. Confirm Premium in Settings → Plans and Billing
  • If you arrived from a template link, the template opens after the success screen
  • Reopening the app shows no further offers anywhere — no full-screen offer, no popup, no pill
  • (Stripe dashboard) One subscription, status trialing, first invoice 30 days out, amount 0

11. A refused card changes nothing

Account: fresh.

Steps Try to accept with 4000 0000 0000 0002, read the message, then close.

Check

  • The message appears inside the payment screen and explains the card problem in ordinary words — no error codes, no raw text from the payment provider
  • Trying again with the good card in the same screen works
  • Any template you were arriving at still opens
  • (Stripe dashboard) No subscription was created by the failed attempt

12. Paying in the browser survives leaving the page (web app only)

Account: fresh, in the web app rather than the desktop app, arriving the browser way described at the top of section B.

If the payment step does not offer PayPal, skip this case and say so — it is not configured, and there is nothing for you to fix.

Steps

  1. Open a template link, sign in, choose Use this template, accept the offer.
  2. Choose PayPal and approve — this leaves the page entirely and comes back.
  3. Separately: do it again, and reload the tab while it is coming back.

Check

  • You come back signed in and the payment completes without asking again
  • The template you were claiming for still opens
  • The web address is tidy afterwards — reloading does not drop you back into a half-finished payment
  • (Stripe dashboard) Exactly one subscription exists afterwards, not two

E — What the screens promise

13. A shorter trial is never called "a month"

Account: one Free account that has not been offered yet, plus one where you use QA → Entitlements → Trial state → Start 15-day trial, and one where you use Trial ends in 1 day.

Steps Open the offer on each and read the badge, the button, the timeline heading, and the payment screen.

Check

  • Free, never offered: everything says a month
  • 15-day trial: every one of those four places says 15 days, and none says "month"
  • 1-day trial: says 1 day, and never "month"
  • Those four places always agree with each other — one saying "month" while another says days is the bug
  • The reminder step on the timeline lands 3 days before the end, and the "plan begins" date is the same date the payment screen names

14. People who already pay are not sold a trial

Account: one with a real subscription (finish case 10), and one set to Premium through QA → Entitlements: choose Premium in the Plan dropdown, then Apply.

Check

  • No offer of any kind on any launch — no full-screen offer, no popup, no pill, no ask at a template link
  • The sidebar shows the plan without an upgrade button or a countdown — on a paid subscription the Basic Plan card is gone entirely

F — The Free plan itself

15. A new account really is on Free, with real limits

Account: fresh.

Check

  • Straight after onboarding the sidebar card reads Basic Plan, with no trial and no countdown
  • Free limits genuinely apply. Use QA → Entitlements → Seed usage near limit, pick a feature and press Set count to limit - 2, then do that thing twice more
  • Crossing the limit shows a message explaining the limit — it does not fail silently and does not show an error page
  • That message closes with the X. There is no "Not now" link any more
  • The offer still works from there — starting on Free does not mean never being asked

16. Settings → Usage tells the truth

Account: a Free account. Seed a few counters with QA → Entitlements → Seed usage near limit.

Steps Open Settings → Usage.

Check

  • The rows read Meetings, Dictation, Voice memos, Mono, History — in that order
  • The numbers match what you have actually used and seeded
  • Renewal dates are in your own timezone. If you can, set the computer to a timezone behind UTC and check just after midnight UTC — a date that is one day out is the bug
  • Something with no limit shows no bar and no renewal date
  • Nothing shows an empty "0 of 0" meter

Reporting

For each case, note pass, fail, or skipped, and for a failure say what you saw instead. Two things always worth adding:

  • Which account and which machine, since so much of this is once-per-account and once-per-machine.
  • Whether anything about money disagreed with anything else — a promise of free days that the payment screen contradicts is the most expensive failure in this whole area.

Engineer checks

These need a developer, a database tool, or a browser console, so they are not part of the QA pass. They exist so the coverage is not silently lost — ask a developer to run them when this area changes.

  • Nothing records a refusal. Closing an offer must send no trial-decision request, and the stored decision must stay unset. There is no user action that records a refusal at all: the decision only ever moves to accepted, and only the payment webhook does that. If a request or a stored refusal ever appears here, a refusal action has been reintroduced and this whole checklist needs revising.
  • The reminder popup on a genuinely empty workspace. The gate is "this workspace has documents", and onboarding always seeds five, so an empty workspace cannot be reached from the UI.
  • The full popup schedule. The week-long gap and the hard stop after the third showing need the stored timestamp to be moved backwards. QA can only confirm it does not reappear the same day.
  • A trial shorter than Stripe's 48-hour minimum. A granted trial with under two days left must not be sold as "$0 today", because payment would be taken immediately. Reaching that state needs a fractional trial length the QA panel cannot set.
  • Re-running an offer on the same account. Clearing the once-per-account and once-per-machine records needs a console and a backend QA route. Until a QA-panel button exists for it, QA uses fresh accounts instead — see Getting a "fresh account".
  • Analytics. That the paywall event fires once per showing, with the right source and offer details, needs Amplitude and event-level inspection.
  • Which mechanism prepared an abandoned signup's sidebar. Case 8 proves the outcome; proving the scheduled sweep did it, rather than the fallback on the next request, needs the backend logs.
  • The charge at the end of the free month. Cut from the QA pass entirely: a tester cannot end a free month, and coordinating with someone who can is slower than the check is worth. Whoever has Stripe access verifies it when billing changes — the subscription should move to active with an invoice raised for the real plan amount, and the app should then show a next-charge date, stay Premium, and stop showing a countdown. The failed-first-charge case (a card that attaches then fails) belongs with it.
  • Ending a free month early, for whoever does that: in the Stripe test dashboard, switch to Test mode, find the customer by their Google address, open the subscription, and use End trial now from its actions menu. Then tell the tester the resulting status and whether an invoice was raised for the real plan amount. If the action is missing you are in Live mode.
  • Repeatable billing-cycle testing. Cases 17 and 18 are one-shot manual pokes. Several cycles, renewals and dunning want Stripe test clocks, and a clock has to be attached when the customer is created, which the code does not currently do — a small staging-only hook, not a change to any trial rule. The slower alternative needing no code: set TRIAL_CLAIM_DAYS=1 on staging and return tomorrow.

Setup notes for whoever runs those: the backend QA routes for plan and trial-decision resets refuse while a live subscription is attached, and a local run needs the Stripe listener sidecar or the payment webhook never records the acceptance. Details in apps/backend/docs/operations/stripe-billing.md and apps/clients/docs/testing/ in the repo.