Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions android/app/src/main/java/com/noop/ui/AppRoot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,10 @@ fun AppRoot(viewModel: AppViewModel = viewModel()) {
}
composable(Destination.TestCentre.route) { TestCentreScreen(viewModel) }
// The "More" page — the iOS More tab's twin: a navigated ScreenScaffold page hosting the
// full grouped destination list (was a pull-up sheet). A row navigates top-level.
// full grouped destination list (was a pull-up sheet). A row pushes its destination so
// Android Back returns to More instead of skipping straight to Today.
composable(Destination.More.route) {
MoreScreen(onNavigate = { nav.navigateTopLevel(it) })
MoreScreen(onNavigate = { nav.navigate(it) })
}
}
}
Expand Down Expand Up @@ -588,7 +589,7 @@ fun AppRoot(viewModel: AppViewModel = viewModel()) {
// ([drawerGroups]) inside a [ScreenScaffold], with the exact section-header + row styling the sheet
// used (uppercase [Overline] group labels, icon + label [NavigationDrawerItem] rows) — now with a
// trailing chevron so each row reads as a navigation push, matching the iOS disclosure rows. Tapping a
// row navigates top-level; there is no sheet to dismiss. The floating bottom bar stays visible because
// row pushes its destination; there is no sheet to dismiss. The floating bottom bar stays visible because
// this is just another NavHost destination under the same Scaffold.

/** The full grouped destination list as a navigated page (the iOS More tab's twin). */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.noop.ui

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeTrue
import org.junit.Test
import java.io.File

/**
* Pins the Android More-page navigation contract. More is a real NavHost destination, so each row must
* push its destination onto the current stack. Treating a row like a bottom-tab switch pops More back to
* Today before opening the destination, which makes Android Back skip More entirely.
*
* This source audit is deliberately paired with the existing navigation wiring: it proves every generated
* [MoreRow] still flows through the shared push callback while the actual bottom bar retains the separate
* top-level state-save/restore policy. It follows the source-locating pattern used by
* [ResolvedSeriesCallSiteAuditTest].
*/
class MoreNavigationContractTest {

/** Locate AppRoot.kt from Gradle, IDE, or repository-root test working directories. */
private fun appRootSource(): File? {
val userDir = File(System.getProperty("user.dir") ?: ".")
return listOf(
File(userDir, "src/main/java/com/noop/ui/AppRoot.kt"),
File(userDir, "app/src/main/java/com/noop/ui/AppRoot.kt"),
File(userDir, "android/app/src/main/java/com/noop/ui/AppRoot.kt"),
).firstOrNull { it.isFile }
}

@Test
fun moreRowsPushWhileBottomTabsRemainTopLevel() {
val sourceFile = appRootSource()
assumeTrue("AppRoot.kt not reachable from ${System.getProperty("user.dir")}", sourceFile != null)
val source = sourceFile!!.readText().replace(Regex("\\s+"), " ")

assertTrue(
"More destinations must be pushed so Back returns to More",
source.contains("MoreScreen(onNavigate = { nav.navigate(it) })"),
)
assertFalse(
"More destinations must not clear the stack through navigateTopLevel",
source.contains("MoreScreen(onNavigate = { nav.navigateTopLevel(it) })"),
)
assertTrue(
"Every generated More row must keep using the shared navigation callback",
source.contains("MoreRow(dest = dest, onClick = { onNavigate(dest.route) })"),
)
assertTrue(
"Bottom-tab selections must retain top-level state save/restore",
source.contains("if (dest.route != currentRoute) nav.navigateTopLevel(dest.route)"),
)
}
}