98 lines
7.5 KiB
Markdown
98 lines
7.5 KiB
Markdown
---
|
|
phase: 2.1
|
|
slug: app-shell-navigation-search-foundation
|
|
status: draft
|
|
nyquist_compliant: false
|
|
wave_0_complete: false
|
|
created: 2026-05-08
|
|
---
|
|
|
|
# Phase 2.1 — Validation Strategy
|
|
|
|
> Per-phase validation contract for feedback sampling during execution.
|
|
> Sourced from `02.1-RESEARCH.md` § Validation Architecture.
|
|
|
|
---
|
|
|
|
## Test Infrastructure
|
|
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| **Framework** | `kotlin.test` (commonTest) — already used in Phase 2 (`AuthSessionTest`, `LoginViewModelTest`) |
|
|
| **Config file** | none — convention plugins handle `recipe.kotlin.multiplatform` |
|
|
| **Quick run command** | `./gradlew :composeApp:commonTest --tests "dev.ulfrx.recipe.ui.screens.shell.*" --tests "dev.ulfrx.recipe.ui.screens.recipes.*Search*" --tests "dev.ulfrx.recipe.navigation.*" --tests "dev.ulfrx.recipe.ui.components.glass.*"` |
|
|
| **Full suite command** | `./gradlew :composeApp:check` |
|
|
| **Estimated runtime** | ~30-90 seconds (commonTest); ~3-6 min (full check incl. iOS sim klib link) |
|
|
|
|
Compose UI Test on KMP iOS is not introduced this phase — feasibility is too low. Visible chrome is verified by a manual smoke runbook (see § Manual-Only Verifications).
|
|
|
|
---
|
|
|
|
## Sampling Rate
|
|
|
|
- **After every task commit:** Run `./gradlew :composeApp:commonTest`
|
|
- **After every plan wave:** Run `./gradlew :composeApp:check`
|
|
- **Before `/gsd-verify-work`:** Full suite green AND manual iOS-simulator smoke runbook executed
|
|
- **Max feedback latency:** ~90 seconds (commonTest)
|
|
|
|
---
|
|
|
|
## Per-Task Verification Map
|
|
|
|
> Task IDs are filled in by the planner. The rows below are the requirement-level verification anchors that any plan task must map onto via its `verify` block.
|
|
|
|
| Anchor | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|
|
|--------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
|
|
| V-01 | TBD | 1 | UI-03 | — | `navigateToTab()` applies `popUpTo(graph.findStartDestination().id) { saveState = true }; launchSingleTop = true; restoreState = true` | unit | `./gradlew :composeApp:commonTest --tests "*NavigationTest*"` | ❌ W0 | ⬜ pending |
|
|
| V-02 | TBD | 1 | UI-04 | — | `GlassSurface` selects Liquid backend on iOS source set at compile time | unit | `./gradlew :composeApp:commonTest --tests "*GlassBackend*"` | ❌ W0 | ⬜ pending |
|
|
| V-03 | TBD | 1 | UI-04 | — | `GlassSurface` debug-toggle flow honors `multiplatform-settings` value via `MapSettings` test impl | unit | `./gradlew :composeApp:commonTest --tests "*GlassBackendOverride*"` | ❌ W0 | ⬜ pending |
|
|
| V-04 | TBD | 1 | UI-09 | — | App.kt `AuthState.Authenticated + currentUser != null` resolves to `AppShell`, not `PostLoginPlaceholderScreen` | unit | `./gradlew :composeApp:commonTest --tests "*AppShellGateTest*"` | ❌ W0 | ⬜ pending |
|
|
| V-05 | TBD | 1 | UI-10 | — | `RecipesSearchViewModel`: `open() → onQueryChange("foo") → close()` clears query and resets `isOpen` | unit | `./gradlew :composeApp:commonTest --tests "*RecipesSearchViewModelTest*"` | ❌ W0 | ⬜ pending |
|
|
| V-06 | TBD | 1 | UI-10 | — | `RecipesSearchViewModel`: `clear()` resets only `query`, keeps `isOpen=true` | unit | (same target) | ❌ W0 | ⬜ pending |
|
|
| V-07 | TBD | 1 | UI-10 | — | `PantrySearchViewModel`: parity with recipes (open/close/clear semantics) | unit | `./gradlew :composeApp:commonTest --tests "*PantrySearchViewModelTest*"` | ❌ W0 | ⬜ pending |
|
|
| V-08 | TBD | 1 | UI-09 / UI-03 | — | Each tab renders its own empty state on first launch without flash | manual smoke (iOS) | n/a | manual | ⬜ pending |
|
|
| V-09 | TBD | 1 | UI-03 | — | Bottom-tab reselect preserves nested back stack | manual smoke (iOS) | n/a | manual | ⬜ pending |
|
|
| V-10 | TBD | 1 | UI-10 | — | Search affordance visible on Recipes + Pantry tabs only (D-06) | manual smoke + screenshot per tab | n/a | manual | ⬜ pending |
|
|
| V-11 | TBD | 1 | UI-04 | — | Liquid dock/menu chrome animates on iOS device path; flat fallback path activates when override is set | manual smoke (iOS) | n/a | manual | ⬜ pending |
|
|
|
|
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
|
|
|
|
---
|
|
|
|
## Wave 0 Requirements
|
|
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/navigation/NavigationTest.kt` — stubs for V-01 (UI-03 navigateToTab semantics; uses `TestNavHostController` if available, else asserts on the option-builder lambda)
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/components/glass/GlassBackendTest.kt` — stubs for V-02 (UI-04 compile-time backend selection)
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/components/glass/GlassBackendOverrideTest.kt` — stubs for V-03 (UI-04 settings-driven debug override)
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/shell/AppShellGateTest.kt` — stubs for V-04 (UI-09 App.kt routing)
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesSearchViewModelTest.kt` — stubs for V-05/V-06 (UI-10)
|
|
- [ ] `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantrySearchViewModelTest.kt` — stubs for V-07 (UI-10 mirror)
|
|
- [ ] iOS-simulator smoke runbook (see § Manual-Only Verifications) committed alongside the phase artifacts so V-08…V-11 have a repeatable check
|
|
- [ ] No new framework install — `kotlin.test` is already wired through `recipe.kotlin.multiplatform` convention plugin
|
|
- [ ] Wave 0 dependency-resolution checks for the three load-bearing assumptions A1/A2/A3 (Liquid iOS klib resolves, Material Icons Outlined available without `material-icons-extended`, nav-compose 2.9.2 K/N back-stack save/restore on iOS)
|
|
|
|
---
|
|
|
|
## Manual-Only Verifications
|
|
|
|
| Behavior | Requirement | Why Manual | Test Instructions |
|
|
|----------|-------------|------------|-------------------|
|
|
| Each tab's empty state renders without flash on first launch | UI-09 | Compose Multiplatform on iOS lacks mature snapshot/UI testing for chrome-level visual verification | iOS sim cold launch → land on Planer (default tab) → confirm intentional empty illustration + copy → no spinner/flash |
|
|
| Tab back-stack preserved across reselection | UI-03 | Real navigation behavior across nested NavHosts is best validated visibly on the simulator | Navigate Przepisy → tap any future stub detail nav → switch to Spiżarnia → switch back to Przepisy → expect previous state restored, not start dest |
|
|
| Search affordance is functional and scoped | UI-10 | UX of opening/closing/clearing must be felt, not just unit-asserted | On Recipes tab: tap search icon → surface opens → type "abc" → confirm query state → tap clear → query empty, surface still open → tap close → surface dismissed. Repeat on Pantry. Confirm Planer/Zakupy do NOT show search affordance. |
|
|
| Liquid dock/menu chrome on iOS device path | UI-04 | Glass aesthetic and performance can only be judged by eye | iOS sim run with default config → confirm Liquid menu/dock renders with the expected glass treatment → toggle debug override via `multiplatform-settings` storage → confirm flat fallback activates |
|
|
| Dock collapse animation on tab change | UI-04 / UI-09 | Animation feel | Tab between all four destinations → confirm dock animation runs smoothly, no jank |
|
|
|
|
---
|
|
|
|
## Validation Sign-Off
|
|
|
|
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
|
|
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
|
|
- [ ] Wave 0 covers all MISSING references (test file stubs above)
|
|
- [ ] No watch-mode flags
|
|
- [ ] Feedback latency < 90s for commonTest
|
|
- [ ] `nyquist_compliant: true` set in frontmatter
|
|
|
|
**Approval:** pending
|