diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlocked/VaultUnlockedNavigation.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlocked/VaultUnlockedNavigation.kt index d768145b7..142f428b2 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlocked/VaultUnlockedNavigation.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlocked/VaultUnlockedNavigation.kt @@ -10,6 +10,8 @@ import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.VAULT_UNLOCKE import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.vaultUnlockedNavBarDestination import com.x8bit.bitwarden.ui.tools.feature.send.navigateToNewSend import com.x8bit.bitwarden.ui.tools.feature.send.newSendDestination +import com.x8bit.bitwarden.ui.vault.feature.vault.item.navigateToVaultItem +import com.x8bit.bitwarden.ui.vault.feature.vault.item.vaultItemDestination import com.x8bit.bitwarden.ui.vault.feature.vault.navigateToVaultAddItem import com.x8bit.bitwarden.ui.vault.feature.vault.vaultAddItemDestination @@ -34,11 +36,13 @@ fun NavGraphBuilder.vaultUnlockedGraph( ) { vaultUnlockedNavBarDestination( onNavigateToVaultAddItem = { navController.navigateToVaultAddItem() }, + onNavigateToVaultItem = { navController.navigateToVaultItem(it) }, onNavigateToNewSend = { navController.navigateToNewSend() }, onNavigateToDeleteAccount = { navController.navigateToDeleteAccount() }, ) deleteAccountDestination(onNavigateBack = { navController.popBackStack() }) vaultAddItemDestination(onNavigateBack = { navController.popBackStack() }) + vaultItemDestination(onNavigateBack = { navController.popBackStack() }) newSendDestination(onNavigateBack = { navController.popBackStack() }) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarNavigation.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarNavigation.kt index 7a40e1ef7..2dad4eaa4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarNavigation.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarNavigation.kt @@ -23,6 +23,7 @@ fun NavController.navigateToVaultUnlockedNavBar(navOptions: NavOptions? = null) */ fun NavGraphBuilder.vaultUnlockedNavBarDestination( onNavigateToVaultAddItem: () -> Unit, + onNavigateToVaultItem: (vaultItemId: String) -> Unit, onNavigateToNewSend: () -> Unit, onNavigateToDeleteAccount: () -> Unit, ) { @@ -35,6 +36,7 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination( ) { VaultUnlockedNavBarScreen( onNavigateToVaultAddItem = onNavigateToVaultAddItem, + onNavigateToVaultItem = onNavigateToVaultItem, onNavigateToNewSend = onNavigateToNewSend, onNavigateToDeleteAccount = onNavigateToDeleteAccount, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt index 2cf11d612..7b7903074 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt @@ -66,6 +66,7 @@ fun VaultUnlockedNavBarScreen( viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(), navController: NavHostController = rememberNavController(), onNavigateToVaultAddItem: () -> Unit, + onNavigateToVaultItem: (vaultItemId: String) -> Unit, onNavigateToNewSend: () -> Unit, onNavigateToDeleteAccount: () -> Unit, ) { @@ -93,6 +94,7 @@ fun VaultUnlockedNavBarScreen( } VaultUnlockedNavBarScaffold( navController = navController, + onNavigateToVaultItem = onNavigateToVaultItem, navigateToVaultAddItem = onNavigateToVaultAddItem, navigateToNewSend = onNavigateToNewSend, navigateToDeleteAccount = onNavigateToDeleteAccount, @@ -123,6 +125,7 @@ private fun VaultUnlockedNavBarScaffold( generatorTabClickedAction: () -> Unit, settingsTabClickedAction: () -> Unit, navigateToVaultAddItem: () -> Unit, + onNavigateToVaultItem: (vaultItemId: String) -> Unit, navigateToNewSend: () -> Unit, navigateToDeleteAccount: () -> Unit, ) { @@ -178,6 +181,7 @@ private fun VaultUnlockedNavBarScaffold( onNavigateToVaultAddItemScreen = { navigateToVaultAddItem() }, + onNavigateToVaultItemScreen = onNavigateToVaultItem, onDimBottomNavBarRequest = { shouldDim -> shouldDimNavBar = shouldDim }, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultNavigation.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultNavigation.kt index a697a9aba..7e595a93a 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultNavigation.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultNavigation.kt @@ -12,11 +12,13 @@ const val VAULT_ROUTE: String = "vault" */ fun NavGraphBuilder.vaultDestination( onNavigateToVaultAddItemScreen: () -> Unit, + onNavigateToVaultItemScreen: (vaultItemId: String) -> Unit, onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit, ) { composable(VAULT_ROUTE) { VaultScreen( onNavigateToVaultAddItemScreen = onNavigateToVaultAddItemScreen, + onNavigateToVaultItemScreen = onNavigateToVaultItemScreen, onDimBottomNavBarRequest = onDimBottomNavBarRequest, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt index 8ef0068f8..355569ede 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt @@ -45,6 +45,7 @@ import kotlinx.collections.immutable.toImmutableList fun VaultScreen( viewModel: VaultViewModel = hiltViewModel(), onNavigateToVaultAddItemScreen: () -> Unit, + onNavigateToVaultItemScreen: (vaultItemId: String) -> Unit, onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit, ) { val context = LocalContext.current @@ -52,6 +53,8 @@ fun VaultScreen( when (event) { VaultEvent.NavigateToAddItemScreen -> onNavigateToVaultAddItemScreen() + is VaultEvent.NavigateToItemScreen -> onNavigateToVaultItemScreen(event.vaultItemId) + VaultEvent.NavigateToVaultSearchScreen -> { // TODO Create vault search screen and navigation implementation BIT-213 Toast diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index 95c3c17bb..6bbf73a80 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -153,6 +153,7 @@ class VaultViewModel @Inject constructor( is DataState.Pending -> vaultPendingReceive(vaultData = vaultData) } } + private fun vaultErrorReceive(vaultData: DataState.Error) { // TODO update state to error state BIT-1157 mutableStateFlow.update { it.copy(viewState = VaultState.ViewState.NoItems) } @@ -407,6 +408,13 @@ sealed class VaultEvent { */ data object NavigateToAddItemScreen : VaultEvent() + /** + * Navigate to the Vault Item screen. + */ + data class NavigateToItemScreen( + val vaultItemId: String, + ) : VaultEvent() + /** * Navigate to the item details screen. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemNavigation.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemNavigation.kt new file mode 100644 index 000000000..57ef1c67a --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemNavigation.kt @@ -0,0 +1,53 @@ +package com.x8bit.bitwarden.ui.vault.feature.vault.item + +import androidx.lifecycle.SavedStateHandle +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.NavType +import androidx.navigation.compose.composable +import androidx.navigation.navArgument +import com.x8bit.bitwarden.ui.platform.theme.TransitionProviders + +private const val VAULT_ITEM_PREFIX = "vault_item" +private const val VAULT_ITEM_ID = "vault_item_id" +private const val VAULT_ITEM_ROUTE = "$VAULT_ITEM_PREFIX/{$VAULT_ITEM_ID}" + +/** + * Class to retrieve vault item arguments from the [SavedStateHandle]. + */ +class VaultItemArgs(val vaultItemId: String) { + constructor(savedStateHandle: SavedStateHandle) : this( + checkNotNull(savedStateHandle[VAULT_ITEM_ID]) as String, + ) +} + +/** + * Add the vault item screen to the nav graph. + */ +fun NavGraphBuilder.vaultItemDestination( + onNavigateBack: () -> Unit, +) { + composable( + route = VAULT_ITEM_ROUTE, + arguments = listOf( + navArgument(VAULT_ITEM_ID) { type = NavType.StringType }, + ), + enterTransition = TransitionProviders.Enter.slideUp, + exitTransition = TransitionProviders.Exit.slideDown, + popEnterTransition = TransitionProviders.Enter.slideUp, + popExitTransition = TransitionProviders.Exit.slideDown, + ) { + VaultItemScreen(onNavigateBack = onNavigateBack) + } +} + +/** + * Navigate to the vault item screen. + */ +fun NavController.navigateToVaultItem( + vaultItemId: String, + navOptions: NavOptions? = null, +) { + navigate("$VAULT_ITEM_PREFIX/$vaultItemId", navOptions) +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreen.kt new file mode 100644 index 000000000..2e080cb99 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreen.kt @@ -0,0 +1,72 @@ +package com.x8bit.bitwarden.ui.vault.feature.vault.item + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +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.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +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 vault item screen. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VaultItemScreen( + viewModel: VaultItemViewModel = hiltViewModel(), + onNavigateBack: () -> Unit, +) { + val state by viewModel.stateFlow.collectAsStateWithLifecycle() + EventsEffect(viewModel = viewModel) { event -> + when (event) { + VaultItemEvent.NavigateBack -> onNavigateBack() + } + } + + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + BitwardenScaffold( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + BitwardenTopAppBar( + title = stringResource(id = R.string.view_item), + scrollBehavior = scrollBehavior, + navigationIcon = painterResource(id = R.drawable.ic_close), + navigationIconContentDescription = stringResource(id = R.string.close), + onNavigationIconClick = remember(viewModel) { + { viewModel.trySendAction(VaultItemAction.CloseClick) } + }, + ) + }, + ) { innerPadding -> + Column( + modifier = Modifier + .imePadding() + .fillMaxSize() + .padding(innerPadding) + .verticalScroll(rememberScrollState()), + ) { + + Spacer(modifier = Modifier.navigationBarsPadding()) + } + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModel.kt new file mode 100644 index 000000000..3daa60e85 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModel.kt @@ -0,0 +1,68 @@ +package com.x8bit.bitwarden.ui.vault.feature.vault.item + +import android.os.Parcelable +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.x8bit.bitwarden.ui.platform.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.parcelize.Parcelize +import javax.inject.Inject + +private const val KEY_STATE = "state" + +/** + * ViewModel responsible for handling user interactions in the vault item screen + */ +@HiltViewModel +class VaultItemViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, +) : BaseViewModel( + initialState = savedStateHandle[KEY_STATE] ?: VaultItemState( + vaultItemId = VaultItemArgs(savedStateHandle).vaultItemId, + ), +) { + + init { + stateFlow.onEach { savedStateHandle[KEY_STATE] = it }.launchIn(viewModelScope) + } + + override fun handleAction(action: VaultItemAction) { + when (action) { + VaultItemAction.CloseClick -> handleCloseClick() + } + } + + private fun handleCloseClick() { + sendEvent(VaultItemEvent.NavigateBack) + } +} + +/** + * Represents the state for viewing an item in the vault. + */ +@Parcelize +data class VaultItemState( + val vaultItemId: String, +) : Parcelable + +/** + * Represents a set of events related view a vault item. + */ +sealed class VaultItemEvent { + /** + * Navigates back. + */ + data object NavigateBack : VaultItemEvent() +} + +/** + * Represents a set of actions related view a vault item. + */ +sealed class VaultItemAction { + /** + * The user has clicked the close button. + */ + data object CloseClick : VaultItemAction() +} diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreenTest.kt index 7c8da3050..683545ee5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreenTest.kt @@ -33,6 +33,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -56,6 +57,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -80,6 +82,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -103,6 +106,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -127,6 +131,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -150,6 +155,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -174,6 +180,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) @@ -197,6 +204,7 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() { viewModel = viewModel, navController = fakeNavHostController, onNavigateToVaultAddItem = {}, + onNavigateToVaultItem = {}, onNavigateToNewSend = {}, onNavigateToDeleteAccount = {}, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index 7ac1865ce..8497fb8d4 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -28,6 +28,7 @@ import org.junit.Test class VaultScreenTest : BaseComposeTest() { private var onNavigateToVaultAddItemScreenCalled = false + private var onNavigateToVaultItemScreenCalled = false private var onDimBottomNavBarRequestCalled = false private val mutableEventFlow = MutableSharedFlow( @@ -45,6 +46,7 @@ class VaultScreenTest : BaseComposeTest() { VaultScreen( viewModel = viewModel, onNavigateToVaultAddItemScreen = { onNavigateToVaultAddItemScreenCalled = true }, + onNavigateToVaultItemScreen = { onNavigateToVaultItemScreenCalled = true }, onDimBottomNavBarRequest = { onDimBottomNavBarRequestCalled = true }, ) } @@ -115,6 +117,12 @@ class VaultScreenTest : BaseComposeTest() { assertTrue(onNavigateToVaultAddItemScreenCalled) } + @Test + fun `NavigateToItemScreen event should call onNavigateToVaultItemScreenCalled`() { + mutableEventFlow.tryEmit(VaultEvent.NavigateToItemScreen(vaultItemId = "id")) + assertTrue(onNavigateToVaultItemScreenCalled) + } + @Test fun `clicking a favorite item should send VaultItemClick with the correct item`() { val itemText = "Test Item" diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreenTest.kt new file mode 100644 index 000000000..9fb5d327f --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemScreenTest.kt @@ -0,0 +1,51 @@ +package com.x8bit.bitwarden.ui.vault.feature.vault.item + +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.Before +import org.junit.Test + +class VaultItemScreenTest : BaseComposeTest() { + + private var onNavigateBackCalled = false + + private val mutableEventFlow = MutableSharedFlow( + extraBufferCapacity = Int.MAX_VALUE, + ) + private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE) + private val viewModel = mockk(relaxed = true) { + every { eventFlow } returns mutableEventFlow + every { stateFlow } returns mutableStateFlow + } + + @Before + fun setUp() { + composeTestRule.setContent { + VaultItemScreen( + viewModel = viewModel, + onNavigateBack = { onNavigateBackCalled = true }, + ) + } + } + + @Test + fun `clicking close button should send CloseClick action`() { + composeTestRule.onNodeWithContentDescription(label = "Close").performClick() + + verify { + viewModel.trySendAction(VaultItemAction.CloseClick) + } + } +} + +private const val VAULT_ITEM_ID = "vault_item_id" + +private val DEFAULT_STATE: VaultItemState = VaultItemState( + vaultItemId = VAULT_ITEM_ID, +) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModelTest.kt new file mode 100644 index 000000000..a7581a4f6 --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/item/VaultItemViewModelTest.kt @@ -0,0 +1,49 @@ +package com.x8bit.bitwarden.ui.vault.feature.vault.item + +import androidx.lifecycle.SavedStateHandle +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 VaultItemViewModelTest : BaseViewModelTest() { + + @Test + fun `initial state should be correct when not set`() { + val viewModel = createViewModel(state = null) + assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) + } + + @Test + fun `initial state should be correct when set`() { + val state = DEFAULT_STATE.copy(vaultItemId = "something_different") + val viewModel = createViewModel(state = state) + assertEquals(state, viewModel.stateFlow.value) + } + + @Test + fun `on BackClick should emit NavigateBack`() = runTest { + val viewModel = createViewModel() + viewModel.eventFlow.test { + viewModel.trySendAction(VaultItemAction.CloseClick) + assertEquals(VaultItemEvent.NavigateBack, awaitItem()) + } + } + + private fun createViewModel( + state: VaultItemState? = DEFAULT_STATE, + vaultItemId: String = VAULT_ITEM_ID, + ): VaultItemViewModel = VaultItemViewModel( + savedStateHandle = SavedStateHandle().apply { + set("state", state) + set("vault_item_id", vaultItemId) + }, + ) +} + +private const val VAULT_ITEM_ID = "vault_item_id" + +private val DEFAULT_STATE: VaultItemState = VaultItemState( + vaultItemId = VAULT_ITEM_ID, +)