feat(nav): root swipe-back on chat with double-back exit and subpage hierarchy (#1001)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
f04d878358
commit
2332bdd5d5
7 files changed
+139
-99
No files matched your search
@@ -1,5 +1,7 @@
|
|||||||
package com.m57.hermescontrol
|
package com.m57.hermescontrol
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -31,10 +33,13 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -191,7 +196,25 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
|
|
||||||
val openDrawer: () -> Unit = { scope.launch { drawerState.open() } }
|
val openDrawer: () -> Unit = { scope.launch { drawerState.open() } }
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
var lastBackPressTime by remember { mutableLongStateOf(0L) }
|
||||||
|
val backToastText = stringResource(R.string.press_back_again_to_exit)
|
||||||
|
|
||||||
|
// Double-back to exit on root ChatScreen
|
||||||
BackHandler(enabled = currentScreen == ChatScreen && backStack.size == 1) {
|
BackHandler(enabled = currentScreen == ChatScreen && backStack.size == 1) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - lastBackPressTime < 2000L) {
|
||||||
|
(context as? Activity)?.finish()
|
||||||
|
} else {
|
||||||
|
lastBackPressTime = now
|
||||||
|
Toast.makeText(context, backToastText, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety fallback: if a non-chat screen somehow sits alone on stack, swipe back returns to ChatScreen
|
||||||
|
BackHandler(
|
||||||
|
enabled = currentScreen != ChatScreen && currentScreen != LandingScreen && backStack.size == 1,
|
||||||
|
) {
|
||||||
NavigationController.goBack()
|
NavigationController.goBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,30 +31,37 @@ object NavigationController {
|
|||||||
|
|
||||||
private var nextChatNavigationRequestId = 0L
|
private var nextChatNavigationRequestId = 0L
|
||||||
|
|
||||||
// Top-level primary screens (Chat, Skills, Cron, System, Settings)
|
/**
|
||||||
private val primaryScreens: MutableSet<NavKey> =
|
* Top-level primary screens (all drawer-accessible screens).
|
||||||
mutableSetOf(
|
* Navigating to any of these clears the stack to `[ChatScreen, key]` (or `[ChatScreen]` for Chat),
|
||||||
ChatScreen,
|
* ensuring swiping back returns to ChatScreen.
|
||||||
SkillsScreen,
|
*/
|
||||||
CronJobsScreen,
|
fun isPrimaryScreen(key: NavKey): Boolean = key == ChatScreen || ScreenRegistry.ALL_SCREENS.any { it.key == key }
|
||||||
SystemScreen,
|
|
||||||
SettingsScreen,
|
|
||||||
)
|
|
||||||
|
|
||||||
/** Returns whether the given key is a primary top-level screen. */
|
|
||||||
fun isPrimaryScreen(key: NavKey): Boolean = key in primaryScreens
|
|
||||||
|
|
||||||
fun navigateTo(key: NavKey) {
|
fun navigateTo(key: NavKey) {
|
||||||
val stack = backStack ?: return
|
val stack = backStack ?: return
|
||||||
if (stack.lastOrNull() == key) return
|
if (stack.lastOrNull() == key) return
|
||||||
|
|
||||||
|
if (key == ChatScreen) {
|
||||||
|
stack.clear()
|
||||||
|
stack.add(ChatScreen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == LandingScreen) {
|
||||||
|
stack.clear()
|
||||||
|
stack.add(LandingScreen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isPrimaryScreen(key)) {
|
if (isPrimaryScreen(key)) {
|
||||||
stack.clear()
|
stack.clear()
|
||||||
|
stack.add(ChatScreen)
|
||||||
|
stack.add(key)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Drawer dismissal is handled by DrawerGestureController (issue #619):
|
|
||||||
// when a non-gesture sub-page composes, its HermesScaffold reconciles
|
// Subscreen / drill-down navigation: append to current stack
|
||||||
// drawerGesturesEnabled=false and the controller closes the drawer
|
|
||||||
// itself via SideEffect. No synchronous closeDrawer callback here.
|
|
||||||
stack.add(key)
|
stack.add(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,15 +105,13 @@ object NavigationController {
|
|||||||
*/
|
*/
|
||||||
fun goBack(fallback: NavKey = ChatScreen) {
|
fun goBack(fallback: NavKey = ChatScreen) {
|
||||||
val stack = backStack ?: return
|
val stack = backStack ?: return
|
||||||
if (stack.lastOrNull() == ChatScreen) {
|
|
||||||
resetTo(HistoryScreen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (stack.size > 1) {
|
if (stack.size > 1) {
|
||||||
stack.removeLastOrNull()
|
stack.removeLastOrNull()
|
||||||
} else if (stack.size == 1) {
|
} else if (stack.size == 1) {
|
||||||
|
if (stack.lastOrNull() != fallback && stack.lastOrNull() != LandingScreen) {
|
||||||
stack.clear()
|
stack.clear()
|
||||||
stack.add(fallback)
|
stack.add(fallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
<string name="billing_topup">충전</string>
|
<string name="billing_topup">충전</string>
|
||||||
|
|
||||||
<!-- Navigation & Drawer -->
|
<!-- Navigation & Drawer -->
|
||||||
|
<string name="press_back_again_to_exit">종료하려면 뒤로가기를 한 번 더 누르세요</string>
|
||||||
<string name="nav_drawer_title">헤르메스</string>
|
<string name="nav_drawer_title">헤르메스</string>
|
||||||
<string name="nav_drawer_subtitle">AI 에이전트 제어</string>
|
<string name="nav_drawer_subtitle">AI 에이전트 제어</string>
|
||||||
<string name="nav_drawer_section_converse">대화</string>
|
<string name="nav_drawer_section_converse">대화</string>
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
<string name="billing_credits_remaining">剩余额度:%1$s</string>
|
<string name="billing_credits_remaining">剩余额度:%1$s</string>
|
||||||
<string name="billing_plan">套餐</string>
|
<string name="billing_plan">套餐</string>
|
||||||
<string name="billing_topup">充值</string>
|
<string name="billing_topup">充值</string>
|
||||||
|
<string name="press_back_again_to_exit">再次返回以退出应用</string>
|
||||||
<string name="nav_drawer_title">Hermes</string>
|
<string name="nav_drawer_title">Hermes</string>
|
||||||
<string name="nav_drawer_subtitle">AI Agent 控制台</string>
|
<string name="nav_drawer_subtitle">AI Agent 控制台</string>
|
||||||
<string name="nav_drawer_section_converse">对话</string>
|
<string name="nav_drawer_section_converse">对话</string>
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
<string name="billing_topup">Top-up</string>
|
<string name="billing_topup">Top-up</string>
|
||||||
|
|
||||||
<!-- Navigation & Drawer -->
|
<!-- Navigation & Drawer -->
|
||||||
|
<string name="press_back_again_to_exit">Press back again to exit</string>
|
||||||
<string name="nav_drawer_title">Hermes</string>
|
<string name="nav_drawer_title">Hermes</string>
|
||||||
<string name="nav_drawer_subtitle">AI Agent Control</string>
|
<string name="nav_drawer_subtitle">AI Agent Control</string>
|
||||||
<string name="nav_drawer_section_converse">Converse</string>
|
<string name="nav_drawer_section_converse">Converse</string>
|
||||||
|
|||||||
@@ -718,17 +718,19 @@ class E2eIntegrationTest {
|
|||||||
|
|
||||||
NavigationController.navigateTo(SkillsScreen)
|
NavigationController.navigateTo(SkillsScreen)
|
||||||
assertEquals(SkillsScreen, backStack.lastOrNull())
|
assertEquals(SkillsScreen, backStack.lastOrNull())
|
||||||
assertEquals(1, backStack.size)
|
assertEquals(2, backStack.size)
|
||||||
|
assertEquals(ChatScreen, backStack[0])
|
||||||
|
|
||||||
NavigationController.navigateTo(CronJobsScreen)
|
NavigationController.navigateTo(CronJobsScreen)
|
||||||
assertEquals(CronJobsScreen, backStack.lastOrNull())
|
assertEquals(CronJobsScreen, backStack.lastOrNull())
|
||||||
assertEquals(1, backStack.size)
|
assertEquals(2, backStack.size)
|
||||||
|
assertEquals(ChatScreen, backStack[0])
|
||||||
|
|
||||||
// Non-clearing drawer screen adds to stack
|
// Drawer screen navigation always roots on ChatScreen
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
NavigationController.navigateTo(ProfilesScreen)
|
||||||
assertEquals(ProfilesScreen, backStack.lastOrNull())
|
assertEquals(ProfilesScreen, backStack.lastOrNull())
|
||||||
assertEquals(2, backStack.size)
|
assertEquals(2, backStack.size)
|
||||||
assertEquals(CronJobsScreen, backStack[0])
|
assertEquals(ChatScreen, backStack[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Tier 2: Boundary & Corner Cases (>=5 per feature) ────────────────
|
// ── Tier 2: Boundary & Corner Cases (>=5 per feature) ────────────────
|
||||||
|
|||||||
@@ -12,15 +12,8 @@ import org.junit.Test
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for [NavigationController] — the central navigation guard that
|
* Unit tests for [NavigationController] — the central navigation guard that
|
||||||
* prevents duplicate screen entries on the back stack.
|
* prevents duplicate screen entries on the back stack and enforces
|
||||||
*
|
* swipe-back-to-chat hierarchy.
|
||||||
* Issue #291 (Critical Test Coverage): Verifies the deduplication logic at
|
|
||||||
* line 45 (`if (stack.lastOrNull() == key) return`) and the primary-screen
|
|
||||||
* stack-clearing behavior at line 47-49.
|
|
||||||
*
|
|
||||||
* All tests are pure Kotlin with no Android dependencies — they only exercise
|
|
||||||
* [NavigationController]'s companion object methods against a real
|
|
||||||
* [NavBackStack] instance.
|
|
||||||
*/
|
*/
|
||||||
class NavigationControllerTest {
|
class NavigationControllerTest {
|
||||||
@Before
|
@Before
|
||||||
@@ -47,7 +40,7 @@ class NavigationControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `navigateTo on same primary screen is a no-op`() {
|
fun `navigateTo on same primary screen is a no-op`() {
|
||||||
val backStack = NavBackStack<NavKey>(SkillsScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, SkillsScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
val sizeBefore = backStack.size
|
val sizeBefore = backStack.size
|
||||||
|
|
||||||
@@ -58,69 +51,60 @@ class NavigationControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `navigateTo on same non-primary screen is a no-op`() {
|
fun `navigateTo on same subscreen is a no-op`() {
|
||||||
val backStack = NavBackStack<NavKey>(ProfilesScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, SettingsScreen, SettingsAppearance)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
// ProfilesScreen is NOT in the default primaryScreens so it stays on the stack
|
NavigationController.navigateTo(SettingsAppearance)
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
|
||||||
|
|
||||||
assertEquals(1, backStack.size)
|
assertEquals(3, backStack.size)
|
||||||
|
assertEquals(SettingsAppearance, backStack.lastOrNull())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Primary-screen behaviour: resets stack to [ChatScreen, Target] ───────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `navigateTo on a different primary screen resets stack with ChatScreen as root`() {
|
||||||
|
val backStack = NavBackStack<NavKey>(ChatScreen)
|
||||||
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
|
NavigationController.navigateTo(SkillsScreen)
|
||||||
|
assertEquals(2, backStack.size)
|
||||||
|
assertEquals(ChatScreen, backStack.firstOrNull())
|
||||||
|
assertEquals(SkillsScreen, backStack.lastOrNull())
|
||||||
|
|
||||||
|
// Now navigate to another primary screen (e.g. ProfilesScreen)
|
||||||
|
NavigationController.navigateTo(ProfilesScreen)
|
||||||
|
assertEquals("primary screen navigation should reset stack to [ChatScreen, target]", 2, backStack.size)
|
||||||
|
assertEquals(ChatScreen, backStack.firstOrNull())
|
||||||
assertEquals(ProfilesScreen, backStack.lastOrNull())
|
assertEquals(ProfilesScreen, backStack.lastOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Primary-screen behaviour: stack clearing ──────────────────────────
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `navigateTo on a different primary screen clears the stack`() {
|
fun `navigateTo ChatScreen clears stack to single ChatScreen root`() {
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, ProfilesScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
// Navigate to a non-primary screen first (should add to stack)
|
|
||||||
NavigationController.navigateTo(LogsScreen)
|
|
||||||
assertEquals(2, backStack.size)
|
|
||||||
|
|
||||||
// Now navigate to a different primary screen — must clear
|
|
||||||
NavigationController.navigateTo(SkillsScreen)
|
|
||||||
|
|
||||||
assertEquals("primary screen navigation should clear the stack", 1, backStack.size)
|
|
||||||
assertEquals(SkillsScreen, backStack.lastOrNull())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `navigateTo on the current primary screen clears the stack`() {
|
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
|
||||||
NavigationController.backStack = backStack
|
|
||||||
|
|
||||||
// Add a non-primary screen
|
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
|
||||||
assertEquals(2, backStack.size)
|
|
||||||
|
|
||||||
// Navigate to ChatScreen — it's a primary screen, so the stack gets
|
|
||||||
// cleared before adding it. The dedup guard only fires when the key
|
|
||||||
// is already the LAST item (ChatScreen is at index 0, ProfilesScreen
|
|
||||||
// is last), not when it's merely present somewhere in the stack.
|
|
||||||
NavigationController.navigateTo(ChatScreen)
|
NavigationController.navigateTo(ChatScreen)
|
||||||
|
|
||||||
// Primary screen navigation clears the stack
|
assertEquals("chat navigation should result in single root entry", 1, backStack.size)
|
||||||
assertEquals("primary navigation clears stack", 1, backStack.size)
|
|
||||||
assertEquals(ChatScreen, backStack.lastOrNull())
|
assertEquals(ChatScreen, backStack.lastOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Non-primary screen behaviour: stack appending ─────────────────────
|
// ── Subscreen behaviour: appends to stack ───────────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `navigateTo on a non-primary screen appends to the stack`() {
|
fun `navigateTo on a subscreen appends to the current stack`() {
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, SettingsScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
NavigationController.navigateTo(SettingsAppearance)
|
||||||
assertEquals(2, backStack.size)
|
|
||||||
assertEquals(ProfilesScreen, backStack.lastOrNull())
|
|
||||||
|
|
||||||
NavigationController.navigateTo(KeysScreen)
|
|
||||||
assertEquals(3, backStack.size)
|
assertEquals(3, backStack.size)
|
||||||
assertEquals(KeysScreen, backStack.lastOrNull())
|
assertEquals(SettingsAppearance, backStack.lastOrNull())
|
||||||
|
|
||||||
|
NavigationController.navigateTo(SettingsAbout)
|
||||||
|
assertEquals(4, backStack.size)
|
||||||
|
assertEquals(SettingsAbout, backStack.lastOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -176,13 +160,9 @@ class NavigationControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `resetTo clears the stack and sets the target screen`() {
|
fun `resetTo clears the stack and sets the target screen`() {
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, ProfilesScreen, KeysScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
|
||||||
NavigationController.navigateTo(KeysScreen)
|
|
||||||
assertEquals(3, backStack.size)
|
|
||||||
|
|
||||||
NavigationController.resetTo(SettingsScreen)
|
NavigationController.resetTo(SettingsScreen)
|
||||||
|
|
||||||
assertEquals(1, backStack.size)
|
assertEquals(1, backStack.size)
|
||||||
@@ -197,14 +177,17 @@ class NavigationControllerTest {
|
|||||||
assertNull(NavigationController.backStack)
|
assertNull(NavigationController.backStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── goBack: never leave the stack empty ───────────────────────────────
|
// ── goBack: pops stack, preserves chat root ───────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `goBack removes the top screen when stack has more than one`() {
|
fun `goBack removes the top screen when stack has more than one`() {
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen, SettingsScreen, SettingsAppearance)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
NavigationController.navigateTo(ProfilesScreen)
|
|
||||||
|
NavigationController.goBack()
|
||||||
|
|
||||||
assertEquals(2, backStack.size)
|
assertEquals(2, backStack.size)
|
||||||
|
assertEquals(SettingsScreen, backStack.lastOrNull())
|
||||||
|
|
||||||
NavigationController.goBack()
|
NavigationController.goBack()
|
||||||
|
|
||||||
@@ -213,18 +196,18 @@ class NavigationControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `goBack from chat opens history`() {
|
fun `goBack on root ChatScreen keeps ChatScreen`() {
|
||||||
val backStack = NavBackStack<NavKey>(ChatScreen)
|
val backStack = NavBackStack<NavKey>(ChatScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
NavigationController.goBack()
|
NavigationController.goBack()
|
||||||
|
|
||||||
assertEquals(1, backStack.size)
|
assertEquals(1, backStack.size)
|
||||||
assertEquals(HistoryScreen, backStack.lastOrNull())
|
assertEquals(ChatScreen, backStack.lastOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `goBack falls back to default screen when stack has one item`() {
|
fun `goBack falls back to default screen when non-root stack has one item`() {
|
||||||
val backStack = NavBackStack<NavKey>(ProfilesScreen)
|
val backStack = NavBackStack<NavKey>(ProfilesScreen)
|
||||||
NavigationController.backStack = backStack
|
NavigationController.backStack = backStack
|
||||||
|
|
||||||
@@ -256,18 +239,42 @@ class NavigationControllerTest {
|
|||||||
// ── primaryScreens ────────────────────────────────────────────────────
|
// ── primaryScreens ────────────────────────────────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isPrimaryScreen returns true for default screens`() {
|
fun `isPrimaryScreen returns true for all drawer screens`() {
|
||||||
assertTrue("ChatScreen should be primary by default", NavigationController.isPrimaryScreen(ChatScreen))
|
assertTrue("ChatScreen should be primary", NavigationController.isPrimaryScreen(ChatScreen))
|
||||||
assertTrue("SkillsScreen should be primary by default", NavigationController.isPrimaryScreen(SkillsScreen))
|
assertTrue("SkillsScreen should be primary", NavigationController.isPrimaryScreen(SkillsScreen))
|
||||||
assertTrue("CronJobsScreen should be primary by default", NavigationController.isPrimaryScreen(CronJobsScreen))
|
assertTrue("CronJobsScreen should be primary", NavigationController.isPrimaryScreen(CronJobsScreen))
|
||||||
assertTrue("SystemScreen should be primary by default", NavigationController.isPrimaryScreen(SystemScreen))
|
assertTrue("SystemScreen should be primary", NavigationController.isPrimaryScreen(SystemScreen))
|
||||||
assertTrue("SettingsScreen should be primary by default", NavigationController.isPrimaryScreen(SettingsScreen))
|
assertTrue("SettingsScreen should be primary", NavigationController.isPrimaryScreen(SettingsScreen))
|
||||||
|
assertTrue("ProfilesScreen should be primary", NavigationController.isPrimaryScreen(ProfilesScreen))
|
||||||
|
assertTrue("LogsScreen should be primary", NavigationController.isPrimaryScreen(LogsScreen))
|
||||||
|
assertTrue("ConfigScreen should be primary", NavigationController.isPrimaryScreen(ConfigScreen))
|
||||||
|
assertTrue("BotsScreen should be primary", NavigationController.isPrimaryScreen(BotsScreen))
|
||||||
|
assertTrue("HistoryScreen should be primary", NavigationController.isPrimaryScreen(HistoryScreen))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isPrimaryScreen returns false for non-default screens`() {
|
fun `isPrimaryScreen returns false for subscreen detail keys`() {
|
||||||
assertFalse("ProfilesScreen should NOT be primary", NavigationController.isPrimaryScreen(ProfilesScreen))
|
assertFalse(
|
||||||
assertFalse("LogsScreen should NOT be primary", NavigationController.isPrimaryScreen(LogsScreen))
|
"SettingsAppearance should NOT be primary",
|
||||||
assertFalse("ConfigScreen should NOT be primary", NavigationController.isPrimaryScreen(ConfigScreen))
|
NavigationController.isPrimaryScreen(SettingsAppearance),
|
||||||
|
)
|
||||||
|
assertFalse(
|
||||||
|
"SettingsConnection should NOT be primary",
|
||||||
|
NavigationController.isPrimaryScreen(SettingsConnection),
|
||||||
|
)
|
||||||
|
assertFalse("SettingsLanguage should NOT be primary", NavigationController.isPrimaryScreen(SettingsLanguage))
|
||||||
|
assertFalse("SettingsChat should NOT be primary", NavigationController.isPrimaryScreen(SettingsChat))
|
||||||
|
assertFalse("SettingsBehavior should NOT be primary", NavigationController.isPrimaryScreen(SettingsBehavior))
|
||||||
|
assertFalse("SettingsAbout should NOT be primary", NavigationController.isPrimaryScreen(SettingsAbout))
|
||||||
|
assertFalse(
|
||||||
|
"ToolsetDetailKey should NOT be primary",
|
||||||
|
NavigationController.isPrimaryScreen(ToolsetDetailKey("tool")),
|
||||||
|
)
|
||||||
|
assertFalse(
|
||||||
|
"MemoryProviderDetailKey should NOT be primary",
|
||||||
|
NavigationController.isPrimaryScreen(MemoryProviderDetailKey("mem")),
|
||||||
|
)
|
||||||
|
assertFalse("GroupChatKey should NOT be primary", NavigationController.isPrimaryScreen(GroupChatKey("room")))
|
||||||
|
assertFalse("AuthLoginScreen should NOT be primary", NavigationController.isPrimaryScreen(AuthLoginScreen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user