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.
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)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #173. Stacked on #185.
Two assertion gaps, not coverage gaps — which is why they lasted.
AppRootRestorationTestalready drivesAppRootatCompactandExpanded, so JaCoCo is green onuseRail; but it asserts only that the selected tab survives recreation, through a stubcontentcomposable. Nothing queried for a rail or a bar, and nothing composed the real screens.Measured before this file existed:
NavigationRailandNavigationBarbodiesContent's two armsuseRail:!= Compact→== ExpandedA 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.Mediumappears in no test in either source set today.useRailis!= Compact, so Medium takes the rail; narrowing it to== Expandedis 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, notcreateComposeRule. RenderingAppRootwith its default content reachesConverterScreen'sviewModel = viewModel(), which needs aViewModelStoreOwner. It works because both ViewModels are@JvmOverloads constructor(app: Application, …)soAndroidViewModelFactorycan build them, and becauseui-test-manifest'sdebugImplementationentry already puts aComponentActivityin the merged manifest the unit tests build against — whichapp/build.gradle.ktssays 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. InTestTagsand 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.TagTableUniquenessTestcovers the new group.Gate
Full gate green. 564 → 568 JVM tests, 0 failures.
🤖 Generated with Claude Code