B1 (#173): tell the rail from the bottom bar, and the Convert tab from the Join tab #186

Merged
JMR-dev merged 3 commits from test/adaptive-shell-wiring into main 2026-09-02 04:13:01 +00:00
JMR-dev commented 2026-09-02 02:54:52 +00:00 (Migrated from github.com)

Closes #173. Stacked on #185.

Two assertion gaps, not coverage gaps — which is why they lasted. AppRootRestorationTest already drives AppRoot at Compact and Expanded, so JaCoCo is green on useRail; but it asserts only that the selected tab survives recreation, through a stub content composable. Nothing queried for a rail or a bar, and nothing composed the real screens.

Measured before this file existed:

transposition old suite now
swap the NavigationRail and NavigationBar bodies green red
swap Content's two arms green red
useRail: != Compact → == Expanded green red

A tablet showing phone chrome, or the Convert tab opening the Join screen, and 546 tests with nothing to say about either. AppRoot's own KDoc is why that matters more than it looks: from targetSdk 37 the app is resized and rotated whether or not it is ready, so the width class is not a preference.

WindowWidthSizeClass.Medium appears in no test in either source set today. useRail is != Compact, so Medium takes the rail; narrowing it to == Expanded is one character and breaks every tablet and unfolded foldable. That third mutation is caught only by the Medium test — the Compact and Expanded ones both survive it.

Two things this needed

createAndroidComposeRule, not createComposeRule. Rendering AppRoot with its default content reaches ConverterScreen's viewModel = viewModel(), which needs a ViewModelStoreOwner. It works because both ViewModels are @JvmOverloads constructor(app: Application, …) so AndroidViewModelFactory can build them, and because ui-test-manifest's debugImplementation entry already puts a ComponentActivity in the merged manifest the unit tests build against — which app/build.gradle.kts says in terms. Checked with a throwaway spike before the ticket was filed, not discovered here.

Two tags, applied inside main — the only production change. TestTags.Shell, set on the rail and the bar. There is no other way to tell them apart: both render the same two destinations with the same labels and the same selection state, so any assertion writable without them is satisfied by either layout. In TestTags and applied by the shell rather than handed down by the test, for the reason that file's KDoc gives — a tag the test supplies proves only that the test set it. TagTableUniquenessTest covers the new group.

Gate

Full gate green. 564 → 568 JVM tests, 0 failures.

🤖 Generated with Claude Code

Closes #173. Stacked on #185. Two **assertion** gaps, not coverage gaps — which is why they lasted. `AppRootRestorationTest` already drives `AppRoot` at `Compact` and `Expanded`, so JaCoCo is green on `useRail`; but it asserts only that the *selected tab* survives recreation, through a stub `content` composable. Nothing queried for a rail or a bar, and nothing composed the real screens. Measured before this file existed: | transposition | old suite | now | |---|---|---| | swap the `NavigationRail` and `NavigationBar` bodies | green | **red** | | swap `Content`'s two arms | green | **red** | | `useRail`: `!= Compact` → `== Expanded` | green | **red** | A tablet showing phone chrome, or the Convert tab opening the Join screen, and 546 tests with nothing to say about either. `AppRoot`'s own KDoc is why that matters more than it looks: from targetSdk 37 the app is resized and rotated whether or not it is ready, so the width class is not a preference. **`WindowWidthSizeClass.Medium` appears in no test in either source set today.** `useRail` is `!= Compact`, so Medium takes the rail; narrowing it to `== Expanded` is one character and breaks every tablet and unfolded foldable. That third mutation is caught **only** by the Medium test — the Compact and Expanded ones both survive it. ### Two things this needed **`createAndroidComposeRule`, not `createComposeRule`.** Rendering `AppRoot` with its default content reaches `ConverterScreen`'s `viewModel = viewModel()`, which needs a `ViewModelStoreOwner`. It works because both ViewModels are `@JvmOverloads constructor(app: Application, …)` so `AndroidViewModelFactory` can build them, and because `ui-test-manifest`'s `debugImplementation` entry already puts a `ComponentActivity` in the merged manifest the unit tests build against — which `app/build.gradle.kts` says in terms. Checked with a throwaway spike before the ticket was filed, not discovered here. **Two tags, applied inside `main`** — the only production change. `TestTags.Shell`, set on the rail and the bar. There is no other way to tell them apart: both render the same two destinations with the same labels and the same selection state, so any assertion writable without them is satisfied by either layout. In `TestTags` and applied by the shell rather than handed down by the test, for the reason that file's KDoc gives — *a tag the test supplies proves only that the test set it*. `TagTableUniquenessTest` covers the new group. ### Gate Full gate green. 564 → 568 JVM tests, 0 failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.