mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
BIT-457: Add Vault Settings and Folders screen UI (#457)
This commit is contained in:
parent
78461394f3
commit
c94b303abc
15 changed files with 530 additions and 164 deletions
|
@ -27,6 +27,7 @@ private const val SETTINGS_ROUTE: String = "settings"
|
||||||
fun NavGraphBuilder.settingsGraph(
|
fun NavGraphBuilder.settingsGraph(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
onNavigateToDeleteAccount: () -> Unit,
|
onNavigateToDeleteAccount: () -> Unit,
|
||||||
|
onNavigateToFolders: () -> Unit,
|
||||||
) {
|
) {
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = SETTINGS_ROUTE,
|
startDestination = SETTINGS_ROUTE,
|
||||||
|
@ -52,12 +53,15 @@ fun NavGraphBuilder.settingsGraph(
|
||||||
appearanceDestination(onNavigateBack = { navController.popBackStack() })
|
appearanceDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
autoFillDestination(onNavigateBack = { navController.popBackStack() })
|
autoFillDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
otherDestination(onNavigateBack = { navController.popBackStack() })
|
otherDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
vaultSettingsDestination(onNavigateBack = { navController.popBackStack() })
|
vaultSettingsDestination(
|
||||||
|
onNavigateBack = { navController.popBackStack() },
|
||||||
|
onNavigateToFolders = onNavigateToFolders,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigate to the settings screen screen.
|
* Navigate to the settings screen.
|
||||||
*/
|
*/
|
||||||
fun NavController.navigateToSettingsGraph(navOptions: NavOptions? = null) {
|
fun NavController.navigateToSettingsGraph(navOptions: NavOptions? = null) {
|
||||||
navigate(SETTINGS_GRAPH_ROUTE, navOptions)
|
navigate(SETTINGS_GRAPH_ROUTE, navOptions)
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||||
|
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||||
|
|
||||||
|
private const val FOLDERS_ROUTE = "settings_folders"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add folders destinations to the nav graph.
|
||||||
|
*/
|
||||||
|
fun NavGraphBuilder.foldersDestination(
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
composableWithSlideTransitions(
|
||||||
|
route = FOLDERS_ROUTE,
|
||||||
|
) {
|
||||||
|
FoldersScreen(
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the folders screen.
|
||||||
|
*/
|
||||||
|
fun NavController.navigateToFolders(navOptions: NavOptions? = null) {
|
||||||
|
navigate(FOLDERS_ROUTE, navOptions)
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
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.components.BitwardenScaffold
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the folders screen.
|
||||||
|
*/
|
||||||
|
@Suppress("LongMethod")
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun FoldersScreen(
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
viewModel: FoldersViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
|
when (event) {
|
||||||
|
FoldersEvent.NavigateBack -> onNavigateBack()
|
||||||
|
is FoldersEvent.ShowToast -> {
|
||||||
|
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
BitwardenScaffold(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
BitwardenTopAppBar(
|
||||||
|
title = stringResource(id = R.string.folders),
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
navigationIcon = painterResource(id = R.drawable.ic_close),
|
||||||
|
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||||
|
onNavigationIconClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(FoldersAction.CloseButtonClick) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(FoldersAction.AddFolderButtonClick) }
|
||||||
|
},
|
||||||
|
modifier = Modifier.navigationBarsPadding(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_plus),
|
||||||
|
contentDescription = stringResource(id = R.string.add_item),
|
||||||
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
// TODO BIT-460 populate Folders screen
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.no_folders_to_list),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(
|
||||||
|
vertical = 4.dp,
|
||||||
|
horizontal = 16.dp,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||||
|
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for the folders screen.
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class FoldersViewModel @Inject constructor() :
|
||||||
|
BaseViewModel<Unit, FoldersEvent, FoldersAction>(
|
||||||
|
initialState = Unit,
|
||||||
|
) {
|
||||||
|
override fun handleAction(action: FoldersAction): Unit = when (action) {
|
||||||
|
FoldersAction.AddFolderButtonClick -> handleAddFolderButtonClicked()
|
||||||
|
FoldersAction.CloseButtonClick -> handleCloseButtonClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleAddFolderButtonClicked() {
|
||||||
|
// TODO BIT-458 implement add folders
|
||||||
|
sendEvent(FoldersEvent.ShowToast("Not yet implemented."))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCloseButtonClicked() {
|
||||||
|
sendEvent(FoldersEvent.NavigateBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models events for the folders screen.
|
||||||
|
*/
|
||||||
|
sealed class FoldersEvent {
|
||||||
|
/**
|
||||||
|
* Navigates back to the previous screen.
|
||||||
|
*/
|
||||||
|
data object NavigateBack : FoldersEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a toast with the given [message].
|
||||||
|
*/
|
||||||
|
data class ShowToast(
|
||||||
|
val message: String,
|
||||||
|
) : FoldersEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models actions for the folders screen.
|
||||||
|
*/
|
||||||
|
sealed class FoldersAction {
|
||||||
|
/**
|
||||||
|
* Indicates that the user clicked the add folder button.
|
||||||
|
*/
|
||||||
|
data object AddFolderButtonClick : FoldersAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the user clicked the close button.
|
||||||
|
*/
|
||||||
|
data object CloseButtonClick : FoldersAction()
|
||||||
|
}
|
|
@ -12,11 +12,15 @@ private const val VAULT_SETTINGS_ROUTE = "vault_settings"
|
||||||
*/
|
*/
|
||||||
fun NavGraphBuilder.vaultSettingsDestination(
|
fun NavGraphBuilder.vaultSettingsDestination(
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToFolders: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composableWithPushTransitions(
|
composableWithPushTransitions(
|
||||||
route = VAULT_SETTINGS_ROUTE,
|
route = VAULT_SETTINGS_ROUTE,
|
||||||
) {
|
) {
|
||||||
VaultSettingsScreen(onNavigateBack = onNavigateBack)
|
VaultSettingsScreen(
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
onNavigateToFolders = onNavigateToFolders,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
@ -12,26 +14,36 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
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.BitwardenExternalLinkRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the vault screen.
|
* Displays the vault settings screen.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("LongMethod")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun VaultSettingsScreen(
|
fun VaultSettingsScreen(
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToFolders: () -> Unit,
|
||||||
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
EventsEffect(viewModel = viewModel) { event ->
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
VaultSettingsEvent.NavigateBack -> onNavigateBack.invoke()
|
VaultSettingsEvent.NavigateBack -> onNavigateBack()
|
||||||
|
VaultSettingsEvent.NavigateToFolders -> onNavigateToFolders()
|
||||||
|
is VaultSettingsEvent.ShowToast -> {
|
||||||
|
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +70,34 @@ fun VaultSettingsScreen(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
// TODO: BIT-928 Display Vault UI
|
BitwardenTextRow(
|
||||||
|
text = stringResource(R.string.folders),
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(VaultSettingsAction.FoldersButtonClick) }
|
||||||
|
},
|
||||||
|
withDivider = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
|
||||||
|
BitwardenTextRow(
|
||||||
|
text = stringResource(R.string.export_vault),
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(VaultSettingsAction.ExportVaultClick) }
|
||||||
|
},
|
||||||
|
withDivider = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
|
||||||
|
BitwardenExternalLinkRow(
|
||||||
|
text = stringResource(R.string.import_items),
|
||||||
|
onConfirmClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||||
|
},
|
||||||
|
withDivider = true,
|
||||||
|
dialogTitle = stringResource(id = R.string.import_items_confirmation),
|
||||||
|
dialogMessage = stringResource(id = R.string.import_items_description),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,28 @@ class VaultSettingsViewModel @Inject constructor() :
|
||||||
initialState = Unit,
|
initialState = Unit,
|
||||||
) {
|
) {
|
||||||
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
|
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
|
||||||
VaultSettingsAction.BackClick -> sendEvent(VaultSettingsEvent.NavigateBack)
|
VaultSettingsAction.BackClick -> handleBackClicked()
|
||||||
|
VaultSettingsAction.ExportVaultClick -> handleExportVaultClicked()
|
||||||
|
VaultSettingsAction.FoldersButtonClick -> handleFoldersButtonClicked()
|
||||||
|
VaultSettingsAction.ImportItemsClick -> handleImportItemsClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBackClicked() {
|
||||||
|
sendEvent(VaultSettingsEvent.NavigateBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleExportVaultClicked() {
|
||||||
|
// TODO BIT-1272 go to vault export screen
|
||||||
|
sendEvent(VaultSettingsEvent.ShowToast("Not yet implemented."))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleFoldersButtonClicked() {
|
||||||
|
sendEvent(VaultSettingsEvent.NavigateToFolders)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleImportItemsClicked() {
|
||||||
|
// TODO BIT-972 implement import items functionality
|
||||||
|
sendEvent(VaultSettingsEvent.ShowToast("Not yet implemented."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +46,18 @@ sealed class VaultSettingsEvent {
|
||||||
* Navigate back.
|
* Navigate back.
|
||||||
*/
|
*/
|
||||||
data object NavigateBack : VaultSettingsEvent()
|
data object NavigateBack : VaultSettingsEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the Folders screen.
|
||||||
|
*/
|
||||||
|
data object NavigateToFolders : VaultSettingsEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a toast with the given [message].
|
||||||
|
*/
|
||||||
|
data class ShowToast(
|
||||||
|
val message: String,
|
||||||
|
) : VaultSettingsEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,4 +68,19 @@ sealed class VaultSettingsAction {
|
||||||
* User clicked back button.
|
* User clicked back button.
|
||||||
*/
|
*/
|
||||||
data object BackClick : VaultSettingsAction()
|
data object BackClick : VaultSettingsAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the user clicked the Export Vault button.
|
||||||
|
*/
|
||||||
|
data object ExportVaultClick : VaultSettingsAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the user clicked the Folders button.
|
||||||
|
*/
|
||||||
|
data object FoldersButtonClick : VaultSettingsAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the user clicked the Import Items button.
|
||||||
|
*/
|
||||||
|
data object ImportItemsClick : VaultSettingsAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.navigation
|
import androidx.navigation.navigation
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.deleteAccountDestination
|
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.deleteAccountDestination
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.navigateToDeleteAccount
|
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.navigateToDeleteAccount
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.folders.foldersDestination
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.folders.navigateToFolders
|
||||||
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
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.passwordhistory.navigateToPasswordHistory
|
import com.x8bit.bitwarden.ui.tools.feature.generator.passwordhistory.navigateToPasswordHistory
|
||||||
|
@ -38,6 +40,7 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||||
route = VAULT_UNLOCKED_GRAPH_ROUTE,
|
route = VAULT_UNLOCKED_GRAPH_ROUTE,
|
||||||
) {
|
) {
|
||||||
vaultUnlockedNavBarDestination(
|
vaultUnlockedNavBarDestination(
|
||||||
|
onNavigateToFolders = { navController.navigateToFolders() },
|
||||||
onNavigateToVaultAddItem = {
|
onNavigateToVaultAddItem = {
|
||||||
navController.navigateToVaultAddEditItem(VaultAddEditType.AddItem)
|
navController.navigateToVaultAddEditItem(VaultAddEditType.AddItem)
|
||||||
},
|
},
|
||||||
|
@ -59,5 +62,6 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||||
)
|
)
|
||||||
newSendDestination(onNavigateBack = { navController.popBackStack() })
|
newSendDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
passwordHistoryDestination(onNavigateBack = { navController.popBackStack() })
|
passwordHistoryDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
|
foldersDestination(onNavigateBack = { navController.popBackStack() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
||||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||||
onNavigateToNewSend: () -> Unit,
|
onNavigateToNewSend: () -> Unit,
|
||||||
onNavigateToDeleteAccount: () -> Unit,
|
onNavigateToDeleteAccount: () -> Unit,
|
||||||
|
onNavigateToFolders: () -> Unit,
|
||||||
onNavigateToPasswordHistory: () -> Unit,
|
onNavigateToPasswordHistory: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composableWithStayTransitions(
|
composableWithStayTransitions(
|
||||||
|
@ -38,6 +39,7 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
||||||
onNavigateToVaultEditItem = onNavigateToVaultEditItem,
|
onNavigateToVaultEditItem = onNavigateToVaultEditItem,
|
||||||
onNavigateToNewSend = onNavigateToNewSend,
|
onNavigateToNewSend = onNavigateToNewSend,
|
||||||
onNavigateToDeleteAccount = onNavigateToDeleteAccount,
|
onNavigateToDeleteAccount = onNavigateToDeleteAccount,
|
||||||
|
onNavigateToFolders = onNavigateToFolders,
|
||||||
onNavigateToPasswordHistory = onNavigateToPasswordHistory,
|
onNavigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
/**
|
/**
|
||||||
* Top level composable for the Vault Unlocked Screen.
|
* Top level composable for the Vault Unlocked Screen.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("LongParameterList")
|
||||||
@Composable
|
@Composable
|
||||||
fun VaultUnlockedNavBarScreen(
|
fun VaultUnlockedNavBarScreen(
|
||||||
viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(),
|
viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(),
|
||||||
|
@ -71,6 +72,7 @@ fun VaultUnlockedNavBarScreen(
|
||||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||||
onNavigateToNewSend: () -> Unit,
|
onNavigateToNewSend: () -> Unit,
|
||||||
onNavigateToDeleteAccount: () -> Unit,
|
onNavigateToDeleteAccount: () -> Unit,
|
||||||
|
onNavigateToFolders: () -> Unit,
|
||||||
onNavigateToPasswordHistory: () -> Unit,
|
onNavigateToPasswordHistory: () -> Unit,
|
||||||
) {
|
) {
|
||||||
EventsEffect(viewModel = viewModel) { event ->
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
|
@ -102,6 +104,7 @@ fun VaultUnlockedNavBarScreen(
|
||||||
navigateToVaultAddItem = onNavigateToVaultAddItem,
|
navigateToVaultAddItem = onNavigateToVaultAddItem,
|
||||||
navigateToNewSend = onNavigateToNewSend,
|
navigateToNewSend = onNavigateToNewSend,
|
||||||
navigateToDeleteAccount = onNavigateToDeleteAccount,
|
navigateToDeleteAccount = onNavigateToDeleteAccount,
|
||||||
|
navigateToFolders = onNavigateToFolders,
|
||||||
navigateToPasswordHistory = onNavigateToPasswordHistory,
|
navigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||||
generatorTabClickedAction = {
|
generatorTabClickedAction = {
|
||||||
viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick)
|
viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick)
|
||||||
|
@ -134,6 +137,7 @@ private fun VaultUnlockedNavBarScaffold(
|
||||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||||
navigateToNewSend: () -> Unit,
|
navigateToNewSend: () -> Unit,
|
||||||
navigateToDeleteAccount: () -> Unit,
|
navigateToDeleteAccount: () -> Unit,
|
||||||
|
navigateToFolders: () -> Unit,
|
||||||
navigateToPasswordHistory: () -> Unit,
|
navigateToPasswordHistory: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var shouldDimNavBar by remember { mutableStateOf(false) }
|
var shouldDimNavBar by remember { mutableStateOf(false) }
|
||||||
|
@ -200,6 +204,7 @@ private fun VaultUnlockedNavBarScaffold(
|
||||||
settingsGraph(
|
settingsGraph(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onNavigateToDeleteAccount = navigateToDeleteAccount,
|
onNavigateToDeleteAccount = navigateToDeleteAccount,
|
||||||
|
onNavigateToFolders = navigateToFolders,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
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.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FoldersScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
|
private var onNavigateBackCalled = false
|
||||||
|
private val mutableEventFlow = MutableSharedFlow<FoldersEvent>(
|
||||||
|
extraBufferCapacity = Int.MAX_VALUE,
|
||||||
|
)
|
||||||
|
private val mutableStateFlow = MutableStateFlow(Unit)
|
||||||
|
val viewModel = mockk<FoldersViewModel>(relaxed = true) {
|
||||||
|
every { eventFlow } returns mutableEventFlow
|
||||||
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
composeTestRule.setContent {
|
||||||
|
FoldersScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `close button click should send CloseButtonClick`() {
|
||||||
|
composeTestRule.onNodeWithContentDescription("Close").performClick()
|
||||||
|
verify { viewModel.trySendAction(FoldersAction.CloseButtonClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `add folder button click should send AddFolderButtonClick`() {
|
||||||
|
composeTestRule.onNodeWithContentDescription("Add item").performClick()
|
||||||
|
verify {
|
||||||
|
viewModel.trySendAction(FoldersAction.AddFolderButtonClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `NavigateBack should call onNavigateBack`() {
|
||||||
|
mutableEventFlow.tryEmit(FoldersEvent.NavigateBack)
|
||||||
|
assertTrue(onNavigateBackCalled)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||||
|
|
||||||
|
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 FoldersViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `BackClick should emit NavigateBack`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(FoldersAction.CloseButtonClick)
|
||||||
|
assertEquals(FoldersEvent.NavigateBack, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `AddFolderButtonClick should emit ShowToast`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(FoldersAction.AddFolderButtonClick)
|
||||||
|
assertEquals(
|
||||||
|
FoldersEvent.ShowToast("Not yet implemented."),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createViewModel(): FoldersViewModel = FoldersViewModel()
|
||||||
|
}
|
|
@ -1,46 +1,98 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.assert
|
||||||
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
|
import androidx.compose.ui.test.hasAnyAncestor
|
||||||
|
import androidx.compose.ui.test.isDialog
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class VaultSettingsScreenTest : BaseComposeTest() {
|
class VaultSettingsScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
@Test
|
private var onNavigateBackCalled = false
|
||||||
fun `on back click should send BackClick`() {
|
private var onNavigateToFoldersCalled = false
|
||||||
val viewModel: VaultSettingsViewModel = mockk {
|
private val mutableEventFlow = MutableSharedFlow<VaultSettingsEvent>(
|
||||||
every { eventFlow } returns emptyFlow()
|
extraBufferCapacity = Int.MAX_VALUE,
|
||||||
every { trySendAction(VaultSettingsAction.BackClick) } returns Unit
|
)
|
||||||
}
|
private val mutableStateFlow = MutableStateFlow(Unit)
|
||||||
|
val viewModel = mockk<VaultSettingsViewModel>(relaxed = true) {
|
||||||
|
every { eventFlow } returns mutableEventFlow
|
||||||
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
VaultSettingsScreen(
|
VaultSettingsScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onNavigateBack = { },
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
|
onNavigateToFolders = { onNavigateToFoldersCalled = true },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on back click should send BackClick`() {
|
||||||
|
every { viewModel.trySendAction(VaultSettingsAction.BackClick) } returns Unit
|
||||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||||
verify { viewModel.trySendAction(VaultSettingsAction.BackClick) }
|
verify { viewModel.trySendAction(VaultSettingsAction.BackClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on NavigateAbout should call onNavigateToVault`() {
|
fun `export vault click should send ExportVaultClick`() {
|
||||||
var haveCalledNavigateBack = false
|
composeTestRule.onNodeWithText("Export vault").performClick()
|
||||||
val viewModel = mockk<VaultSettingsViewModel> {
|
verify {
|
||||||
every { eventFlow } returns flowOf(VaultSettingsEvent.NavigateBack)
|
viewModel.trySendAction(VaultSettingsAction.ExportVaultClick)
|
||||||
}
|
}
|
||||||
composeTestRule.setContent {
|
}
|
||||||
VaultSettingsScreen(
|
|
||||||
viewModel = viewModel,
|
@Test
|
||||||
onNavigateBack = { haveCalledNavigateBack = true },
|
fun `import items click should display dialog and confirming should send ImportItemsClick`() {
|
||||||
)
|
composeTestRule.onNodeWithText("Import items").performClick()
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("Continue")
|
||||||
|
.assert(hasAnyAncestor(isDialog()))
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||||
}
|
}
|
||||||
assertTrue(haveCalledNavigateBack)
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `import items click should display dialog & canceling should not send ImportItemsClick`() {
|
||||||
|
composeTestRule.onNodeWithText("Import items").performClick()
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("Cancel")
|
||||||
|
.assert(hasAnyAncestor(isDialog()))
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify(exactly = 0) {
|
||||||
|
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `NavigateBack should call onNavigateBack`() {
|
||||||
|
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateBack)
|
||||||
|
assertTrue(onNavigateBackCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `NavigateToFolders should call onNavigateToFolders`() {
|
||||||
|
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToFolders)
|
||||||
|
assertTrue(onNavigateToFoldersCalled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,37 @@ import org.junit.jupiter.api.Test
|
||||||
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
fun `BackClick should emit NavigateBack`() = runTest {
|
||||||
val viewModel = VaultSettingsViewModel()
|
val viewModel = createViewModel()
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(VaultSettingsAction.BackClick)
|
viewModel.trySendAction(VaultSettingsAction.BackClick)
|
||||||
assertEquals(VaultSettingsEvent.NavigateBack, awaitItem())
|
assertEquals(VaultSettingsEvent.NavigateBack, awaitItem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ExportVaultClick should emit ShowToast`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(VaultSettingsAction.ExportVaultClick)
|
||||||
|
assertEquals(
|
||||||
|
VaultSettingsEvent.ShowToast("Not yet implemented."),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ImportItemsClick should emit ShowToast`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||||
|
assertEquals(
|
||||||
|
VaultSettingsEvent.ShowToast("Not yet implemented."),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,20 @@ import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
private val fakeNavHostController = FakeNavHostController()
|
private val fakeNavHostController = FakeNavHostController()
|
||||||
|
private val mutableEventFlow = MutableSharedFlow<VaultUnlockedNavBarEvent>(
|
||||||
|
extraBufferCapacity = Int.MAX_VALUE,
|
||||||
|
)
|
||||||
|
private val mutableStateFlow = MutableStateFlow(Unit)
|
||||||
|
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true) {
|
||||||
|
every { eventFlow } returns mutableEventFlow
|
||||||
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
}
|
||||||
|
|
||||||
private val expectedNavOptions = navOptions {
|
private val expectedNavOptions = navOptions {
|
||||||
// When changing root navigation state, pop everything else off the back stack:
|
// When changing root navigation state, pop everything else off the back stack:
|
||||||
|
@ -24,9 +34,8 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
restoreState = true
|
restoreState = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Before
|
||||||
fun `vault tab click should send VaultTabClick action`() {
|
fun setup() {
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true)
|
|
||||||
composeTestRule.apply {
|
composeTestRule.apply {
|
||||||
setContent {
|
setContent {
|
||||||
VaultUnlockedNavBarScreen(
|
VaultUnlockedNavBarScreen(
|
||||||
|
@ -37,90 +46,42 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
onNavigateToVaultEditItem = {},
|
onNavigateToVaultEditItem = {},
|
||||||
onNavigateToNewSend = {},
|
onNavigateToNewSend = {},
|
||||||
onNavigateToDeleteAccount = {},
|
onNavigateToDeleteAccount = {},
|
||||||
|
onNavigateToFolders = {},
|
||||||
onNavigateToPasswordHistory = {},
|
onNavigateToPasswordHistory = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onNodeWithText("My vault").performClick()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `vault tab click should send VaultTabClick action`() {
|
||||||
|
composeTestRule.onNodeWithText("My vault").performClick()
|
||||||
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.VaultTabClick) }
|
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.VaultTabClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `NavigateToVaultScreen should navigate to VaultScreen`() {
|
fun `NavigateToVaultScreen should navigate to VaultScreen`() {
|
||||||
val vaultUnlockedNavBarEventFlow = MutableSharedFlow<VaultUnlockedNavBarEvent>(
|
composeTestRule.runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
||||||
extraBufferCapacity = Int.MAX_VALUE,
|
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToVaultScreen)
|
||||||
)
|
composeTestRule.runOnIdle {
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true) {
|
fakeNavHostController.assertLastNavigation(
|
||||||
every { eventFlow } returns vaultUnlockedNavBarEventFlow
|
route = "vault_graph",
|
||||||
}
|
navOptions = expectedNavOptions,
|
||||||
composeTestRule.apply {
|
)
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
|
||||||
vaultUnlockedNavBarEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToVaultScreen)
|
|
||||||
runOnIdle {
|
|
||||||
fakeNavHostController.assertLastNavigation(
|
|
||||||
route = "vault_graph",
|
|
||||||
navOptions = expectedNavOptions,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `send tab click should send SendTabClick action`() {
|
fun `send tab click should send SendTabClick action`() {
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true)
|
composeTestRule.onNodeWithText("Send").performClick()
|
||||||
composeTestRule.apply {
|
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onNodeWithText("Send").performClick()
|
|
||||||
}
|
|
||||||
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.SendTabClick) }
|
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.SendTabClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `NavigateToSendScreen should navigate to SendScreen`() {
|
fun `NavigateToSendScreen should navigate to SendScreen`() {
|
||||||
val vaultUnlockedNavBarEventFlow = MutableSharedFlow<VaultUnlockedNavBarEvent>(
|
|
||||||
extraBufferCapacity = Int.MAX_VALUE,
|
|
||||||
)
|
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true) {
|
|
||||||
every { eventFlow } returns vaultUnlockedNavBarEventFlow
|
|
||||||
}
|
|
||||||
composeTestRule.apply {
|
composeTestRule.apply {
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
||||||
vaultUnlockedNavBarEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToSendScreen)
|
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToSendScreen)
|
||||||
runOnIdle {
|
runOnIdle {
|
||||||
fakeNavHostController.assertLastNavigation(
|
fakeNavHostController.assertLastNavigation(
|
||||||
route = "send",
|
route = "send",
|
||||||
|
@ -132,48 +93,15 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `generator tab click should send GeneratorTabClick action`() {
|
fun `generator tab click should send GeneratorTabClick action`() {
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true)
|
composeTestRule.onNodeWithText("Generator").performClick()
|
||||||
composeTestRule.apply {
|
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onNodeWithText("Generator").performClick()
|
|
||||||
}
|
|
||||||
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick) }
|
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `NavigateToGeneratorScreen should navigate to GeneratorScreen`() {
|
fun `NavigateToGeneratorScreen should navigate to GeneratorScreen`() {
|
||||||
val vaultUnlockedNavBarEventFlow = MutableSharedFlow<VaultUnlockedNavBarEvent>(
|
|
||||||
extraBufferCapacity = Int.MAX_VALUE,
|
|
||||||
)
|
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true) {
|
|
||||||
every { eventFlow } returns vaultUnlockedNavBarEventFlow
|
|
||||||
}
|
|
||||||
composeTestRule.apply {
|
composeTestRule.apply {
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
||||||
vaultUnlockedNavBarEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen)
|
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen)
|
||||||
runOnIdle {
|
runOnIdle {
|
||||||
fakeNavHostController.assertLastNavigation(
|
fakeNavHostController.assertLastNavigation(
|
||||||
route = "generator",
|
route = "generator",
|
||||||
|
@ -185,48 +113,15 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `settings tab click should send SendTabClick action`() {
|
fun `settings tab click should send SendTabClick action`() {
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true)
|
composeTestRule.onNodeWithText("Settings").performClick()
|
||||||
composeTestRule.apply {
|
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onNodeWithText("Settings").performClick()
|
|
||||||
}
|
|
||||||
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.SettingsTabClick) }
|
verify { viewModel.trySendAction(VaultUnlockedNavBarAction.SettingsTabClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `NavigateToSettingsScreen should navigate to SettingsScreen`() {
|
fun `NavigateToSettingsScreen should navigate to SettingsScreen`() {
|
||||||
val vaultUnlockedNavBarEventFlow = MutableSharedFlow<VaultUnlockedNavBarEvent>(
|
|
||||||
extraBufferCapacity = Int.MAX_VALUE,
|
|
||||||
)
|
|
||||||
val viewModel = mockk<VaultUnlockedNavBarViewModel>(relaxed = true) {
|
|
||||||
every { eventFlow } returns vaultUnlockedNavBarEventFlow
|
|
||||||
}
|
|
||||||
composeTestRule.apply {
|
composeTestRule.apply {
|
||||||
setContent {
|
|
||||||
VaultUnlockedNavBarScreen(
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = fakeNavHostController,
|
|
||||||
onNavigateToVaultAddItem = {},
|
|
||||||
onNavigateToVaultItem = {},
|
|
||||||
onNavigateToVaultEditItem = {},
|
|
||||||
onNavigateToNewSend = {},
|
|
||||||
onNavigateToDeleteAccount = {},
|
|
||||||
onNavigateToPasswordHistory = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
||||||
vaultUnlockedNavBarEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToSettingsScreen)
|
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToSettingsScreen)
|
||||||
runOnIdle {
|
runOnIdle {
|
||||||
fakeNavHostController.assertLastNavigation(
|
fakeNavHostController.assertLastNavigation(
|
||||||
route = "settings_graph",
|
route = "settings_graph",
|
||||||
|
|
Loading…
Reference in a new issue