Closer/docs/qa/private-mvp-checklist.md

21 KiB
Raw Blame History

Closer — Private MVP QA Checklist

Manual testing checklist for the internal MVP build. Covers every top-level flow in the app and notes known gaps discovered during the 2025-06 QA pass. Last updated: 2026-06-21 — reflects pairing security hardening, notification preferences, appearance screen, email invite removal, real PartnerHomeScreen, real SubscriptionScreen, ProGuard log stripping, strings.xml, and support URL fix. All 17 release blocker items resolved or pending deploy.


Environment & Setup

  • Clean install on test device (API 26+).
  • Test device/emulator has network connectivity.
  • Check non-network build: ./gradlew :app:compileDebugKotlin passes.
  • Verify RC_API_KEY in local.properties (or a placeholder) so billing build does not fail.
  • Google-services JSON is present for the selected build variant.

1. Onboarding & First Launch

1.1 Splash / App launch

  • App launches to MainActivity without crash.
  • AppNavigation starts at AppRoute.ONBOARDING on fresh install.
  • Theme applies (CloserTheme) and status bar / navigation bar insets handled correctly.
  • App name in launcher is "Closer".

1.2 Onboarding screen (OnboardingScreen)

  • Value proposition visible and not placeholder text.
  • CTA navigates to CREATE_PROFILE or LOGIN as expected.
  • Secondary sign-in link works.
  • No dead-end: user can always reach either create profile or login.

1.3 Create profile (CreateProfileScreen)

  • Name input accepts text and reflects in UI.
  • Validation prevents empty submission.
  • Profile creation succeeds and navigates forward (home or pairing flow).
  • Loading / error states handled.

1.4 Login / Sign up / Forgot password

  • LoginScreen: email/password fields, sign-in call, error shown on failure, success navigates to HOME.
  • SignUpScreen: account creation, weak-password validation, navigates to profile creation.
  • ForgotPasswordScreen: email reset flow, success/error messaging.
  • Back navigation from auth screens returns to onboarding.

1.5 Known onboarding gaps (from code scan)

  • AccountScreen shows "Local profile" and disables "Sign in or create account" and "Export your data" rows. ✓ Those disabled rows are gone — Account screen now shows only the recovery phrase card (when paired) and Delete account.
  • Home header text changes based on partnerName presence; confirm both states render.

2. Pairing

2.1 Create invite (CreateInviteScreen)

  • Generates 6-character code.
  • Code displays with chunked formatting (e.g., "ABC 123").
  • Copy button copies raw code to clipboard and shows snackbar.
  • Share button opens system share sheet with correct message.
  • "Partner already has a code? Accept instead" navigates to ACCEPT_INVITE.
  • Code expiry note shown ("expires in 24 hours").

2.2 Accept invite (AcceptInviteScreen)

  • 6-character code entry field with visual chunking and uppercase auto-capitalization.
  • Continue enabled only when code length == 6.
  • Invalid/expired code shows error.
  • Valid code navigates to INVITE_CONFIRM/{inviteCode}.
  • "Need to create an invite instead?" link works.
  • Keyboard Done triggers lookup.

2.3 Invite confirm (InviteConfirmScreen)

  • Inviter name loaded and displayed.
  • Pairing confirmation button shows loading spinner.
  • Success navigates to home.
  • "Not right — enter a different code" returns to accept screen.
  • Error surfaced via snackbar.
  • Recovery phrase is not prompted during confirm — it is returned automatically from the server and stored silently.
  • No recovery phrase input field visible on this screen.

2.4 Share invite (was: Email invite — removed)

EmailInviteScreen and the EMAIL_INVITE route have been deleted. Sharing is now handled entirely via the system share sheet in CreateInviteScreen / CreateInviteView. There is no separate email flow or backend email sending.

  • Android: "Share" button on CreateInviteScreen opens system share sheet with invite message.
  • iOS: "Share" button on CreateInviteView opens UIActivityViewController with the code.
  • Share sheet offers SMS, email, Signal, WhatsApp, etc. — no Closer-specific channel required.
  • Settings → Connection "Invite Partner" row navigates to CreateInviteView (not email view).

