Parity gallery: honest populated-state coverage (10/34 surfaces differ)

Fixed & documented, not-just-marketed:
- HomeScreen now derives summary card from LocalDataManager.myResidences
  with VM fallback — populated PNG genuinely differs from empty.
- DocumentsScreen added same LocalDataManager fallback pattern + ambient
  subscription check (bypass SubscriptionHelper's singleton gate).
- ScreenshotTests.setUp seeds the global DataManager singleton from the
  fixture per variant (subscription/user/residences/tasks/docs/contractors/
  lookups). Unblocks screens that bypass LocalDataManager.

Honest coverage after all fixes: 10/34 surface-pairs genuinely differ
(home, profile, residences, contractors, all_tasks, task_templates_browser
in dark mode, etc.). The other 24 remain identical because their VMs
independently track state via APILayer.getXxx() calls that fail in
Robolectric — VM state stays Idle/Error, so gated "populated" branches
never render.

Root architectural fix needed (not landed here): every VM's xxxState
should mirror DataManager.xxx reactively instead of tracking API results
independently. That's a ~20-VM refactor tracked as follow-up in
docs/parity-gallery.md "Known limitations".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-19 09:31:52 -05:00
parent ab0e5c450c
commit f83e89bee3
119 changed files with 103 additions and 26 deletions

View File

@@ -97,12 +97,34 @@ class ScreenshotTests(
fun captureAllVariants() {
Variant.all().forEach { variant ->
val fileName = "${surface.name}_${variant.state}_${variant.mode}.png"
val fixture = variant.dataManager()
// Seed the global DataManager singleton from the fixture. Many
// helpers (SubscriptionHelper, screen ViewModels that read
// DataManager directly, plus the screens' APILayer-triggered
// fallbacks) bypass LocalDataManager and read the singleton. By
// seeding here, all three data paths converge on the fixture
// data so empty/populated tests produce genuinely different
// renders — not just the ones that happen to use LocalDataManager.
val dm = com.tt.honeyDue.data.DataManager
dm.setSubscription(fixture.subscription.value)
dm.setCurrentUser(fixture.currentUser.value)
fixture.myResidences.value?.let { dm.setMyResidences(it) }
dm.setResidences(fixture.residences.value)
fixture.totalSummary.value?.let { dm.setTotalSummary(it) }
fixture.allTasks.value?.let { dm.setAllTasks(it) }
dm.setDocuments(fixture.documents.value)
dm.setContractors(fixture.contractors.value)
dm.setFeatureBenefits(fixture.featureBenefits.value)
dm.setUpgradeTriggers(fixture.upgradeTriggers.value)
dm.setTaskCategories(fixture.taskCategories.value)
dm.setTaskPriorities(fixture.taskPriorities.value)
dm.setTaskFrequencies(fixture.taskFrequencies.value)
captureRoboImage(filePath = "src/androidUnitTest/roborazzi/$fileName") {
HoneyDueTheme(
themeColors = AppThemes.Default,
darkTheme = variant.darkTheme,
) {
CompositionLocalProvider(LocalDataManager provides variant.dataManager()) {
CompositionLocalProvider(LocalDataManager provides fixture) {
Box(Modifier.fillMaxSize()) {
surface.content()
}
@@ -110,6 +132,8 @@ class ScreenshotTests(
}
}
}
// Reset after suite so other tests don't inherit state.
com.tt.honeyDue.data.DataManager.setSubscription(null)
}
companion object {