mirror of
https://github.com/bitwarden/android.git
synced 2026-11-09 10:34:18 -06:00
[PM-45372] refactor: Migrate the Test Harness module to Navigation3 (#7508)
This commit is contained in:
@@ -94,7 +94,6 @@ dependencies {
|
||||
implementation(libs.google.hilt.android)
|
||||
ksp(libs.google.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
||||
implementation(libs.androidx.navigation3.runtime)
|
||||
implementation(libs.androidx.navigation3.ui)
|
||||
|
||||
@@ -7,11 +7,12 @@ import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import com.bitwarden.testharness.ui.platform.feature.main.MainGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.RootNavScreen
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.RootNavigationRoute
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.rootNavDestination
|
||||
import com.bitwarden.ui.platform.navigation.components.BitwardenNavDisplay
|
||||
import com.bitwarden.ui.platform.navigation.util.rememberNavigationController
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.platform.util.setupEdgeToEdge
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -40,18 +41,20 @@ class MainActivity : ComponentActivity() {
|
||||
setupEdgeToEdge(appThemeFlow = mainViewModel.stateFlow.map { it.theme })
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
val navigationController = rememberNavigationController<MainGraph>(
|
||||
routes = arrayOf(MainGraph.RootNavigationRoute),
|
||||
)
|
||||
val state by mainViewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
BitwardenTheme(
|
||||
theme = state.theme,
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = RootNavigationRoute,
|
||||
) {
|
||||
rootNavDestination { shouldShowSplashScreen = false }
|
||||
}
|
||||
BitwardenNavDisplay(
|
||||
navigationController = navigationController,
|
||||
entryProvider = entryProvider {
|
||||
rootNavDestination { shouldShowSplashScreen = false }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bitwarden.testharness.ui.platform.feature.autofill
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents the hierarchy of the Autofill test UI navigation graph and provides type-safe routes
|
||||
* to each destination available in the graph.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class AutofillGraph : NavKey {
|
||||
/**
|
||||
* The type-safe route for the Autofill test placeholder screen, the root of the Autofill test
|
||||
* flow.
|
||||
*/
|
||||
@Serializable
|
||||
data object AutofillPlaceholderRoute : AutofillGraph()
|
||||
}
|
||||
+24
-33
@@ -2,48 +2,39 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.autofill
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithRootPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.RootNavGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.components.BitwardenNavDisplay
|
||||
import com.bitwarden.ui.platform.navigation.util.rememberNavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.rootPushEntry
|
||||
|
||||
/**
|
||||
* Autofill graph route - serves as the parent for all Autofill test flows.
|
||||
* Add the Autofill test flow to the root navigation entries. The [RootNavGraph.AutofillRoute]
|
||||
* hosts the [AutofillGraph].
|
||||
*/
|
||||
@Serializable
|
||||
data object AutofillGraphRoute
|
||||
|
||||
/**
|
||||
* Autofill test placeholder screen - the start destination of the graph.
|
||||
*/
|
||||
@Serializable
|
||||
data object AutofillPlaceholderRoute
|
||||
|
||||
/**
|
||||
* Add Autofill nav graph to the root nav graph.
|
||||
*
|
||||
* This graph contains the placeholder screen and can be expanded with
|
||||
* additional autofill test screens as needed.
|
||||
*/
|
||||
fun NavGraphBuilder.autofillGraph(
|
||||
fun EntryProviderScope<RootNavGraph>.autofillGraph(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
navigation<AutofillGraphRoute>(
|
||||
startDestination = AutofillPlaceholderRoute,
|
||||
) {
|
||||
autofillPlaceholderDestination(
|
||||
onNavigateBack = onNavigateBack,
|
||||
entry<RootNavGraph.AutofillRoute> {
|
||||
val navigationController = rememberNavigationController<AutofillGraph>(
|
||||
routes = arrayOf(AutofillGraph.AutofillPlaceholderRoute),
|
||||
)
|
||||
BitwardenNavDisplay(
|
||||
navigationController = navigationController,
|
||||
entryProvider = entryProvider {
|
||||
autofillPlaceholderDestination(onNavigateBack = onNavigateBack)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NavGraphBuilder.autofillPlaceholderDestination(
|
||||
private fun EntryProviderScope<AutofillGraph>.autofillPlaceholderDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithRootPushTransitions<AutofillPlaceholderRoute> {
|
||||
rootPushEntry<AutofillGraph.AutofillPlaceholderRoute> {
|
||||
AutofillPlaceholderScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -51,8 +42,8 @@ private fun NavGraphBuilder.autofillPlaceholderDestination(
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the Autofill graph.
|
||||
* Navigate to the Autofill test flow.
|
||||
*/
|
||||
fun NavController.navigateToAutofillGraph(navOptions: NavOptions? = null) {
|
||||
navigate(route = AutofillGraphRoute, navOptions = navOptions)
|
||||
fun NavigationController<RootNavGraph>.navigateToAutofill() {
|
||||
this.navigate(route = RootNavGraph.AutofillRoute)
|
||||
}
|
||||
|
||||
+9
-16
@@ -2,26 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.createpasskey
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.pushEntry
|
||||
|
||||
/**
|
||||
* Create passkey test screen.
|
||||
* Add Create Passkey destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object CreatePasskeyRoute
|
||||
|
||||
/**
|
||||
* Add Create Passkey destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.createPasskeyDestination(
|
||||
fun EntryProviderScope<CredentialManagerGraph>.createPasskeyDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions<CreatePasskeyRoute> {
|
||||
pushEntry<CredentialManagerGraph.CreatePasskeyRoute> {
|
||||
CreatePasskeyScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -31,6 +24,6 @@ fun NavGraphBuilder.createPasskeyDestination(
|
||||
/**
|
||||
* Navigate to the Create Passkey test screen.
|
||||
*/
|
||||
fun NavController.navigateToCreatePasskey(navOptions: NavOptions? = null) {
|
||||
navigate(route = CreatePasskeyRoute, navOptions = navOptions)
|
||||
fun NavigationController<CredentialManagerGraph>.navigateToCreatePasskey() {
|
||||
this.navigate(route = CredentialManagerGraph.CreatePasskeyRoute)
|
||||
}
|
||||
|
||||
+9
-16
@@ -2,26 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.createpassword
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.pushEntry
|
||||
|
||||
/**
|
||||
* Create password test screen.
|
||||
* Add Create Password destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object CreatePasswordRoute
|
||||
|
||||
/**
|
||||
* Add Create Password destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.createPasswordDestination(
|
||||
fun EntryProviderScope<CredentialManagerGraph>.createPasswordDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions<CreatePasswordRoute> {
|
||||
pushEntry<CredentialManagerGraph.CreatePasswordRoute> {
|
||||
CreatePasswordScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -31,6 +24,6 @@ fun NavGraphBuilder.createPasswordDestination(
|
||||
/**
|
||||
* Navigate to the Create Password test screen.
|
||||
*/
|
||||
fun NavController.navigateToCreatePassword(navOptions: NavOptions? = null) {
|
||||
navigate(route = CreatePasswordRoute, navOptions = navOptions)
|
||||
fun NavigationController<CredentialManagerGraph>.navigateToCreatePassword() {
|
||||
this.navigate(route = CredentialManagerGraph.CreatePasswordRoute)
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.bitwarden.testharness.ui.platform.feature.credentialmanager
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents the hierarchy of the Credential Manager test UI navigation graph and provides
|
||||
* type-safe routes to each destination available in the graph.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class CredentialManagerGraph : NavKey {
|
||||
/**
|
||||
* The type-safe route for the Credential Manager test category list screen, the root of the
|
||||
* Credential Manager test flow.
|
||||
*/
|
||||
@Serializable
|
||||
data object CredentialManagerListRoute : CredentialManagerGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Get Password test screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasswordRoute : CredentialManagerGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Create Password test screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object CreatePasswordRoute : CredentialManagerGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Get Passkey test screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasskeyRoute : CredentialManagerGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Create Passkey test screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object CreatePasskeyRoute : CredentialManagerGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Get Password or Passkey test screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasswordOrPasskeyRoute : CredentialManagerGraph()
|
||||
}
|
||||
+68
-67
@@ -2,10 +2,8 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.credentialmanager
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.testharness.ui.platform.feature.createpasskey.createPasskeyDestination
|
||||
import com.bitwarden.testharness.ui.platform.feature.createpasskey.navigateToCreatePasskey
|
||||
@@ -17,76 +15,79 @@ import com.bitwarden.testharness.ui.platform.feature.getpassword.getPasswordDest
|
||||
import com.bitwarden.testharness.ui.platform.feature.getpassword.navigateToGetPassword
|
||||
import com.bitwarden.testharness.ui.platform.feature.getpasswordorpasskey.getPasswordOrPasskeyDestination
|
||||
import com.bitwarden.testharness.ui.platform.feature.getpasswordorpasskey.navigateToGetPasswordOrPasskey
|
||||
import com.bitwarden.ui.platform.base.util.composableWithRootPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.RootNavGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.components.BitwardenNavDisplay
|
||||
import com.bitwarden.ui.platform.navigation.util.rememberNavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.rootPushEntry
|
||||
|
||||
/**
|
||||
* Credential Manager graph route - serves as the parent for all Credential Manager API test flows.
|
||||
* Add the Credential Manager test flow to the root navigation entries. The
|
||||
* [RootNavGraph.CredentialManagerRoute] hosts the [CredentialManagerGraph].
|
||||
*/
|
||||
@Serializable
|
||||
data object CredentialManagerGraphRoute
|
||||
|
||||
/**
|
||||
* Credential Manager test category list screen - the start destination of the graph.
|
||||
*/
|
||||
@Serializable
|
||||
data object CredentialManagerListRoute
|
||||
|
||||
/**
|
||||
* Add Credential Manager nav graph to the root nav graph.
|
||||
*
|
||||
* This graph contains the list screen and all nested credential manager API test screens.
|
||||
*/
|
||||
fun NavGraphBuilder.credentialManagerGraph(
|
||||
fun EntryProviderScope<RootNavGraph>.credentialManagerGraph(
|
||||
onNavigateBack: () -> Unit,
|
||||
navController: NavController,
|
||||
) {
|
||||
navigation<CredentialManagerGraphRoute>(
|
||||
startDestination = CredentialManagerListRoute,
|
||||
) {
|
||||
credentialManagerListDestination(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToGetPassword = {
|
||||
navController.navigateToGetPassword()
|
||||
},
|
||||
onNavigateToCreatePassword = {
|
||||
navController.navigateToCreatePassword()
|
||||
},
|
||||
onNavigateToGetPasskey = {
|
||||
navController.navigateToGetPasskey()
|
||||
},
|
||||
onNavigateToCreatePasskey = {
|
||||
navController.navigateToCreatePasskey()
|
||||
},
|
||||
onNavigateToGetPasswordOrPasskey = {
|
||||
navController.navigateToGetPasswordOrPasskey()
|
||||
},
|
||||
entry<RootNavGraph.CredentialManagerRoute> {
|
||||
val navigationController = rememberNavigationController<CredentialManagerGraph>(
|
||||
routes = arrayOf(CredentialManagerGraph.CredentialManagerListRoute),
|
||||
)
|
||||
|
||||
getPasswordDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
createPasswordDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
getPasskeyDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
createPasskeyDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
getPasswordOrPasskeyDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
BitwardenNavDisplay(
|
||||
navigationController = navigationController,
|
||||
entryProvider = entryProvider {
|
||||
credentialManagerDestinations(
|
||||
navigationController = navigationController,
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the Credential Manager test destinations to the Credential Manager navigation entries.
|
||||
*
|
||||
* The [CredentialManagerGraph.CredentialManagerListRoute] is the root of the Credential Manager
|
||||
* test flow, which contains the list screen and all nested credential manager API test screens.
|
||||
*/
|
||||
private fun EntryProviderScope<CredentialManagerGraph>.credentialManagerDestinations(
|
||||
navigationController: NavigationController<CredentialManagerGraph>,
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
credentialManagerListDestination(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToGetPassword = { navigationController.navigateToGetPassword() },
|
||||
onNavigateToCreatePassword = { navigationController.navigateToCreatePassword() },
|
||||
onNavigateToGetPasskey = { navigationController.navigateToGetPasskey() },
|
||||
onNavigateToCreatePasskey = { navigationController.navigateToCreatePasskey() },
|
||||
onNavigateToGetPasswordOrPasskey = {
|
||||
navigationController.navigateToGetPasswordOrPasskey()
|
||||
},
|
||||
)
|
||||
|
||||
getPasswordDestination(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
|
||||
createPasswordDestination(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
|
||||
getPasskeyDestination(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
|
||||
createPasskeyDestination(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
|
||||
getPasswordOrPasskeyDestination(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun NavGraphBuilder.credentialManagerListDestination(
|
||||
private fun EntryProviderScope<CredentialManagerGraph>.credentialManagerListDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToGetPassword: () -> Unit,
|
||||
onNavigateToCreatePassword: () -> Unit,
|
||||
@@ -94,7 +95,7 @@ private fun NavGraphBuilder.credentialManagerListDestination(
|
||||
onNavigateToCreatePasskey: () -> Unit,
|
||||
onNavigateToGetPasswordOrPasskey: () -> Unit,
|
||||
) {
|
||||
composableWithRootPushTransitions<CredentialManagerListRoute> {
|
||||
rootPushEntry<CredentialManagerGraph.CredentialManagerListRoute> {
|
||||
CredentialManagerListScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToGetPassword = { onNavigateToGetPassword() },
|
||||
@@ -107,8 +108,8 @@ private fun NavGraphBuilder.credentialManagerListDestination(
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the Credential Manager flow.
|
||||
* Navigate to the Credential Manager test flow.
|
||||
*/
|
||||
fun NavController.navigateToCredentialManagerGraph(navOptions: NavOptions? = null) {
|
||||
navigate(route = CredentialManagerGraphRoute, navOptions = navOptions)
|
||||
fun NavigationController<RootNavGraph>.navigateToCredentialManager() {
|
||||
this.navigate(route = RootNavGraph.CredentialManagerRoute)
|
||||
}
|
||||
|
||||
+9
-16
@@ -2,26 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.getpasskey
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.pushEntry
|
||||
|
||||
/**
|
||||
* Get passkey test screen.
|
||||
* Add Get Passkey destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasskeyRoute
|
||||
|
||||
/**
|
||||
* Add Get Passkey destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.getPasskeyDestination(
|
||||
fun EntryProviderScope<CredentialManagerGraph>.getPasskeyDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions<GetPasskeyRoute> {
|
||||
pushEntry<CredentialManagerGraph.GetPasskeyRoute> {
|
||||
GetPasskeyScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -31,6 +24,6 @@ fun NavGraphBuilder.getPasskeyDestination(
|
||||
/**
|
||||
* Navigate to the Get Passkey test screen.
|
||||
*/
|
||||
fun NavController.navigateToGetPasskey(navOptions: NavOptions? = null) {
|
||||
navigate(route = GetPasskeyRoute, navOptions = navOptions)
|
||||
fun NavigationController<CredentialManagerGraph>.navigateToGetPasskey() {
|
||||
this.navigate(route = CredentialManagerGraph.GetPasskeyRoute)
|
||||
}
|
||||
|
||||
+9
-16
@@ -2,26 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.getpassword
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.pushEntry
|
||||
|
||||
/**
|
||||
* Get password test screen.
|
||||
* Add Get Password destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasswordRoute
|
||||
|
||||
/**
|
||||
* Add Get Password destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.getPasswordDestination(
|
||||
fun EntryProviderScope<CredentialManagerGraph>.getPasswordDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions<GetPasswordRoute> {
|
||||
pushEntry<CredentialManagerGraph.GetPasswordRoute> {
|
||||
GetPasswordScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -31,6 +24,6 @@ fun NavGraphBuilder.getPasswordDestination(
|
||||
/**
|
||||
* Navigate to the Get Password test screen.
|
||||
*/
|
||||
fun NavController.navigateToGetPassword(navOptions: NavOptions? = null) {
|
||||
navigate(route = GetPasswordRoute, navOptions = navOptions)
|
||||
fun NavigationController<CredentialManagerGraph>.navigateToGetPassword() {
|
||||
this.navigate(route = CredentialManagerGraph.GetPasswordRoute)
|
||||
}
|
||||
|
||||
+9
-16
@@ -2,26 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.getpasswordorpasskey
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.util.pushEntry
|
||||
|
||||
/**
|
||||
* Get Password or Passkey test screen.
|
||||
* Add Get Password or Passkey destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object GetPasswordOrPasskeyRoute
|
||||
|
||||
/**
|
||||
* Add Get Password or Passkey destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.getPasswordOrPasskeyDestination(
|
||||
fun EntryProviderScope<CredentialManagerGraph>.getPasswordOrPasskeyDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions<GetPasswordOrPasskeyRoute> {
|
||||
pushEntry<CredentialManagerGraph.GetPasswordOrPasskeyRoute> {
|
||||
GetPasswordOrPasskeyScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -31,6 +24,6 @@ fun NavGraphBuilder.getPasswordOrPasskeyDestination(
|
||||
/**
|
||||
* Navigate to the Get Password or Passkey test screen.
|
||||
*/
|
||||
fun NavController.navigateToGetPasswordOrPasskey(navOptions: NavOptions? = null) {
|
||||
navigate(route = GetPasswordOrPasskeyRoute, navOptions = navOptions)
|
||||
fun NavigationController<CredentialManagerGraph>.navigateToGetPasswordOrPasskey() {
|
||||
this.navigate(route = CredentialManagerGraph.GetPasswordOrPasskeyRoute)
|
||||
}
|
||||
|
||||
+6
-12
@@ -2,25 +2,19 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.landing
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.ui.platform.base.util.composableWithRootPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.rootnav.RootNavGraph
|
||||
import com.bitwarden.ui.platform.navigation.util.rootPushEntry
|
||||
|
||||
/**
|
||||
* Landing screen - main entry point showing test category options.
|
||||
* Add Landing destination to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object LandingRoute
|
||||
|
||||
/**
|
||||
* Add Landing destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.landingDestination(
|
||||
fun EntryProviderScope<RootNavGraph>.landingDestination(
|
||||
onNavigateToAutofill: () -> Unit,
|
||||
onNavigateToCredentialManager: () -> Unit,
|
||||
) {
|
||||
composableWithRootPushTransitions<LandingRoute> {
|
||||
rootPushEntry<RootNavGraph.LandingRoute> {
|
||||
LandingScreen(
|
||||
onNavigateToAutofill = onNavigateToAutofill,
|
||||
onNavigateToCredentialManager = onNavigateToCredentialManager,
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bitwarden.testharness.ui.platform.feature.main
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents the hierarchy of the Main UI navigation graph and provides type-safe routes to each
|
||||
* destination available in the graph.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class MainGraph : NavKey {
|
||||
/**
|
||||
* The type-safe route for the root navigation screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object RootNavigationRoute : MainGraph()
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.bitwarden.testharness.ui.platform.feature.rootnav
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import com.bitwarden.testharness.ui.platform.feature.autofill.AutofillGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.CredentialManagerGraph
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents the hierarchy of the Root UI navigation graph and provides type-safe routes to each
|
||||
* top-level section of the test harness.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class RootNavGraph : NavKey {
|
||||
/**
|
||||
* The type-safe route for the landing screen, the main entry point showing the test category
|
||||
* options.
|
||||
*/
|
||||
@Serializable
|
||||
data object LandingRoute : RootNavGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Autofill test flow, which hosts the [AutofillGraph].
|
||||
*/
|
||||
@Serializable
|
||||
data object AutofillRoute : RootNavGraph()
|
||||
|
||||
/**
|
||||
* The type-safe route for the Credential Manager test flow, which hosts the
|
||||
* [CredentialManagerGraph].
|
||||
*/
|
||||
@Serializable
|
||||
data object CredentialManagerRoute : RootNavGraph()
|
||||
}
|
||||
+5
-12
@@ -2,24 +2,17 @@
|
||||
|
||||
package com.bitwarden.testharness.ui.platform.feature.rootnav
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.bitwarden.testharness.ui.platform.feature.main.MainGraph
|
||||
|
||||
/**
|
||||
* The type-safe route for the root navigation screen.
|
||||
* Add the root navigation screen to the navigation entries.
|
||||
*/
|
||||
@Serializable
|
||||
data object RootNavigationRoute
|
||||
|
||||
/**
|
||||
* Add the root navigation screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.rootNavDestination(
|
||||
fun EntryProviderScope<MainGraph>.rootNavDestination(
|
||||
onSplashScreenRemoved: () -> Unit,
|
||||
) {
|
||||
composable<RootNavigationRoute> {
|
||||
entry<MainGraph.RootNavigationRoute> {
|
||||
RootNavScreen(onSplashScreenRemoved = onSplashScreenRemoved)
|
||||
}
|
||||
}
|
||||
|
||||
+35
-30
@@ -5,24 +5,32 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation3.runtime.NavEntry
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import com.bitwarden.testharness.ui.platform.feature.autofill.autofillGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.autofill.navigateToAutofillGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.autofill.navigateToAutofill
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.credentialManagerGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.navigateToCredentialManagerGraph
|
||||
import com.bitwarden.testharness.ui.platform.feature.landing.LandingRoute
|
||||
import com.bitwarden.testharness.ui.platform.feature.credentialmanager.navigateToCredentialManager
|
||||
import com.bitwarden.testharness.ui.platform.feature.landing.landingDestination
|
||||
import com.bitwarden.ui.platform.navigation.NavigationController
|
||||
import com.bitwarden.ui.platform.navigation.components.BitwardenNavDisplay
|
||||
import com.bitwarden.ui.platform.navigation.util.rememberNavigationController
|
||||
|
||||
/**
|
||||
* Controls the root level [NavHost] for the test harness app.
|
||||
* Controls the root level [NavDisplay] for the test harness app. Each route in the [RootNavGraph]
|
||||
* represents a top-level section of the test harness, which may host its own nested navigation
|
||||
* graph.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun RootNavScreen(
|
||||
viewModel: RootNavViewModel = hiltViewModel(),
|
||||
navController: NavHostController = rememberNavController(),
|
||||
navigationController: NavigationController<RootNavGraph> = rememberNavigationController(
|
||||
routes = arrayOf(RootNavGraph.LandingRoute),
|
||||
),
|
||||
entryProvider: (RootNavGraph) -> NavEntry<RootNavGraph> = entryProvider(
|
||||
navigationController = navigationController,
|
||||
),
|
||||
onSplashScreenRemoved: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
@@ -33,26 +41,23 @@ fun RootNavScreen(
|
||||
}
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = LandingRoute,
|
||||
) {
|
||||
landingDestination(
|
||||
onNavigateToAutofill = {
|
||||
navController.navigateToAutofillGraph()
|
||||
},
|
||||
onNavigateToCredentialManager = {
|
||||
navController.navigateToCredentialManagerGraph()
|
||||
},
|
||||
)
|
||||
BitwardenNavDisplay(
|
||||
navigationController = navigationController,
|
||||
entryProvider = entryProvider,
|
||||
)
|
||||
}
|
||||
|
||||
autofillGraph(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
credentialManagerGraph(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
navController = navController,
|
||||
)
|
||||
}
|
||||
private fun entryProvider(
|
||||
navigationController: NavigationController<RootNavGraph>,
|
||||
): (RootNavGraph) -> NavEntry<RootNavGraph> = entryProvider {
|
||||
landingDestination(
|
||||
onNavigateToAutofill = { navigationController.navigateToAutofill() },
|
||||
onNavigateToCredentialManager = { navigationController.navigateToCredentialManager() },
|
||||
)
|
||||
autofillGraph(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
credentialManagerGraph(
|
||||
onNavigateBack = { navigationController.navigateBack() },
|
||||
)
|
||||
}
|
||||
|
||||
+54
-19
@@ -1,22 +1,28 @@
|
||||
package com.bitwarden.testharness.ui.platform.feature.rootnav
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.navigation3.runtime.NavEntry
|
||||
import com.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.bitwarden.ui.platform.base.createMockNavHostController
|
||||
import com.bitwarden.ui.platform.navigation.util.navigationControllerOf
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Tests for the [RootNavScreen] composable in the testharness module.
|
||||
*
|
||||
* Verifies that root-level navigation correctly routes to different test flows
|
||||
* based on the [RootNavState].
|
||||
*/
|
||||
class RootNavScreenTest : BaseComposeTest() {
|
||||
private val mockNavHostController = createMockNavHostController()
|
||||
private val navigationController = navigationControllerOf<RootNavGraph>(
|
||||
routes = arrayOf(RootNavGraph.LandingRoute),
|
||||
)
|
||||
private val rootNavStateFlow = MutableStateFlow<RootNavState>(RootNavState.Splash)
|
||||
private val viewModel = mockk<RootNavViewModel> {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
@@ -30,7 +36,8 @@ class RootNavScreenTest : BaseComposeTest() {
|
||||
setTestContent {
|
||||
RootNavScreen(
|
||||
viewModel = viewModel,
|
||||
navController = mockNavHostController,
|
||||
navigationController = navigationController,
|
||||
entryProvider = { key -> NavEntry(key = key) { Text(text = key.toString()) } },
|
||||
onSplashScreenRemoved = { isSplashScreenRemoved = true },
|
||||
)
|
||||
}
|
||||
@@ -39,40 +46,68 @@ class RootNavScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `initial state does not invoke splash screen removal`() {
|
||||
composeTestRule.runOnIdle {
|
||||
assert(!isSplashScreenRemoved)
|
||||
assertFalse(isSplashScreenRemoved)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `splash screen callback is called when transitioning from Splash state`() {
|
||||
composeTestRule.runOnIdle {
|
||||
assert(!isSplashScreenRemoved)
|
||||
assertFalse(isSplashScreenRemoved)
|
||||
}
|
||||
|
||||
// Transition to Landing state
|
||||
rootNavStateFlow.value = RootNavState.Landing
|
||||
composeTestRule.runOnIdle {
|
||||
assert(isSplashScreenRemoved)
|
||||
assertTrue(isSplashScreenRemoved)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nav host displays landing destination as start destination`() {
|
||||
fun `nav display displays the landing destination as the initial destination`() {
|
||||
composeTestRule
|
||||
.onNodeWithText(text = RootNavGraph.LandingRoute.toString())
|
||||
.assertIsDisplayed()
|
||||
composeTestRule.runOnIdle {
|
||||
// The NavHost is configured with LandingRoute as startDestination
|
||||
assert(true)
|
||||
assertEquals(
|
||||
listOf(RootNavGraph.LandingRoute),
|
||||
navigationController.backStack.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nav host contains autofill and credential manager graphs`() {
|
||||
fun `nav display displays the destination on top of the back stack`() {
|
||||
navigationController.navigate(RootNavGraph.CredentialManagerRoute)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = RootNavGraph.CredentialManagerRoute.toString())
|
||||
.assertIsDisplayed()
|
||||
composeTestRule.runOnIdle {
|
||||
// The NavHost contains:
|
||||
// - landingDestination()
|
||||
// - autofillGraph()
|
||||
// - credentialManagerGraph()
|
||||
// Navigation to these graphs is triggered by user interaction in LandingScreen
|
||||
assert(true)
|
||||
assertEquals(
|
||||
listOf(RootNavGraph.LandingRoute, RootNavGraph.CredentialManagerRoute),
|
||||
navigationController.backStack.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `system back should remove the top destination from the back stack`() {
|
||||
navigationController.navigate(RootNavGraph.AutofillRoute)
|
||||
// Wait for the new destination to be displayed so the back handler has been enabled.
|
||||
composeTestRule
|
||||
.onNodeWithText(text = RootNavGraph.AutofillRoute.toString())
|
||||
.assertIsDisplayed()
|
||||
backDispatcher?.onBackPressed()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = RootNavGraph.LandingRoute.toString())
|
||||
.assertIsDisplayed()
|
||||
composeTestRule.runOnIdle {
|
||||
assertEquals(
|
||||
listOf(RootNavGraph.LandingRoute),
|
||||
navigationController.backStack.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user