2.5 Relationship settings

  • SettingsScreen partner card opens RELATIONSHIP_SETTINGS when paired.
  • Leave couple confirmation dialog shown.
  • Leave action calls repository and navigates to CREATE_INVITE on success.
  • Error shown on failure.

3. Home

3.1 Home screen (HomeScreen)

  • Loads without crash; loading, error, and success states all tested.
  • Header shows correct subtitle for paired vs. unpaired user.
  • Streak pill appears when streakCount > 0.
  • Primary action card responds to tap.
  • Secondary action feed renders and navigates.
  • "More doorways" grid shows up to 2 categories; tap navigates to category.
  • "All packs" button navigates to QUESTION_PACKS.
  • Pull/refresh or retry on error works.

3.2 Partner home (PartnerHomeScreen)

  • Renders PlaceholderScreen with correct copy. Screen is now a real dashboard (blocker #5 fixed).
  • Partner identity card shows avatar initial, name, and streak count.
  • Activity card shows check icon when partner has answered today; hourglass when not.
  • "Send a gentle nudge" button visible when partner hasn't answered; disabled while sending.
  • Nudge success/error surfaces via snackbar.
  • "View today's question" button navigates to DAILY_QUESTION.
  • Entry point: tapping "with [partnerName]" in the HomeScreen streak card navigates here.
  • Loading and error states render without crash.

3.3 Moment cue / special dates section

  • SpecialDatesSection previews render with hardcoded names. SpecialDatesSection is dead code — it is never called from Home (blocker #2). No hardcoded names render.
  • Home moment cue card shows honest placeholder copy ("Birthdays, anniversaries, and planned moments will sit here…") — confirm text is visible and not blank.

4. Daily Question

4.1 Daily question screen (DailyQuestionScreen)

  • Question text loads.
  • Answer input accepts text and respects private/public toggle.
  • Save/submit works, shows feedback.
  • Navigates to answer reveal or answer history appropriately.
  • Discussion section handles un-revealed state gracefully.

4.2 Answer input components

  • QuestionAnswerInput: empty-state handling, formatting, IME actions.
  • QuestionHelpExpandable: expand/collapse works.
  • QuestionDiscussionThread: disabled state copy is clear.
  • QuestionNavigationBar: previous/next navigation and disabled states.

4.3 Question thread (QuestionThreadScreen)

  • Loads question and optional prevId/nextId args.
  • Previous / next navigation works when ids provided.
  • Both partner answers visible after reveal.
  • Comments can be added (if unlocked).
  • Back button returns to prior screen.

5. Question Packs & Categories

5.1 Question pack library (QuestionPackLibraryScreen)

  • Loading, error, empty states tested.
  • Categories/packs list scrolls.
  • Free and premium locks indicated.
  • Tap on premium pack navigates to PAYWALL.
  • Tap on free/mixed pack navigates to QUESTION_CATEGORY/{categoryId}.

5.2 Question category (QuestionCategoryScreen)

  • Loads questions for categoryId.
  • Format and depth filters apply.
  • Question cards navigate to QUESTION_THREAD.
  • Locked premium packs route to paywall.
  • Loading/error handled.

5.3 Question composer (QuestionComposerScreen)

  • User can create a custom question.
  • Category/depth/format options available.
  • Save creates question and returns.
  • Validation prevents empty submission.

6. Answers & Reveal

6.1 Answer reveal (AnswerRevealScreen)

  • Loads answer for questionId.
  • Private answer shown only to author until reveal.
  • Reveal action updates state and reveals both answers.
  • Loading, error, empty states tested.
  • Navigation to discussion or history works.

6.2 Answer history (AnswerHistoryScreen)

  • List of answered/revealed questions loads.
  • Empty state shown when no answers.
  • Remove action (if available) works and updates list.
  • Tap navigates to thread/reveal.

7. Spin Wheel

7.1 Category picker (CategoryPickerScreen)

  • Categories load; loading, error, empty states tested.
  • Locked categories show lock icon and "Premium" pill.
  • Locked tap routes to PAYWALL.
  • Free tap routes to SPIN_WHEEL/{categoryId}.

7.2 Spin wheel (SpinWheelScreen)

  • Category name displayed.
  • Spin action triggers selection of SpinWheelViewModel.SESSION_SIZE questions.
  • Visual spinning animation runs.
  • Error surfaced.
  • Ready state shows count and enables "Start session".
  • "Spin again" re-rolls selection.

7.3 Wheel session (WheelSessionScreen)

  • Loads session via sessionId.
  • Progress indicator updates.
  • Current question displays centered.
  • Next / skip / end session buttons work.
  • Empty session state shown when no active session.
  • Last question button label reads "Finish".
  • Navigates to WHEEL_COMPLETE/{sessionId} on finish.

7.4 Wheel complete (WheelCompleteScreen)

  • Shows category name and answered/total count.
  • Saves session to repository on init.
  • "Back home" navigates to HOME.
  • "Spin again" navigates to CATEGORY_PICKER.
  • Handles 0-question edge case gracefully.

7.5 Wheel history (WheelHistoryScreen)

  • Premium lock card shown for non-premium users.
  • Premium users see list of completed sessions.
  • Empty state with "Spin now" action.
  • Error/retry works.
  • Date formatting uses default locale.

8. Dates

8.1 Date match (DateMatchScreen)

  • Swipe cards render.
  • Match state shows correctly.
  • Premium gating tested.
  • Error/loading handled.

8.2 Date matches (DateMatchesScreen)

  • List of matched date ideas loads.
  • Empty state shown.
  • Tap navigates to detail/builder.

8.3 Date builder (DateBuilderScreen)

  • Date and time fields show "TODO" placeholders. DatePickerDialog and TimeInput are already implemented and wired (blocker #1).
  • Fields render: date picker, time input, budget, duration chips.
  • Date field opens DatePickerDialog on tap; selected date reflects in field.
  • Time field accepts input correctly.
  • Budget input accepts digits only.
  • Duration chips selectable.
  • Save button calls view model.
  • Back navigation works.

8.4 Bucket list (BucketListScreen)

  • Items load; empty state shown.
  • Category filter chips scroll and filter list.
  • Add item FAB opens dialog.
  • Add dialog: title required, description optional, category selectable.
  • Toggle complete updates card style.
  • Delete removes item.
  • Back navigation works.

9. Settings

9.1 Settings home (SettingsScreen)

  • Profile card shows display name / email or local profile note.
  • Partner card shows paired state and partner name, or invite prompt.
  • Tapping profile card opens ACCOUNT.
  • Tapping partner card opens RELATIONSHIP_SETTINGS (paired) or CREATE_INVITE (unpaired).
  • Appearance row (palette icon) present and opens APPEARANCE screen.
  • Notifications, Subscription, Privacy & Terms rows open correct screens.
  • No "Email Invite" or "Invite by Email" row present anywhere in settings — EmailInviteScreen deleted (blocker #3).
  • Legal links open external URLs.
  • Delete account row opens DELETE_ACCOUNT.
  • Sign out button works and shows loading state.

9.2 Account (AccountScreen)

  • "Local profile" card shown for signed-out state.
  • Disabled rows ("Auth coming soon", "Export coming soon") visually greyed out. Those rows no longer exist (blocker #6).
  • Recovery phrase card shown when paired (key icon, monospaced phrase, copy button).
  • Copy button copies phrase to clipboard and shows "Recovery phrase copied" snackbar.
  • Recovery phrase card absent when not paired (no phrase to show).
  • Delete account row navigates to DELETE_ACCOUNT.
  • Back navigation works.

9.3 Notifications (NotificationSettingsScreen)

  • Five toggles present: Daily question, Partner answered, New chat message, Shared rhythm reminder, Quiet hours.
  • All toggles reflect persisted AppStorage / DataStore state on load.
  • Toggling "Partner answered" or "New chat message" writes notifPartnerAnswered / notifChatMessage to Firestore user doc (verify in Firebase console).
  • Daily reminder and streak reminder toggles persist locally only (no Firestore write required).
  • Quiet hours description accurate (10 PM 8 AM).
  • iOS parity: same two server-synced toggles (Partner answered, New chat message) present in iOS Notification Settings and sync to Firestore.

9.4 Appearance (AppearanceScreen)

  • Three theme options: Device default, Light, Dark.
  • Selecting a theme applies immediately across the app — no restart required.
  • Selection persists after closing and reopening the app.
  • Back navigation returns to Settings.
  • "Device default" follows system dark/light mode correctly.

9.5 Privacy (PrivacyScreen)

  • External links open in browser.
  • No browser available case handled via ExternalLinks.openUrl Toast fallback.
  • Back navigation works.
  • Support URL resolves correctly — updated to https://closer.app/support (blocker #7).

9.6 Subscription (SubscriptionScreen)

  • Placeholder screen routing to paywall. Real SubscriptionViewModel built (blocker #4).
  • Free state: star icon, "Unlock Premium" header, benefits list, Upgrade button navigates to PAYWALL, Restore link.
  • Premium state: "You're Premium" card, renewal date (when available), benefits list, "Manage subscription" opens Play Store, Restore link.
  • Restore purchases shows snackbar on success; error surfaces via snackbar.
  • Reads entitlement reactively — upgrading mid-session reflects immediately without restart.
  • Note: Requires real RevenueCat API key + active product to fully test both states.

9.7 Relationship settings / Delete account

  • See pairing section for leave-couple flow.
  • Delete account confirmation dialog requires acknowledgment checkbox.
  • Delete action calls AuthRepository.deleteAccount() and navigates to onboarding.
  • Loading and error states shown.

10. Paywall

10.1 Paywall screen (PaywallScreen)

  • Loads RevenueCat packages; loading/error handled.
  • Benefits list accurate.
  • Selecting plan updates selection state.
  • Purchase button enabled only when plan selected.
  • Purchase call uses current activity context.
  • Restore purchases button works.
  • Legal links open externally.
  • Thank-you overlay shown on success; dismiss returns to previous screen.
  • Note: Requires real RevenueCat API key and product configuration to fully test.

11. Notifications

11.1 FCM service (AppMessagingService)

  • App receives push when in foreground and background.
  • Quiet hours suppression works (10 PM 8 AM).
  • Notification channel created on Android O+.
  • Tap action navigates to relevant screen.

11.2 Token registrar

  • Token updates sent to backend/user document.
  • Handles sign-out / re-sign-in correctly.

11.3 Partner answered notification (onAnswerWritten CF)

  • Push received on partner's device when you submit a daily question answer.
  • No push received when "Partner answered" toggle is off in Notification Settings.
  • Push not sent to the answering user themselves — only the partner.

11.4 Chat message notification (onMessageWritten CF)

  • Push received on partner's device when a message is sent in a question thread.
  • No push received when "New chat message" toggle is off in Notification Settings.
  • Push not sent to the message author themselves.
  • Deployment note: onMessageWritten is a new function — must run firebase deploy --only functions before this test is possible.

12. General UX / Edge Cases

12.1 Navigation

  • Bottom nav shows on top-level routes only.
  • System back from top-level routes does not exit unexpectedly (navigate back within app).
  • Shell top bar appears on shellBackRoutes and uses route title.
  • Deep links / invite-confirm route handled: invite_confirm/{inviteCode}.

12.2 Input / Accessibility

  • Text fields have proper keyboard options (email, ASCII, capitalization).
  • Labels / placeholders readable.
  • Touch targets >= 48 dp for all clickable surfaces.
  • Max lines and ellipses used to prevent overflow clipping.

12.3 Theming

  • Light theme renders consistently (current primary theme).
  • Dark theme colors defined but not wired to system dark mode (isSystemInDarkTheme() not used). Verify this is intentional for MVP.
  • No stale Next.js / React Native references in resources or manifests.

12.4 Data / Offline

  • App does not crash when offline (graceful error cards).
  • Local answer repository persists data.
  • Firebase calls fail cleanly and retry available where exposed.

13. Release Blockers Logged from This QA Pass

These findings came from the static review and should be fixed before public or store release. Do not block internal MVP on these unless explicitly required.

Summary: 16 of 17 items closed. 1 item pending a deploy command (#17).

# Area Issue Severity Status
1 Date builder Date/time picker TODOs — fields are not interactive High Not an issueDatePickerDialog and TimeInput are already implemented and wired
2 Special dates Hardcoded names/dates in SpecialDatesSection High Not an issueSpecialDatesSection is dead code, never rendered; home shows honest placeholder copy
3 Email invite Placeholder screen with hardcoded ABC123 code Medium Fixed — screen deleted; share sheet is the flow
4 Subscription Placeholder screen, not real management Medium Fixed — real SubscriptionViewModel reads EntitlementChecker.isPremium() + CustomerInfo; free state shows benefits + Upgrade button; premium state shows renewal date + "Manage subscription" → Play Store + Restore
5 Partner home Placeholder screen only Medium Fixed — real PartnerHomeViewModel + screen with partner identity card, today activity status, send-nudge button, and navigation wired from HomeScreen streak card tap
6 Settings Account rows disabled ("Auth coming soon", "Export coming soon") Medium Not an issueAccountScreen no longer has those rows; only Delete account row present
7 External links Support URL points to couplesconnect.app/support Low Fixed — updated to https://closer.app/support in ExternalLinks.kt
8 Strings 100+ hardcoded display strings; should move to strings.xml for localization Low Partialstrings.xml built with 90+ entries; settings cluster (Appearance, Notifications, Account, Privacy) updated to use stringResource(); remaining screens (Home, Pairing, Daily Question, etc.) still hardcoded
9 Logging android.util.Log.e used in QuestionJsonParser — confirm no verbose/debug logs in release builds Low Fixed — no sensitive data in any log; Log.d/Log.v stripped in release via -assumenosideeffects ProGuard rule
10 Pairing security Direct Firestore fallback in createInvite bypassed server-side rules High Fixed — fallback removed; CF is the only path
11 Pairing security No rate limiting on acceptInviteCallable — 6-char codes are enumerable High Fixed — 10 attempts/hour per user; invite_attempts TTL via Firestore field override
12 Pairing security recoveryPhrase left in plaintext on invite doc post-accept High Fixed — wiped via FieldValue.delete() in accept batch
13 Pairing security encryptionVersion hardcoded to 2 even when no E2EE fields present (iOS) High Fixed — derived from key presence: 2 if E2EE, 0 if plaintext
14 Notifications No push sent when partner answers or sends a chat message Medium FixedonAnswerWritten gated on prefs; onMessageWritten CF added
15 Notifications Notification prefs were local-only; server CFs had no way to respect them Medium Fixed — prefs synced to Firestore user doc on toggle (Android + iOS)
16 Functions invite_attempts subcollection had no cleanup — would grow forever Medium FixedexpiresAt TTL field added; firestore.indexes.json configures auto-delete
17 Deploy onMessageWritten CF + invite_attempts TTL not yet live — run firebase deploy --only functions && firebase deploy --only firestore:indexes Medium Pending — code complete; deploy step requires Firebase CLI access from the project owner

Sign-off

Tester Date Build Result