Single source of truth: `com.tt.honeyDue.testing.GalleryScreens` lists every user-reachable screen with its category (DataCarrying / DataFree) and per-platform reachability. Both platforms' test harnesses are CI-gated against it — `GalleryManifestParityTest` on each side fails if the surface list drifts from the manifest. Variant matrix by category: DataCarrying captures 4 PNGs (empty/populated × light/dark), DataFree captures 2 (light/dark only). Empty variants for DataCarrying use `FixtureDataManager.empty(seedLookups = false)` so form screens that only read DM lookups can diff against populated. Detail-screen rendering fixed on both platforms. Root cause: VM `stateIn(Eagerly, initialValue = …)` closures evaluated `_selectedX.value` before screen-side `LaunchedEffect` / `.onAppear` could set the id, leaving populated captures byte-identical to empty. Kotlin: `ContractorViewModel` + `DocumentViewModel` accept `initialSelectedX: Int? = null` so the id is set in the primary constructor before `stateIn` computes its seed. Swift: `ContractorViewModel`, `DocumentViewModelWrapper`, `ResidenceViewModel`, `OnboardingTasksViewModel` gained pre-seed init params. `ContractorDetailView`, `DocumentDetailView`, `ResidenceDetailView`, `OnboardingFirstTaskContent` gained test/preview init overloads that accept the pre-seeded VM. Corresponding view bodies prefer cached success state over loading/error — avoids a spinner flashing over already-visible content during background refreshes (production benefit too). Real production bug fixed along the way: `DataManager.clear()` was missing `_contractorDetail`, `_documentDetail`, `_contractorsByResidence`, `_taskCompletions`, `_notificationPreferences`. On logout these maps leaked across user sessions; in the gallery they leaked the previous surface's populated state into the next surface's empty capture. `ImagePicker.android.kt` guards `rememberCameraPicker` with `LocalInspectionMode` — `FileProvider.getUriForFile` can't resolve the Robolectric test-cache path, so `add_document` / `edit_document` previously failed the entire capture. Honest reclassifications: `complete_task`, `manage_users`, and `task_suggestions` moved to DataFree. Their first-paint visible state is driven by static props or APILayer calls, not by anything on `IDataManager` — populated would be byte-identical to empty without a significant production rewire. The manifest comments call this out. Manifest counts after all moves: 43 screens = 12 DataCarrying + 31 DataFree, 37 on both platforms + 3 Android-only (home, documents, biometric_lock) + 3 iOS-only (documents_warranties, add_task, profile_edit). Test results after full record: Android: 11/11 DataCarrying diff populated vs empty iOS: 12/12 DataCarrying diff populated vs empty Also in this change: - `scripts/build_parity_gallery.py` parses the Kotlin manifest directly, renders rows in product-flow order, shows explicit `[missing — <platform>]` placeholders for expected-but-absent captures and muted `not on <platform>` placeholders for platform-specific screens. Docs regenerated. - `scripts/cleanup_orphan_goldens.sh` safely removes PNGs from prior test configurations (theme-named, compare artifacts, legacy empty/populated pairs for what is now DataFree). Dry-run by default. - `docs/parity-gallery.md` rewritten: canonical-manifest workflow, adding-a-screen guide, variant matrix explained. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
30 KiB
30 KiB
honeyDue parity gallery
43 screens · 102 Android · 174 iOS
Auto-generated by scripts/build_parity_gallery.py — do not hand-edit.
See parity-gallery.md for the workflow guide.
Screens
- login (DataFree)
- register (DataFree)
- forgot_password (DataFree)
- verify_reset_code (DataFree)
- reset_password (DataFree)
- verify_email (DataFree)
- onboarding_welcome (DataFree)
- onboarding_value_props (DataFree)
- onboarding_create_account (DataFree)
- onboarding_verify_email (DataFree)
- onboarding_location (DataFree)
- onboarding_name_residence (DataFree)
- onboarding_home_profile (DataFree)
- onboarding_join_residence (DataFree)
- onboarding_first_task (DataCarrying)
- onboarding_subscription (DataFree)
- home (DataCarrying) — Android-only
- residences (DataCarrying)
- residence_detail (DataCarrying)
- add_residence (DataFree)
- edit_residence (DataFree)
- join_residence (DataFree)
- manage_users (DataFree)
- all_tasks (DataCarrying)
- add_task (DataFree) — iOS-only
- add_task_with_residence (DataFree)
- edit_task (DataFree)
- complete_task (DataFree)
- task_suggestions (DataFree)
- task_templates_browser (DataCarrying)
- contractors (DataCarrying)
- contractor_detail (DataCarrying)
- documents (DataCarrying) — Android-only
- documents_warranties (DataCarrying) — iOS-only
- document_detail (DataCarrying)
- add_document (DataFree)
- edit_document (DataFree)
- profile (DataCarrying)
- profile_edit (DataFree) — iOS-only
- notification_preferences (DataFree)
- theme_selection (DataFree)
- biometric_lock (DataFree) — Android-only
- feature_comparison (DataFree)
login (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
register (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
forgot_password (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
verify_reset_code (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
reset_password (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
verify_email (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_welcome (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_value_props (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_create_account (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_verify_email (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_location (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_name_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_home_profile (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_join_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
onboarding_first_task (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
onboarding_subscription (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
home (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
(not on ios) |
| empty / dark | ![]() |
(not on ios) |
| populated / light | ![]() |
(not on ios) |
| populated / dark | ![]() |
(not on ios) |
residences (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
residence_detail (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
add_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
edit_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
join_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
manage_users (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
[missing — ios] |
| dark | ![]() |
[missing — ios] |
all_tasks (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
add_task (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | (not on android) | ![]() |
| dark | (not on android) | ![]() |
add_task_with_residence (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
edit_task (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
complete_task (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
task_suggestions (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
[missing — ios] |
| dark | ![]() |
[missing — ios] |
task_templates_browser (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
contractors (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
contractor_detail (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
documents (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
(not on ios) |
| empty / dark | ![]() |
(not on ios) |
| populated / light | ![]() |
(not on ios) |
| populated / dark | ![]() |
(not on ios) |
documents_warranties (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | (not on android) | ![]() |
| empty / dark | (not on android) | ![]() |
| populated / light | (not on android) | ![]() |
| populated / dark | (not on android) | ![]() |
document_detail (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
add_document (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
edit_document (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
profile (DataCarrying)
| State / Mode | Android | iOS |
|---|---|---|
| empty / light | ![]() |
![]() |
| empty / dark | ![]() |
![]() |
| populated / light | ![]() |
![]() |
| populated / dark | ![]() |
![]() |
profile_edit (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | (not on android) | ![]() |
| dark | (not on android) | ![]() |
notification_preferences (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
theme_selection (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |
biometric_lock (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
(not on ios) |
| dark | ![]() |
(not on ios) |
feature_comparison (DataFree)
| State / Mode | Android | iOS |
|---|---|---|
| light | ![]() |
![]() |
| dark | ![]() |
![]() |





































































































































































































