mirror of
https://github.com/bitwarden/android.git
synced 2024-11-22 01:16:02 +03:00
BIT-896 Allow user to log out (#129)
This commit is contained in:
parent
6af438a264
commit
aafd32fbc3
21 changed files with 581 additions and 41 deletions
|
@ -36,6 +36,11 @@ interface AuthRepository {
|
||||||
captchaToken: String?,
|
captchaToken: String?,
|
||||||
): LoginResult
|
): LoginResult
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log out the current user.
|
||||||
|
*/
|
||||||
|
fun logout()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of [captchaTokenResultFlow].
|
* Set the value of [captchaTokenResultFlow].
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@ -88,6 +89,10 @@ class AuthRepositoryImpl @Inject constructor(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun logout() {
|
||||||
|
mutableAuthStateFlow.update { AuthState.Unauthenticated }
|
||||||
|
}
|
||||||
|
|
||||||
override fun setCaptchaCallbackTokenResult(tokenResult: CaptchaCallbackTokenResult) {
|
override fun setCaptchaCallbackTokenResult(tokenResult: CaptchaCallbackTokenResult) {
|
||||||
mutableCaptchaTokenFlow.tryEmit(tokenResult)
|
mutableCaptchaTokenFlow.tryEmit(tokenResult)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import com.x8bit.bitwarden.ui.auth.feature.landing.landingDestinations
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.login.loginDestinations
|
import com.x8bit.bitwarden.ui.auth.feature.login.loginDestinations
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.login.navigateToLogin
|
import com.x8bit.bitwarden.ui.auth.feature.login.navigateToLogin
|
||||||
|
|
||||||
private const val AUTH_ROUTE: String = "auth"
|
const val AUTH_ROUTE: String = "auth"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add auth destinations to the nav graph.
|
* Add auth destinations to the nav graph.
|
||||||
|
|
|
@ -5,15 +5,18 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.navigation.NavDestination
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navOptions
|
import androidx.navigation.navOptions
|
||||||
|
import com.x8bit.bitwarden.ui.auth.feature.auth.AUTH_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.auth.authDestinations
|
import com.x8bit.bitwarden.ui.auth.feature.auth.authDestinations
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.auth.navigateToAuth
|
import com.x8bit.bitwarden.ui.auth.feature.auth.navigateToAuth
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.SPLASH_ROUTE
|
import com.x8bit.bitwarden.ui.platform.feature.splash.SPLASH_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.navigateToSplash
|
import com.x8bit.bitwarden.ui.platform.feature.splash.navigateToSplash
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.splashDestinations
|
import com.x8bit.bitwarden.ui.platform.feature.splash.splashDestinations
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.VAULT_UNLOCKED_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.navigateToVaultUnlocked
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.navigateToVaultUnlocked
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.vaultUnlockedDestinations
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.vaultUnlockedDestinations
|
||||||
|
|
||||||
|
@ -39,7 +42,20 @@ fun RootNavScreen(
|
||||||
) {
|
) {
|
||||||
splashDestinations()
|
splashDestinations()
|
||||||
authDestinations(navController)
|
authDestinations(navController)
|
||||||
vaultUnlockedDestinations()
|
vaultUnlockedDestinations(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetRoute = when (state) {
|
||||||
|
RootNavState.Auth -> AUTH_ROUTE
|
||||||
|
RootNavState.Splash -> SPLASH_ROUTE
|
||||||
|
RootNavState.VaultUnlocked -> VAULT_UNLOCKED_ROUTE
|
||||||
|
}
|
||||||
|
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||||
|
|
||||||
|
// Don't navigate if we are already at the correct root. This notably happens during process
|
||||||
|
// death. In this case, the NavHost already restores state, so we don't have to navigate.
|
||||||
|
if (currentRoute == targetRoute) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// When state changes, navigate to different root navigation state
|
// When state changes, navigate to different root navigation state
|
||||||
|
@ -47,10 +63,10 @@ fun RootNavScreen(
|
||||||
// When changing root navigation state, pop everything else off the back stack:
|
// When changing root navigation state, pop everything else off the back stack:
|
||||||
popUpTo(navController.graph.id) {
|
popUpTo(navController.graph.id) {
|
||||||
inclusive = false
|
inclusive = false
|
||||||
saveState = true
|
saveState = false
|
||||||
}
|
}
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
restoreState = true
|
restoreState = false
|
||||||
}
|
}
|
||||||
|
|
||||||
when (state) {
|
when (state) {
|
||||||
|
@ -59,3 +75,20 @@ fun RootNavScreen(
|
||||||
RootNavState.VaultUnlocked -> navController.navigateToVaultUnlocked(rootNavOptions)
|
RootNavState.VaultUnlocked -> navController.navigateToVaultUnlocked(rootNavOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method that returns the highest level route for the given [NavDestination].
|
||||||
|
*
|
||||||
|
* As noted above, this can be removed after upgrading to latest compose navigation, since
|
||||||
|
* the nav args can prevent us from having to do this check.
|
||||||
|
*/
|
||||||
|
@Suppress("ReturnCount")
|
||||||
|
private fun NavDestination?.rootLevelRoute(): String? {
|
||||||
|
if (this == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (parent?.route == null) {
|
||||||
|
return route
|
||||||
|
}
|
||||||
|
return parent.rootLevelRoute()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
|
||||||
|
private const val ACCOUNT_SECURITY_ROUTE = "account_security"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add settings destinations to the nav graph.
|
||||||
|
*/
|
||||||
|
fun NavGraphBuilder.accountSecurityDestination(
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
composable(ACCOUNT_SECURITY_ROUTE) {
|
||||||
|
AccountSecurityScreen(onNavigateBack = onNavigateBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the account security screen.
|
||||||
|
*/
|
||||||
|
fun NavController.navigateToAccountSecurity(navOptions: NavOptions? = null) {
|
||||||
|
navigate(ACCOUNT_SECURITY_ROUTE, navOptions)
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenOverflowTopAppBar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the account security screen.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AccountSecurityScreen(
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
viewModel: AccountSecurityViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
|
when (event) {
|
||||||
|
AccountSecurityEvent.NavigateBack -> onNavigateBack.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color = MaterialTheme.colorScheme.surface),
|
||||||
|
) {
|
||||||
|
BitwardenOverflowTopAppBar(
|
||||||
|
title = stringResource(id = R.string.account),
|
||||||
|
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||||
|
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||||
|
onNavigationIconClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(AccountSecurityAction.BackClick) }
|
||||||
|
},
|
||||||
|
dropdownMenuItemContent = {},
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
AccountSecurityRow(
|
||||||
|
text = R.string.log_out.asText(),
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(AccountSecurityAction.LogoutClick) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AccountSecurityRow(
|
||||||
|
text: Text,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
text = text(),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for the account security screen.
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class AccountSecurityViewModel @Inject constructor(
|
||||||
|
private val authRepository: AuthRepository,
|
||||||
|
) : BaseViewModel<Unit, AccountSecurityEvent, AccountSecurityAction>(
|
||||||
|
initialState = Unit,
|
||||||
|
) {
|
||||||
|
override fun handleAction(action: AccountSecurityAction): Unit = when (action) {
|
||||||
|
AccountSecurityAction.LogoutClick -> authRepository.logout()
|
||||||
|
AccountSecurityAction.BackClick -> sendEvent(AccountSecurityEvent.NavigateBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models events for the account security screen.
|
||||||
|
*/
|
||||||
|
sealed class AccountSecurityEvent {
|
||||||
|
/**
|
||||||
|
* Navigate back.
|
||||||
|
*/
|
||||||
|
data object NavigateBack : AccountSecurityEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models actions for the account security screen.
|
||||||
|
*/
|
||||||
|
sealed class AccountSecurityAction {
|
||||||
|
/**
|
||||||
|
* User clicked back button.
|
||||||
|
*/
|
||||||
|
data object BackClick : AccountSecurityAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked log out.
|
||||||
|
*/
|
||||||
|
data object LogoutClick : AccountSecurityAction()
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
|
||||||
|
const val SETTINGS_ROUTE: String = "settings"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add settings destinations to the nav graph.
|
||||||
|
*/
|
||||||
|
fun NavGraphBuilder.settingsDestinations(
|
||||||
|
onNavigateToAccountSecurity: () -> Unit,
|
||||||
|
) {
|
||||||
|
composable(SETTINGS_ROUTE) {
|
||||||
|
SettingsScreen(onNavigateToAccountSecurity = onNavigateToAccountSecurity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the settings screen screen.
|
||||||
|
*/
|
||||||
|
fun NavController.navigateToSettings(navOptions: NavOptions? = null) {
|
||||||
|
navigate(SETTINGS_ROUTE, navOptions)
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenMediumTopAppBar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the settings screen.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SettingsScreen(
|
||||||
|
onNavigateToAccountSecurity: () -> Unit,
|
||||||
|
viewModel: SettingsViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
|
when (event) {
|
||||||
|
SettingsEvent.NavigateAccountSecurity -> onNavigateToAccountSecurity.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color = MaterialTheme.colorScheme.surface),
|
||||||
|
) {
|
||||||
|
BitwardenMediumTopAppBar(
|
||||||
|
title = stringResource(id = R.string.settings),
|
||||||
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
|
||||||
|
)
|
||||||
|
SettingsRow(
|
||||||
|
text = R.string.account.asText(),
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(SettingsAction.AccountSecurityClick) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SettingsRow(
|
||||||
|
text: Text,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
text = text(),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for the settings screen.
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class SettingsViewModel @Inject constructor() : BaseViewModel<Unit, SettingsEvent, SettingsAction>(
|
||||||
|
initialState = Unit,
|
||||||
|
) {
|
||||||
|
override fun handleAction(action: SettingsAction): Unit = when (action) {
|
||||||
|
SettingsAction.AccountSecurityClick -> handleAccountSecurityClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleAccountSecurityClick() {
|
||||||
|
sendEvent(SettingsEvent.NavigateAccountSecurity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models events for the settings screen.
|
||||||
|
*/
|
||||||
|
sealed class SettingsEvent {
|
||||||
|
/**
|
||||||
|
* Navigate to the account security screen.
|
||||||
|
*/
|
||||||
|
data object NavigateAccountSecurity : SettingsEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models actions for the settings screen.
|
||||||
|
*/
|
||||||
|
sealed class SettingsAction {
|
||||||
|
/**
|
||||||
|
* User clicked account security.
|
||||||
|
*/
|
||||||
|
data object AccountSecurityClick : SettingsAction()
|
||||||
|
}
|
|
@ -2,12 +2,15 @@ package com.x8bit.bitwarden.ui.platform.feature.vaultunlocked
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.navigation
|
import androidx.navigation.navigation
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.accountSecurityDestination
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToAccountSecurity
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.VAULT_UNLOCKED_NAV_BAR_ROUTE
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.VAULT_UNLOCKED_NAV_BAR_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.vaultUnlockedNavBarDestination
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.vaultUnlockedNavBarDestination
|
||||||
|
|
||||||
private const val VAULT_UNLOCKED_ROUTE: String = "VaultUnlocked"
|
const val VAULT_UNLOCKED_ROUTE: String = "VaultUnlocked"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigate to the vault unlocked screen.
|
* Navigate to the vault unlocked screen.
|
||||||
|
@ -19,11 +22,16 @@ fun NavController.navigateToVaultUnlocked(navOptions: NavOptions? = null) {
|
||||||
/**
|
/**
|
||||||
* Add vault unlocked destinations to the root nav graph.
|
* Add vault unlocked destinations to the root nav graph.
|
||||||
*/
|
*/
|
||||||
fun NavGraphBuilder.vaultUnlockedDestinations() {
|
fun NavGraphBuilder.vaultUnlockedDestinations(navController: NavHostController) {
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = VAULT_UNLOCKED_NAV_BAR_ROUTE,
|
startDestination = VAULT_UNLOCKED_NAV_BAR_ROUTE,
|
||||||
route = VAULT_UNLOCKED_ROUTE,
|
route = VAULT_UNLOCKED_ROUTE,
|
||||||
) {
|
) {
|
||||||
vaultUnlockedNavBarDestination()
|
vaultUnlockedNavBarDestination(
|
||||||
|
onNavigateToAccountSecurity = { navController.navigateToAccountSecurity() },
|
||||||
|
)
|
||||||
|
accountSecurityDestination(
|
||||||
|
onNavigateBack = { navController.popBackStack() },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,12 @@ fun NavController.navigateToVaultUnlockedNavBar(navOptions: NavOptions? = null)
|
||||||
/**
|
/**
|
||||||
* Add vault unlocked destination to the root nav graph.
|
* Add vault unlocked destination to the root nav graph.
|
||||||
*/
|
*/
|
||||||
fun NavGraphBuilder.vaultUnlockedNavBarDestination() {
|
fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
||||||
|
onNavigateToAccountSecurity: () -> Unit,
|
||||||
|
) {
|
||||||
composable(VAULT_UNLOCKED_NAV_BAR_ROUTE) {
|
composable(VAULT_UNLOCKED_NAV_BAR_ROUTE) {
|
||||||
VaultUnlockedNavBarScreen()
|
VaultUnlockedNavBarScreen(
|
||||||
|
onNavigateToAccountSecurity = onNavigateToAccountSecurity,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ import androidx.navigation.navOptions
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.components.PlaceholderComposable
|
import com.x8bit.bitwarden.ui.platform.components.PlaceholderComposable
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.SETTINGS_ROUTE
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToSettings
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.settingsDestinations
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GENERATOR_ROUTE
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GENERATOR_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.generatorDestination
|
import com.x8bit.bitwarden.ui.tools.feature.generator.generatorDestination
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGenerator
|
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGenerator
|
||||||
|
@ -41,6 +44,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun VaultUnlockedNavBarScreen(
|
fun VaultUnlockedNavBarScreen(
|
||||||
|
onNavigateToAccountSecurity: () -> Unit,
|
||||||
viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(),
|
viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(),
|
||||||
navController: NavHostController = rememberNavController(),
|
navController: NavHostController = rememberNavController(),
|
||||||
) {
|
) {
|
||||||
|
@ -68,6 +72,7 @@ fun VaultUnlockedNavBarScreen(
|
||||||
}
|
}
|
||||||
VaultUnlockedNavBarScaffold(
|
VaultUnlockedNavBarScaffold(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
onNavigateToAccountSecurity = onNavigateToAccountSecurity,
|
||||||
generatorTabClickedAction = {
|
generatorTabClickedAction = {
|
||||||
viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick)
|
viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick)
|
||||||
},
|
},
|
||||||
|
@ -89,6 +94,7 @@ fun VaultUnlockedNavBarScreen(
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
private fun VaultUnlockedNavBarScaffold(
|
private fun VaultUnlockedNavBarScaffold(
|
||||||
|
onNavigateToAccountSecurity: () -> Unit,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
vaultTabClickedAction: () -> Unit,
|
vaultTabClickedAction: () -> Unit,
|
||||||
sendTabClickedAction: () -> Unit,
|
sendTabClickedAction: () -> Unit,
|
||||||
|
@ -162,7 +168,9 @@ private fun VaultUnlockedNavBarScaffold(
|
||||||
vaultDestination()
|
vaultDestination()
|
||||||
sendDestination()
|
sendDestination()
|
||||||
generatorDestination()
|
generatorDestination()
|
||||||
settingsDestination()
|
settingsDestinations(
|
||||||
|
onNavigateToAccountSecurity = onNavigateToAccountSecurity,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,32 +309,3 @@ private fun NavController.navigateToSend(navOptions: NavOptions? = null) {
|
||||||
navigate(SEND_ROUTE, navOptions)
|
navigate(SEND_ROUTE, navOptions)
|
||||||
}
|
}
|
||||||
// #endregion Send
|
// #endregion Send
|
||||||
|
|
||||||
// #region Settings
|
|
||||||
/**
|
|
||||||
* TODO: move to settings package (BIT-147)
|
|
||||||
*/
|
|
||||||
private const val SETTINGS_ROUTE = "settings"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add settings destination to the nav graph.
|
|
||||||
*
|
|
||||||
* TODO: move to settings package (BIT-147)
|
|
||||||
*/
|
|
||||||
private fun NavGraphBuilder.settingsDestination() {
|
|
||||||
composable(SETTINGS_ROUTE) {
|
|
||||||
PlaceholderComposable(text = "Settings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the generator screen. Note this will only work if generator screen was added
|
|
||||||
* via [settingsDestination].
|
|
||||||
*
|
|
||||||
* TODO: move to settings package (BIT-147)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private fun NavController.navigateToSettings(navOptions: NavOptions? = null) {
|
|
||||||
navigate(SETTINGS_ROUTE, navOptions)
|
|
||||||
}
|
|
||||||
// #endregion Settings
|
|
||||||
|
|
9
app/src/main/res/drawable/ic_back.xml
Normal file
9
app/src/main/res/drawable/ic_back.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="25dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="25">
|
||||||
|
<path
|
||||||
|
android:pathData="M20,11.103H7.83L13.42,5.513L12,4.103L4,12.103L12,20.103L13.41,18.693L7.83,13.103H20V11.103Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
|
@ -202,6 +202,31 @@ class AuthRepositoryTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `logout should change AuthState to be Unauthenticated`() = runTest {
|
||||||
|
// First login:
|
||||||
|
coEvery {
|
||||||
|
accountsService.preLogin(email = EMAIL)
|
||||||
|
} returns Result.success(PRE_LOGIN_SUCCESS)
|
||||||
|
coEvery {
|
||||||
|
identityService.getToken(
|
||||||
|
email = EMAIL,
|
||||||
|
passwordHash = PASSWORD_HASH,
|
||||||
|
captchaToken = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
||||||
|
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||||
|
repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||||
|
|
||||||
|
// Then call logout:
|
||||||
|
repository.authStateFlow.test {
|
||||||
|
assertEquals(AuthState.Authenticated(ACCESS_TOKEN), awaitItem())
|
||||||
|
repository.logout()
|
||||||
|
assertEquals(AuthState.Unauthenticated, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val EMAIL = "test@test.com"
|
private const val EMAIL = "test@test.com"
|
||||||
private const val PASSWORD = "password"
|
private const val PASSWORD = "password"
|
||||||
|
|
|
@ -19,10 +19,10 @@ class RootNavScreenTest : BaseComposeTest() {
|
||||||
// When changing root navigation state, pop everything else off the back stack:
|
// When changing root navigation state, pop everything else off the back stack:
|
||||||
popUpTo(fakeNavHostController.graphId) {
|
popUpTo(fakeNavHostController.graphId) {
|
||||||
inclusive = false
|
inclusive = false
|
||||||
saveState = true
|
saveState = false
|
||||||
}
|
}
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
restoreState = true
|
restoreState = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class AccountSecurityScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on Log out click should send LogoutClick`() {
|
||||||
|
val viewModel: AccountSecurityViewModel = mockk {
|
||||||
|
every { eventFlow } returns emptyFlow()
|
||||||
|
every { trySendAction(AccountSecurityAction.LogoutClick) } returns Unit
|
||||||
|
}
|
||||||
|
composeTestRule.setContent {
|
||||||
|
AccountSecurityScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateBack = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithText("Log out").performClick()
|
||||||
|
verify { viewModel.trySendAction(AccountSecurityAction.LogoutClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on back click should send BackClick`() {
|
||||||
|
val viewModel: AccountSecurityViewModel = mockk {
|
||||||
|
every { eventFlow } returns emptyFlow()
|
||||||
|
every { trySendAction(AccountSecurityAction.BackClick) } returns Unit
|
||||||
|
}
|
||||||
|
composeTestRule.setContent {
|
||||||
|
AccountSecurityScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateBack = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||||
|
verify { viewModel.trySendAction(AccountSecurityAction.BackClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateAccountSecurity should call onNavigateToAccountSecurity`() {
|
||||||
|
var haveCalledNavigateBack = false
|
||||||
|
val viewModel = mockk<AccountSecurityViewModel> {
|
||||||
|
every { eventFlow } returns flowOf(AccountSecurityEvent.NavigateBack)
|
||||||
|
}
|
||||||
|
composeTestRule.setContent {
|
||||||
|
AccountSecurityScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateBack = { haveCalledNavigateBack = true },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assert(haveCalledNavigateBack)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import app.cash.turbine.test
|
||||||
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class AccountSecurityViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||||
|
val viewModel = AccountSecurityViewModel(
|
||||||
|
authRepository = mockk(),
|
||||||
|
)
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(AccountSecurityAction.BackClick)
|
||||||
|
assertEquals(AccountSecurityEvent.NavigateBack, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on LogoutClick should call logout`() = runTest {
|
||||||
|
val authRepository: AuthRepository = mockk {
|
||||||
|
every { logout() } returns Unit
|
||||||
|
}
|
||||||
|
val viewModel = AccountSecurityViewModel(
|
||||||
|
authRepository = authRepository,
|
||||||
|
)
|
||||||
|
viewModel.trySendAction(AccountSecurityAction.LogoutClick)
|
||||||
|
verify { authRepository.logout() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class SettingsScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on account row click should emit AccountSecurityClick`() {
|
||||||
|
val viewModel = mockk<SettingsViewModel> {
|
||||||
|
every { eventFlow } returns emptyFlow()
|
||||||
|
every { trySendAction(SettingsAction.AccountSecurityClick) } returns Unit
|
||||||
|
}
|
||||||
|
composeTestRule.setContent {
|
||||||
|
SettingsScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateToAccountSecurity = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithText("Account").performClick()
|
||||||
|
verify { viewModel.trySendAction(SettingsAction.AccountSecurityClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateAccountSecurity should call onNavigateToAccountSecurity`() {
|
||||||
|
var haveCalledNavigateToAccountSecurity = false
|
||||||
|
val viewModel = mockk<SettingsViewModel> {
|
||||||
|
every { eventFlow } returns flowOf(SettingsEvent.NavigateAccountSecurity)
|
||||||
|
}
|
||||||
|
composeTestRule.setContent {
|
||||||
|
SettingsScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateToAccountSecurity = {
|
||||||
|
haveCalledNavigateToAccountSecurity = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assert(haveCalledNavigateToAccountSecurity)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||||
|
|
||||||
|
import app.cash.turbine.test
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class SettingsViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on AccountSecurityClick should emit NavigateAccountSecurity`() = runTest {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(SettingsAction.AccountSecurityClick)
|
||||||
|
assertEquals(SettingsEvent.NavigateAccountSecurity, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onNodeWithText("My vault").performClick()
|
onNodeWithText("My vault").performClick()
|
||||||
|
@ -52,6 +53,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
||||||
|
@ -73,6 +75,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onNodeWithText("Send").performClick()
|
onNodeWithText("Send").performClick()
|
||||||
|
@ -93,6 +96,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
||||||
|
@ -114,6 +118,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onNodeWithText("Generator").performClick()
|
onNodeWithText("Generator").performClick()
|
||||||
|
@ -134,6 +139,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
||||||
|
@ -155,6 +161,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onNodeWithText("Settings").performClick()
|
onNodeWithText("Settings").performClick()
|
||||||
|
@ -175,6 +182,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
navController = fakeNavHostController,
|
navController = fakeNavHostController,
|
||||||
|
onNavigateToAccountSecurity = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault") }
|
||||||
|
|
Loading…
Reference in a new issue