mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 00:06:22 -06:00
added tests for archive
This commit is contained in:
parent
3935458842
commit
009ee11ef4
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.bitwarden.vault.CipherListView
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
|
||||
@ -24,16 +25,14 @@ class Fido2CredentialStoreImpl(
|
||||
/**
|
||||
* Return all active ciphers that contain FIDO 2 credentials.
|
||||
*/
|
||||
override suspend fun allCredentials(): List<CipherView> {
|
||||
override suspend fun allCredentials(): List<CipherListView> {
|
||||
val syncResult = vaultRepository.syncForResult()
|
||||
if (syncResult is SyncVaultDataResult.Error) {
|
||||
syncResult.throwable
|
||||
?.let { throw it }
|
||||
?: throw IllegalStateException("Sync failed.")
|
||||
}
|
||||
return vaultRepository.ciphersStateFlow.value.data
|
||||
?.filter { it.isActiveWithFido2Credentials }
|
||||
?: emptyList()
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -53,7 +53,9 @@ fun CipherView.determineListingPredicate(
|
||||
}
|
||||
|
||||
is VaultItemListingState.ItemListingType.Vault.Collection -> {
|
||||
itemListingType.collectionId in this.collectionIds && deletedDate == null
|
||||
itemListingType.collectionId in this.collectionIds &&
|
||||
deletedDate == null &&
|
||||
archivedDate == null
|
||||
}
|
||||
|
||||
is VaultItemListingState.ItemListingType.Vault.Folder -> {
|
||||
|
||||
@ -37,6 +37,7 @@ private val FIXED_CLOCK: Clock = Clock.fixed(
|
||||
* @param number the number to create the cipher with.
|
||||
* @param isDeleted whether or not the cipher has been deleted.
|
||||
* @param cipherType the type of cipher to create.
|
||||
* @param isArchived whether or not the cipher has been deleted.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
fun createMockCipherView(
|
||||
@ -50,6 +51,7 @@ fun createMockCipherView(
|
||||
clock: Clock = FIXED_CLOCK,
|
||||
fido2Credentials: List<Fido2Credential>? = null,
|
||||
sshKey: SshKeyView? = createMockSshKeyView(number = number),
|
||||
isArchived: Boolean = false,
|
||||
): CipherView =
|
||||
CipherView(
|
||||
id = "mockId-$number",
|
||||
@ -90,7 +92,11 @@ fun createMockCipherView(
|
||||
viewPassword = true,
|
||||
localData = null,
|
||||
permissions = null,
|
||||
archivedDate = null,
|
||||
archivedDate = if (isArchived) {
|
||||
clock.instant()
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@ -545,6 +545,11 @@ class SearchScreenTest : BaseComposeTest() {
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText(text = "Search mockName").assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(searchType = SearchTypeData.Vault.Archive)
|
||||
}
|
||||
composeTestRule.onNodeWithText(text = "Search Archive").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -1512,6 +1512,100 @@ class SearchViewModelTest : BaseViewModelTest() {
|
||||
assertTrue(viewModel.stateFlow.value.isIconLoadingDisabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Loaded with archived items should update ViewState to Empty`() =
|
||||
runTest {
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
|
||||
mutableVaultDataStateFlow.tryEmit(value = dataState)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = SearchState.ViewState.Empty(null),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Pending with archived data should update state to Empty`() =
|
||||
runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(
|
||||
createMockCipherView(
|
||||
number = 1,
|
||||
isArchived = true,
|
||||
),
|
||||
),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(viewState = SearchState.ViewState.Empty(message = null)),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Error with archived data should update state to Empty`() = runTest {
|
||||
val dataState = DataState.Error(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isArchived = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
error = IllegalStateException(),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
mutableVaultDataStateFlow.tryEmit(value = dataState)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = SearchState.ViewState.Empty(message = null),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow NoNetwork with archived data should update state to Empty`() = runTest {
|
||||
val dataState = DataState.NoNetwork(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isArchived = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
mutableVaultDataStateFlow.tryEmit(value = dataState)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = SearchState.ViewState.Empty(message = null),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun createViewModel(
|
||||
initialState: SearchState? = null,
|
||||
|
||||
@ -1878,6 +1878,312 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `menu Archive option should be displayed based on state`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_LOGIN_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(
|
||||
currentCipher =
|
||||
createMockCipherView(1).copy(
|
||||
collectionIds = emptyList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Confirm overflow is closed on initial load
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filter(hasAnyAncestor(isPopup()))
|
||||
.assertCountEquals(0)
|
||||
|
||||
// Open the overflow menu
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("More")
|
||||
.performClick()
|
||||
|
||||
// Confirm Archive option is present
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filterToOne(hasAnyAncestor(isPopup()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
// Confirm Archive option is not present when cipher has archivedDate
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_LOGIN_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher =
|
||||
createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filter(hasAnyAncestor(isPopup()))
|
||||
.assertCountEquals(0)
|
||||
|
||||
// Confirm Archive option is not present when cipher has deletedDate
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_LOGIN_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher =
|
||||
createMockCipherView(1).copy(
|
||||
deletedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filter(hasAnyAncestor(isPopup()))
|
||||
.assertCountEquals(0)
|
||||
|
||||
// Confirm Archive option is not present when cipher is in a Collection
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_LOGIN_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher =
|
||||
createMockCipherView(1).copy(
|
||||
collectionIds = listOf("collection-id"),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filter(hasAnyAncestor(isPopup()))
|
||||
.assertCountEquals(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Arcjove dialog ok click should send ConfirmArchiveClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = VaultItemState
|
||||
.DialogState
|
||||
.ArchiveConfirmationPrompt,
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Archive")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Ok")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmArchiveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Clicking Unarchive should send UnarchiveVaultItemClick ViewModel action`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_IDENTITY_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher = createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Unarchive")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultItemAction.Common.UnarchiveVaultItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unarchive dialog should display correctly when dialog state changes`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_IDENTITY_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher = createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
collectionIds = emptyList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = VaultItemState.DialogState.UnarchiveItemDialog)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Do you really want to restore this item?")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Unarchive")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Ok")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Cancel")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unarchive dialog should hide unarchive confirmation menu if dialog state changes`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_IDENTITY_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher = createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = VaultItemState.DialogState.RestoreItemDialog)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Do you really want to restore this item?")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Restore")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Ok")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Cancel")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = null)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unarchive dialog ok click should send ConfirmUnarchiveClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_IDENTITY_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher = createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = VaultItemState.DialogState.UnarchiveItemDialog)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Ok")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmUnarchiveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unarchive dialog cancel click should send DismissDialogClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_IDENTITY_VIEW_STATE
|
||||
.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(
|
||||
currentCipher = createMockCipherView(1).copy(
|
||||
archivedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialog = VaultItemState.DialogState.UnarchiveItemDialog)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Cancel")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.DismissDialogClick)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion common
|
||||
|
||||
//region login
|
||||
@ -3140,7 +3446,7 @@ private val DEFAULT_STATE: VaultItemState = VaultItemState(
|
||||
dialog = null,
|
||||
baseIconUrl = "https://example.com/",
|
||||
isIconLoadingDisabled = true,
|
||||
isArchiveItemEnabled = false,
|
||||
isArchiveItemEnabled = true,
|
||||
)
|
||||
|
||||
private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
|
||||
|
||||
@ -31,9 +31,11 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ArchiveCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DownloadAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RestoreCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UnarchiveCipherResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
@ -2888,6 +2890,277 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ArchiveClick should update state when re-prompt is not required`() =
|
||||
runTest {
|
||||
val loginState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON
|
||||
.copy(requiresReprompt = false),
|
||||
)
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns loginState
|
||||
|
||||
val expected = DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(
|
||||
requiresReprompt = false,
|
||||
),
|
||||
),
|
||||
dialog = VaultItemState.DialogState.ArchiveConfirmationPrompt,
|
||||
)
|
||||
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ArchiveClick)
|
||||
assertEquals(expected, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ConfirmArchiveClick with ArchiveCipherResult Success should should ShowToast and NavigateBack`() =
|
||||
runTest {
|
||||
val loginViewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(requiresReprompt = false),
|
||||
)
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns loginViewState
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value =
|
||||
DataState.Loaded(data = createVerificationCodeItem())
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = DEFAULT_STATE)
|
||||
coEvery {
|
||||
vaultRepo.archiveCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns ArchiveCipherResult.Success
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmArchiveClick)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultItemEvent.ShowToast(R.string.item_archived.asText()),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
VaultItemEvent.NavigateBack,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ConfirmArchiveClick with ArchiveCipherResult Failure should should Show generic error`() =
|
||||
runTest {
|
||||
val loginViewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(requiresReprompt = false),
|
||||
)
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns loginViewState
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = DEFAULT_STATE)
|
||||
coEvery {
|
||||
vaultRepo.archiveCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns ArchiveCipherResult.Error
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmArchiveClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = loginViewState,
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on UnarchiveItemClick updates pendingCipher state correctly`() =
|
||||
runTest {
|
||||
val viewState =
|
||||
DEFAULT_VIEW_STATE.copy(common = DEFAULT_COMMON.copy(requiresReprompt = false))
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = any(),
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns viewState
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value =
|
||||
DataState.Loaded(data = createVerificationCodeItem())
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
val loginState = DEFAULT_STATE.copy(viewState = viewState)
|
||||
val viewModel = createViewModel(state = loginState)
|
||||
assertEquals(loginState, viewModel.stateFlow.value)
|
||||
|
||||
// show dialog
|
||||
viewModel.trySendAction(VaultItemAction.Common.UnarchiveVaultItemClick)
|
||||
assertEquals(
|
||||
loginState.copy(dialog = VaultItemState.DialogState.UnarchiveItemDialog),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
|
||||
// dismiss dialog
|
||||
viewModel.trySendAction(VaultItemAction.Common.DismissDialogClick)
|
||||
assertEquals(
|
||||
// setting this to be explicit.
|
||||
loginState.copy(dialog = null),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ConfirmUnarchiveClick with UnarchiveCipherResult Success should should ShowToast and NavigateBack`() =
|
||||
runTest {
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(
|
||||
data = createVerificationCodeItem(),
|
||||
)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = DEFAULT_STATE)
|
||||
coEvery {
|
||||
vaultRepo.unarchiveCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns UnarchiveCipherResult.Success
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmUnarchiveClick)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultItemEvent.ShowToast(R.string.item_restored.asText()),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
VaultItemEvent.NavigateBack,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ConfirmUnarchiveClick with UnarchiveCipherResult Failure should should Show generic error`() {
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = createVerificationCodeItem())
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = DEFAULT_STATE)
|
||||
coEvery {
|
||||
vaultRepo.unarchiveCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmUnarchiveClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
@ -683,6 +683,14 @@ class VaultItemListingScreenTest : BaseComposeTest() {
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Add item")
|
||||
.assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(itemListingType = VaultItemListingState.ItemListingType.Vault.Archive)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Add item")
|
||||
.assertDoesNotExist()
|
||||
@ -1266,6 +1274,13 @@ class VaultItemListingScreenTest : BaseComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "mockName")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(itemListingType = VaultItemListingState.ItemListingType.Vault.Archive)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Archive")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -4560,6 +4560,91 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun `vaultDataStateFlow Loaded with archived items should update ViewState to NoItems`() =
|
||||
runTest {
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isArchived = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
mutableVaultDataStateFlow.tryEmit(value = dataState)
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
viewState = VaultItemListingState.ViewState.NoItems(
|
||||
header = null,
|
||||
message = R.string.no_logins.asText(),
|
||||
shouldShowAddButton = true,
|
||||
buttonText = R.string.new_login.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `vaultDataStateFlow Pending with archived data should update state to NoItems`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isArchived = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
viewState = VaultItemListingState.ViewState.NoItems(
|
||||
header = null,
|
||||
message = R.string.no_logins.asText(),
|
||||
shouldShowAddButton = true,
|
||||
buttonText = R.string.new_login.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Error with archived data should update state to NoItems`() = runTest {
|
||||
val dataState = DataState.Error(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1, isArchived = true)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
error = IllegalStateException(),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
mutableVaultDataStateFlow.tryEmit(value = dataState)
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
viewState = VaultItemListingState.ViewState.NoItems(
|
||||
header = null,
|
||||
message = R.string.no_logins.asText(),
|
||||
shouldShowAddButton = true,
|
||||
buttonText = R.string.new_login.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun createSavedStateHandleWithVaultItemListingType(
|
||||
|
||||
@ -412,6 +412,130 @@ class VaultItemListingDataExtensionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `determineListingPredicate should return the correct predicate for archived Login cipherView`() {
|
||||
val cipherView = createMockCipherView(
|
||||
number = 1,
|
||||
isArchived = true,
|
||||
isDeleted = false,
|
||||
cipherType = CipherType.LOGIN,
|
||||
)
|
||||
|
||||
mapOf(
|
||||
VaultItemListingState.ItemListingType.Vault.Login to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Card to false,
|
||||
VaultItemListingState.ItemListingType.Vault.SecureNote to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Identity to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Trash to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Archive to true,
|
||||
VaultItemListingState.ItemListingType.Vault.Folder(folderId = "mockId-1") to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Collection(collectionId = "mockId-1") to false,
|
||||
)
|
||||
.forEach { (type, expected) ->
|
||||
val result = cipherView.determineListingPredicate(
|
||||
itemListingType = type,
|
||||
)
|
||||
assertEquals(
|
||||
expected,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `determineListingPredicate should return the correct predicate for archived SecureNote cipherView`() {
|
||||
val cipherView = createMockCipherView(
|
||||
number = 1,
|
||||
isArchived = true,
|
||||
isDeleted = false,
|
||||
cipherType = CipherType.SECURE_NOTE,
|
||||
)
|
||||
|
||||
mapOf(
|
||||
VaultItemListingState.ItemListingType.Vault.Login to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Card to false,
|
||||
VaultItemListingState.ItemListingType.Vault.SecureNote to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Identity to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Trash to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Archive to true,
|
||||
VaultItemListingState.ItemListingType.Vault.Folder(folderId = "mockId-1") to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Collection(collectionId = "mockId-1") to false,
|
||||
)
|
||||
.forEach { (type, expected) ->
|
||||
val result = cipherView.determineListingPredicate(
|
||||
itemListingType = type,
|
||||
)
|
||||
assertEquals(
|
||||
expected,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `determineListingPredicate should return the correct predicate for archive Identity cipherView`() {
|
||||
val cipherView = createMockCipherView(
|
||||
number = 1,
|
||||
isArchived = true,
|
||||
isDeleted = false,
|
||||
cipherType = CipherType.IDENTITY,
|
||||
)
|
||||
|
||||
mapOf(
|
||||
VaultItemListingState.ItemListingType.Vault.Login to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Card to false,
|
||||
VaultItemListingState.ItemListingType.Vault.SecureNote to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Identity to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Trash to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Archive to true,
|
||||
VaultItemListingState.ItemListingType.Vault.Folder(folderId = "mockId-1") to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Collection(collectionId = "mockId-1") to false,
|
||||
)
|
||||
.forEach { (type, expected) ->
|
||||
val result = cipherView.determineListingPredicate(
|
||||
itemListingType = type,
|
||||
)
|
||||
assertEquals(
|
||||
expected,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `determineListingPredicate should return the correct predicate for archive Card cipherView`() {
|
||||
val cipherView = createMockCipherView(
|
||||
number = 1,
|
||||
isArchived = true,
|
||||
isDeleted = false,
|
||||
cipherType = CipherType.CARD,
|
||||
)
|
||||
|
||||
mapOf(
|
||||
VaultItemListingState.ItemListingType.Vault.Login to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Card to false,
|
||||
VaultItemListingState.ItemListingType.Vault.SecureNote to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Identity to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Trash to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Archive to true,
|
||||
VaultItemListingState.ItemListingType.Vault.Folder(folderId = "mockId-1") to false,
|
||||
VaultItemListingState.ItemListingType.Vault.Collection(collectionId = "mockId-1") to false,
|
||||
)
|
||||
.forEach { (type, expected) ->
|
||||
val result = cipherView.determineListingPredicate(
|
||||
itemListingType = type,
|
||||
)
|
||||
assertEquals(
|
||||
expected,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toViewState should transform a list of CipherViews into a ViewState when not autofill`() {
|
||||
mockkStatic(CipherView::subtitle)
|
||||
@ -728,6 +852,29 @@ class VaultItemListingDataExtensionsTest {
|
||||
),
|
||||
)
|
||||
|
||||
// Archive
|
||||
assertEquals(
|
||||
VaultItemListingState.ViewState.NoItems(
|
||||
header = R.string.no_items_archive.asText(),
|
||||
message = R.string.no_items_archive_description.asText(),
|
||||
shouldShowAddButton = false,
|
||||
buttonText = R.string.new_item.asText(),
|
||||
vectorRes = R.drawable.no_archives_icon,
|
||||
),
|
||||
vaultData.toViewState(
|
||||
itemListingType = VaultItemListingState.ItemListingType.Vault.Archive,
|
||||
vaultFilterType = VaultFilterType.AllVaults,
|
||||
hasMasterPassword = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
autofillSelectionData = null,
|
||||
fido2CreationData = null,
|
||||
fido2CredentialAutofillViews = null,
|
||||
totpData = null,
|
||||
isPremiumUser = true,
|
||||
),
|
||||
)
|
||||
|
||||
// SSH keys
|
||||
assertEquals(
|
||||
VaultItemListingState.ViewState.NoItems(
|
||||
|
||||
@ -121,6 +121,16 @@ class VaultItemListingStateExtensionsTest {
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSearchType should return Archive when item type is Archive`() {
|
||||
val expected = SearchType.Vault.Archive
|
||||
val itemType = VaultItemListingState.ItemListingType.Vault.Archive
|
||||
|
||||
val result = itemType.toSearchType()
|
||||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toVaultItemCipherType should return the correct response`() {
|
||||
val itemListingTypes = listOf(
|
||||
|
||||
@ -21,6 +21,7 @@ class VaultItemListingTypeExtensionsTest {
|
||||
VaultItemListingType.Identity,
|
||||
VaultItemListingType.Login,
|
||||
VaultItemListingType.SecureNote,
|
||||
VaultItemListingType.Archive,
|
||||
)
|
||||
|
||||
val result = itemListingTypeList.map { it.toItemListingType() }
|
||||
@ -39,6 +40,7 @@ class VaultItemListingTypeExtensionsTest {
|
||||
VaultItemListingState.ItemListingType.Vault.Identity,
|
||||
VaultItemListingState.ItemListingType.Vault.Login,
|
||||
VaultItemListingState.ItemListingType.Vault.SecureNote,
|
||||
VaultItemListingState.ItemListingType.Vault.Archive,
|
||||
),
|
||||
result,
|
||||
)
|
||||
|
||||
@ -1372,6 +1372,54 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `archive count should update according to state`() {
|
||||
val rowText = "Archive"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
// Header
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(text = "HIDDEN ITEMS (2)")
|
||||
.assertIsDisplayed()
|
||||
// Item
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(rowText)
|
||||
.assertTextEquals(rowText, 0.toString())
|
||||
|
||||
val archiveCount = 5
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_CONTENT_VIEW_STATE.copy(
|
||||
archiveItemsCount = archiveCount,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Header
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(text = "HIDDEN ITEMS (2)")
|
||||
.assertIsDisplayed()
|
||||
// Item
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(rowText)
|
||||
.assertTextEquals(rowText, archiveCount.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking archive item should send ArchiveClick action`() {
|
||||
val rowText = "Archive"
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
|
||||
}
|
||||
|
||||
composeTestRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(rowText))
|
||||
composeTestRule.onAllNodes(hasText(rowText)).filterToOne(hasClickAction()).performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(VaultAction.ArchiveClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ACTIVE_ACCOUNT_SUMMARY = AccountSummary(
|
||||
|
||||
@ -1968,6 +1968,18 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ArchiveClick should emit NavigateToItemListing event with archive type`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAction.ArchiveClick)
|
||||
assertEquals(
|
||||
VaultEvent.NavigateToItemListing(VaultItemListingType.Archive),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(): VaultViewModel =
|
||||
VaultViewModel(
|
||||
authRepository = authRepository,
|
||||
|
||||
@ -919,6 +919,49 @@ class VaultDataExtensionsTest {
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toViewState should count only archived items for the archived count`() {
|
||||
val vaultData = VaultData(
|
||||
cipherViewList = listOf(
|
||||
createMockCipherView(number = 2, isDeleted = false, isArchived = false),
|
||||
createMockCipherView(number = 1, isDeleted = true),
|
||||
createMockCipherView(number = 2, isDeleted = true, isArchived = true),
|
||||
createMockCipherView(number = 3, isDeleted = false, isArchived = true),
|
||||
createMockCipherView(number = 2, isArchived = true),
|
||||
),
|
||||
collectionViewList = listOf(),
|
||||
folderViewList = listOf(),
|
||||
sendViewList = listOf(),
|
||||
)
|
||||
|
||||
val actual = vaultData.toViewState(
|
||||
isPremium = true,
|
||||
isIconLoadingDisabled = false,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
vaultFilterType = VaultFilterType.AllVaults,
|
||||
hasMasterPassword = true,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultState.ViewState.Content(
|
||||
loginItemsCount = 1,
|
||||
cardItemsCount = 0,
|
||||
identityItemsCount = 0,
|
||||
secureNoteItemsCount = 0,
|
||||
favoriteItems = listOf(),
|
||||
folderItems = listOf(),
|
||||
collectionItems = listOf(),
|
||||
noFolderItems = listOf(),
|
||||
trashItemsCount = 2,
|
||||
totpItemsCount = 1,
|
||||
itemTypesCount = 5,
|
||||
sshKeyItemsCount = 0,
|
||||
archiveItemsCount = 2,
|
||||
),
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMockSshKeyVaultItem(number: Int): VaultState.ViewState.VaultItem.SshKey =
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user