19 KiB
19 KiB
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, and email invite removal.
Environment & Setup
- Clean install on test device (API 26+).
- Test device/emulator has network connectivity.
- Check non-network build:
./gradlew :app:compileDebugKotlinpasses. - Verify
RC_API_KEYinlocal.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
MainActivitywithout crash. AppNavigationstarts atAppRoute.ONBOARDINGon 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_PROFILEorLOGINas 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 toHOME.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)
AccountScreenshows "Local profile" and disables "Sign in or create account" and "Export your data" rows. These need real wiring before public release.- Home header text changes based on
partnerNamepresence; 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)
EmailInviteScreenand theEMAIL_INVITEroute have been deleted. Sharing is now handled entirely via the system share sheet inCreateInviteScreen/CreateInviteView. There is no separate email flow or backend email sending.
- Android: "Share" button on
CreateInviteScreenopens system share sheet with invite message. - iOS: "Share" button on
CreateInviteViewopensUIActivityViewControllerwith 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
SettingsScreenpartner card opensRELATIONSHIP_SETTINGSwhen paired.- Leave couple confirmation dialog shown.
- Leave action calls repository and navigates to
CREATE_INVITEon 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
PlaceholderScreenwith correct copy. - Actions navigate to invite flow or home.
- Gap: screen is a placeholder; not a functional partner dashboard yet.
3.3 Moment cue / special dates section
SpecialDatesSectionpreviews render.- Home moment cue card text not placeholder.
- Hardcoded names ("Jessica", "Mark") and dates in
SpecialDatesSectionmust be replaced with real data before public release.
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/nextIdargs. - 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_SIZEquestions. - 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)
- Fields render: date, time, budget, duration.
- Gap: date and time fields show "TODO: Date picker dialog" / "TODO: Time picker dialog"; they are not interactive yet.
- 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) orCREATE_INVITE(unpaired). - Appearance row (palette icon) present and opens
APPEARANCEscreen. - Notifications, Subscription, Privacy & Terms rows open correct screens.
- No "Email Invite" or "Invite by Email" row present anywhere in settings.
- 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 visually greyed out.
- 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/notifChatMessageto 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.4 Privacy (PrivacyScreen)
- External links open in browser.
- No browser available case handled via
ExternalLinks.openUrlToast fallback. - Back navigation works.
- Support URL resolves correctly — now
https://closer.app/support.
9.5 Subscription (SubscriptionScreen)
- Renders placeholder with paywall and settings actions.
- Gap: subscription management is placeholder; needs real UI before release.
9.6 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:
onMessageWrittenis a new function — must runfirebase deploy --only functionsbefore 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
shellBackRoutesand 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.
| # | Area | Issue | Severity | Status |
|---|---|---|---|---|
| 1 | Date builder | Date/time picker TODOs — fields are not interactive | High | Not an issue — DatePickerDialog and TimeInput are already implemented and wired |
| 2 | Special dates | Hardcoded names/dates in SpecialDatesSection |
High | Not an issue — SpecialDatesSection 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 | Open |
| 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 issue — AccountScreen 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 | Partial — strings.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 | Fixed — onAnswerWritten 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 | Fixed — expiresAt TTL field added; firestore.indexes.json configures auto-delete |
| 17 | iOS deploy | onMessageWritten CF not yet deployed — iOS chat notifications not active until firebase deploy --only functions is run |
Medium | Open — deploy required |
Sign-off
| Tester | Date | Build | Result |
|---|---|---|---|