mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 02:53:19 -05:00
[PM-33561] debt: Wire CipherManager and cipher ViewModel error handlers (#6651)
This commit is contained in:
1 parent
870f15418b
commit
85c3a1deb8
25 files changed
+1019
-35
No files matched your search
+4
-1
@@ -114,7 +114,10 @@ class Fido2CredentialStoreImpl(
|
||||
private fun UpdateCipherResult.toCreateCipherResult(): CreateCipherResult =
|
||||
when (this) {
|
||||
UpdateCipherResult.Success -> CreateCipherResult.Success
|
||||
is UpdateCipherResult.Error -> CreateCipherResult.Error(errorMessage, error)
|
||||
is UpdateCipherResult.Error -> CreateCipherResult.Error(
|
||||
error = error,
|
||||
errorMessage = errorMessage,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,10 +85,7 @@ class CipherManagerImpl(
|
||||
|
||||
override suspend fun createCipher(cipherView: CipherView): CreateCipherResult {
|
||||
val userId = activeUserId
|
||||
?: return CreateCipherResult.Error(
|
||||
error = NoActiveUserException(),
|
||||
errorMessage = null,
|
||||
)
|
||||
?: return CreateCipherResult.Error(error = NoActiveUserException())
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
@@ -111,7 +108,7 @@ class CipherManagerImpl(
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onFailure = { CreateCipherResult.Error(errorMessage = null, error = it) },
|
||||
onFailure = { CreateCipherResult.Error(error = it) },
|
||||
onSuccess = {
|
||||
reviewPromptManager.registerAddCipherAction()
|
||||
it
|
||||
@@ -124,7 +121,7 @@ class CipherManagerImpl(
|
||||
collectionIds: List<String>,
|
||||
): CreateCipherResult {
|
||||
val userId = activeUserId
|
||||
?: return CreateCipherResult.Error(errorMessage = null, error = NoActiveUserException())
|
||||
?: return CreateCipherResult.Error(error = NoActiveUserException())
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
@@ -157,7 +154,7 @@ class CipherManagerImpl(
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onFailure = { CreateCipherResult.Error(errorMessage = null, error = it) },
|
||||
onFailure = { CreateCipherResult.Error(error = it) },
|
||||
onSuccess = {
|
||||
reviewPromptManager.registerAddCipherAction()
|
||||
it
|
||||
@@ -169,7 +166,8 @@ class CipherManagerImpl(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): ArchiveCipherResult {
|
||||
val userId = activeUserId ?: return ArchiveCipherResult.Error(NoActiveUserException())
|
||||
val userId = activeUserId
|
||||
?: return ArchiveCipherResult.Error(error = NoActiveUserException())
|
||||
return ciphersService
|
||||
.archiveCipher(cipherId = cipherId)
|
||||
.flatMap { response ->
|
||||
@@ -204,7 +202,8 @@ class CipherManagerImpl(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UnarchiveCipherResult {
|
||||
val userId = activeUserId ?: return UnarchiveCipherResult.Error(NoActiveUserException())
|
||||
val userId = activeUserId
|
||||
?: return UnarchiveCipherResult.Error(error = NoActiveUserException())
|
||||
return ciphersService
|
||||
.unarchiveCipher(cipherId = cipherId)
|
||||
.flatMap { response ->
|
||||
@@ -368,7 +367,7 @@ class CipherManagerImpl(
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult {
|
||||
val userId = activeUserId
|
||||
?: return UpdateCipherResult.Error(errorMessage = null, error = NoActiveUserException())
|
||||
?: return UpdateCipherResult.Error(error = NoActiveUserException())
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
@@ -399,7 +398,7 @@ class CipherManagerImpl(
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onFailure = { UpdateCipherResult.Error(errorMessage = null, error = it) },
|
||||
onFailure = { UpdateCipherResult.Error(error = it) },
|
||||
onSuccess = { it },
|
||||
)
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of archiving a cipher.
|
||||
*/
|
||||
@@ -16,6 +18,6 @@ sealed class ArchiveCipherResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : ArchiveCipherResult()
|
||||
}
|
||||
+6
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of creating a cipher.
|
||||
*/
|
||||
@@ -14,5 +16,8 @@ sealed class CreateCipherResult {
|
||||
* Generic error while creating cipher. The optional [errorMessage] may be displayed directly in
|
||||
* the UI when present.
|
||||
*/
|
||||
data class Error(val errorMessage: String?, val error: Throwable?) : CreateCipherResult()
|
||||
data class Error(
|
||||
val error: Throwable?,
|
||||
val errorMessage: String? = error?.userFriendlyMessage,
|
||||
) : CreateCipherResult()
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of deleting an attachment from a cipher.
|
||||
*/
|
||||
@@ -16,6 +18,6 @@ sealed class DeleteAttachmentResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : DeleteAttachmentResult()
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of deleting a cipher.
|
||||
*/
|
||||
@@ -16,6 +18,6 @@ sealed class DeleteCipherResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : DeleteCipherResult()
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of restoring a cipher.
|
||||
*/
|
||||
@@ -16,6 +18,6 @@ sealed class RestoreCipherResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : RestoreCipherResult()
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of sharing a cipher.
|
||||
*/
|
||||
@@ -15,6 +17,6 @@ sealed class ShareCipherResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : ShareCipherResult()
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of unarchiving a cipher.
|
||||
*/
|
||||
@@ -16,6 +18,6 @@ sealed class UnarchiveCipherResult {
|
||||
*/
|
||||
data class Error(
|
||||
val error: Throwable,
|
||||
val errorMessage: String? = null,
|
||||
val errorMessage: String? = error.userFriendlyMessage,
|
||||
) : UnarchiveCipherResult()
|
||||
}
|
||||
+6
-1
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage
|
||||
|
||||
/**
|
||||
* Models result of updating a cipher.
|
||||
*/
|
||||
@@ -14,5 +16,8 @@ sealed class UpdateCipherResult {
|
||||
* Generic error while updating cipher. The optional [errorMessage] may be displayed directly in
|
||||
* the UI when present.
|
||||
*/
|
||||
data class Error(val errorMessage: String?, val error: Throwable?) : UpdateCipherResult()
|
||||
data class Error(
|
||||
val error: Throwable?,
|
||||
val errorMessage: String? = error?.userFriendlyMessage,
|
||||
) : UpdateCipherResult()
|
||||
}
|
||||
+4
-2
@@ -676,7 +676,8 @@ class SearchViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialogState = SearchState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
@@ -697,7 +698,8 @@ class SearchViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialogState = SearchState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
+6
-3
@@ -1821,7 +1821,8 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
is ArchiveCipherResult.Error -> {
|
||||
showDialog(
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1845,7 +1846,8 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
is UnarchiveCipherResult.Error -> {
|
||||
showDialog(
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1873,7 +1875,8 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
is DeleteCipherResult.Error -> {
|
||||
showDialog(
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = BitwardenString.generic_error_message.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.generic_error_message.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
+4
-1
@@ -298,7 +298,10 @@ class AttachmentsViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialogState = AttachmentsState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.generic_error_message.asText(),
|
||||
message = result
|
||||
.errorMessage
|
||||
?.asText()
|
||||
?: BitwardenString.generic_error_message.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1183,7 +1183,8 @@ class VaultItemViewModel @Inject constructor(
|
||||
is DeleteCipherResult.Error -> {
|
||||
updateDialogState(
|
||||
VaultItemState.DialogState.Generic(
|
||||
message = BitwardenString.generic_error_message.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.generic_error_message.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1211,7 +1212,8 @@ class VaultItemViewModel @Inject constructor(
|
||||
is RestoreCipherResult.Error -> {
|
||||
updateDialogState(
|
||||
VaultItemState.DialogState.Generic(
|
||||
message = BitwardenString.generic_error_message.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.generic_error_message.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1291,7 +1293,8 @@ class VaultItemViewModel @Inject constructor(
|
||||
is ArchiveCipherResult.Error -> {
|
||||
updateDialogState(
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1315,7 +1318,8 @@ class VaultItemViewModel @Inject constructor(
|
||||
is UnarchiveCipherResult.Error -> {
|
||||
updateDialogState(
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
+4
-2
@@ -1723,7 +1723,8 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1750,7 +1751,8 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
+4
-1
@@ -161,7 +161,10 @@ class VaultMoveToOrganizationViewModel @Inject constructor(
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = VaultMoveToOrganizationState.DialogState.Error(
|
||||
message = BitwardenString.generic_error_message.asText(),
|
||||
message = result
|
||||
.errorMessage
|
||||
?.asText()
|
||||
?: BitwardenString.generic_error_message.asText(),
|
||||
throwable = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1018,7 +1018,8 @@ class VaultViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialog = VaultState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_archive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
@@ -1039,7 +1040,8 @@ class VaultViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialog = VaultState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
message = result.errorMessage?.asText()
|
||||
?: BitwardenString.unable_to_unarchive_selected_item.asText(),
|
||||
error = result.error,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.data.manager.file.FileManager
|
||||
import com.bitwarden.data.manager.model.DownloadResult
|
||||
import com.bitwarden.network.exception.CookieRedirectException
|
||||
import com.bitwarden.network.model.ArchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.AttachmentJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
|
||||
@@ -2933,6 +2934,114 @@ class CipherManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `hardDeleteCipher with CookieRedirectException should return DeleteCipherResult Error with errorMessage`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val cipherId = "mockId-1"
|
||||
val error = CookieRedirectException("test.host")
|
||||
coEvery {
|
||||
ciphersService.hardDeleteCipher(cipherId = cipherId)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = cipherManager.hardDeleteCipher(cipherId)
|
||||
|
||||
assertEquals(
|
||||
DeleteCipherResult.Error(
|
||||
errorMessage = "Your request was interrupted because " +
|
||||
"the app needed to re-authenticate. Please try again.",
|
||||
error = error,
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `softDeleteCipher with CookieRedirectException should return DeleteCipherResult Error with errorMessage`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val userId = MOCK_USER_STATE.activeUserId
|
||||
val cipherId = "mockId-1"
|
||||
val cipherView = createMockCipherView(number = 1)
|
||||
val encryptionContext = createMockEncryptionContext(number = 1)
|
||||
val error = CookieRedirectException("test.host")
|
||||
coEvery {
|
||||
vaultSdkSource.encryptCipher(userId = userId, cipherView = cipherView)
|
||||
} returns encryptionContext.asSuccess()
|
||||
coEvery {
|
||||
ciphersService.softDeleteCipher(cipherId = cipherId)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = cipherManager.softDeleteCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = cipherView,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DeleteCipherResult.Error(
|
||||
errorMessage = "Your request was interrupted because " +
|
||||
"the app needed to re-authenticate. Please try again.",
|
||||
error = error,
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `restoreCipher with CookieRedirectException should return RestoreCipherResult Error with errorMessage`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val cipherId = "mockId-1"
|
||||
val cipherView = createMockCipherView(number = 1)
|
||||
val error = CookieRedirectException("test.host")
|
||||
coEvery {
|
||||
ciphersService.restoreCipher(cipherId = cipherId)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = cipherManager.restoreCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = cipherView,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
RestoreCipherResult.Error(
|
||||
errorMessage = "Your request was interrupted because " +
|
||||
"the app needed to re-authenticate. Please try again.",
|
||||
error = error,
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `archiveCipher with CookieRedirectException should return ArchiveCipherResult Error with errorMessage`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val cipherId = "mockId-1"
|
||||
val error = CookieRedirectException("test.host")
|
||||
coEvery {
|
||||
ciphersService.archiveCipher(cipherId = cipherId)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = cipherManager.archiveCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
ArchiveCipherResult.Error(
|
||||
errorMessage = "Your request was interrupted because " +
|
||||
"the app needed to re-authenticate. Please try again.",
|
||||
error = error,
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupMockUri(
|
||||
url: String,
|
||||
queryParams: Map<String, String> = emptyMap(),
|
||||
|
||||
+80
@@ -398,6 +398,46 @@ class SearchViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ArchiveClick with ArchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createViewModel(initialState = null)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.archiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns ArchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
SearchAction.OverflowOptionClick(
|
||||
overflowAction = ListingItemOverflowAction.VaultAction.ArchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = SearchState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult Success should emit a ShowSnackbar event`() =
|
||||
runTest {
|
||||
@@ -456,6 +496,46 @@ class SearchViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createViewModel(initialState = null)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.unarchiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
SearchAction.OverflowOptionClick(
|
||||
overflowAction = ListingItemOverflowAction.VaultAction.UnarchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = SearchState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AutofillItemClick should call emitAccessibilitySelection`() = runTest {
|
||||
val cipherView = setupForAutofill(
|
||||
|
||||
+253
@@ -735,6 +735,80 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ConfirmDeleteClick with DeleteCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherListView = createMockCipherListView(number = 1)
|
||||
val cipherView = createMockCipherView(number = 1)
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType)
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
data = createVaultData(cipherListView = cipherListView),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
vaultItemCipherType = VaultItemCipherType.LOGIN,
|
||||
),
|
||||
)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.softDeleteCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns DeleteCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.ConfirmDeleteClick)
|
||||
|
||||
assertEquals(
|
||||
createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
originalCipher = createMockCipherView(number = 1),
|
||||
notes = "mockNotes-1",
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
itemId = "testId",
|
||||
name = "mockName-1",
|
||||
value = "mockValue-1",
|
||||
),
|
||||
),
|
||||
),
|
||||
typeContentViewState = createLoginTypeContentViewState(
|
||||
username = "mockUsername-1",
|
||||
password = "mockPassword-1",
|
||||
uri = listOf(
|
||||
UriItem(
|
||||
id = "testId",
|
||||
uri = "www.mockuri1.com",
|
||||
match = UriMatchType.HOST,
|
||||
checksum = "mockUriChecksum-1",
|
||||
),
|
||||
),
|
||||
totpCode = "mockTotp-1",
|
||||
canViewPassword = true,
|
||||
fido2CredentialCreationDateTime = null,
|
||||
)
|
||||
.copy(totp = "mockTotp-1"),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode, SaveClick should show dialog, remove it once an item is saved, and emit NavigateBack`() =
|
||||
@@ -2476,6 +2550,96 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ArchiveClick with ArchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
mutableUserStateFlow.update {
|
||||
it?.copy(
|
||||
accounts = it.accounts.map { account ->
|
||||
account.copy(isPremium = true)
|
||||
},
|
||||
)
|
||||
}
|
||||
val cipherListView =
|
||||
createMockCipherListView(number = 1, isArchived = false)
|
||||
val cipherView =
|
||||
createMockCipherView(number = 1, isArchived = false)
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val initState = createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
originalCipher = cipherView,
|
||||
),
|
||||
hasPremium = true,
|
||||
)
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
data = createVaultData(cipherListView = cipherListView),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
vaultItemCipherType = VaultItemCipherType.LOGIN,
|
||||
),
|
||||
)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.archiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns ArchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.ArchiveClick)
|
||||
|
||||
assertEquals(
|
||||
createVaultAddItemState(
|
||||
hasPremium = true,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
originalCipher = createMockCipherView(number = 1),
|
||||
notes = "mockNotes-1",
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
itemId = "testId",
|
||||
name = "mockName-1",
|
||||
value = "mockValue-1",
|
||||
),
|
||||
),
|
||||
),
|
||||
typeContentViewState = createLoginTypeContentViewState(
|
||||
username = "mockUsername-1",
|
||||
password = "mockPassword-1",
|
||||
uri = listOf(
|
||||
UriItem(
|
||||
id = "testId",
|
||||
uri = "www.mockuri1.com",
|
||||
match = UriMatchType.HOST,
|
||||
checksum = "mockUriChecksum-1",
|
||||
),
|
||||
),
|
||||
totpCode = "mockTotp-1",
|
||||
canViewPassword = true,
|
||||
fido2CredentialCreationDateTime = null,
|
||||
)
|
||||
.copy(totp = "mockTotp-1"),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult Success should send snackbar event and NavigateBack`() =
|
||||
@@ -2588,6 +2752,95 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
mutableUserStateFlow.update {
|
||||
it?.copy(
|
||||
accounts = it.accounts.map { account ->
|
||||
account.copy(isPremium = true)
|
||||
},
|
||||
)
|
||||
}
|
||||
val cipherListView =
|
||||
createMockCipherListView(number = 1, isArchived = false)
|
||||
val cipherView =
|
||||
createMockCipherView(number = 1, isArchived = false)
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val initState = createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
originalCipher = cipherView,
|
||||
),
|
||||
hasPremium = true,
|
||||
)
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
data = createVaultData(cipherListView = cipherListView),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
vaultItemCipherType = VaultItemCipherType.LOGIN,
|
||||
),
|
||||
)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.unarchiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.UnarchiveClick)
|
||||
|
||||
assertEquals(
|
||||
createVaultAddItemState(
|
||||
hasPremium = true,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
dialogState = VaultAddEditState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
originalCipher = createMockCipherView(number = 1),
|
||||
notes = "mockNotes-1",
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
itemId = "testId",
|
||||
name = "mockName-1",
|
||||
value = "mockValue-1",
|
||||
),
|
||||
),
|
||||
),
|
||||
typeContentViewState = createLoginTypeContentViewState(
|
||||
username = "mockUsername-1",
|
||||
password = "mockPassword-1",
|
||||
uri = listOf(
|
||||
UriItem(
|
||||
id = "testId",
|
||||
uri = "www.mockuri1.com",
|
||||
match = UriMatchType.HOST,
|
||||
checksum = "mockUriChecksum-1",
|
||||
),
|
||||
),
|
||||
totpCode = "mockTotp-1",
|
||||
canViewPassword = true,
|
||||
fido2CredentialCreationDateTime = null,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class VaultAddEditLoginTypeItemActions {
|
||||
private lateinit var viewModel: VaultAddEditViewModel
|
||||
|
||||
+49
@@ -480,6 +480,55 @@ class AttachmentsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `DeleteClick with deleteCipherAttachment error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherId = "mockId-1"
|
||||
val attachmentId = "mockId-1"
|
||||
val cipherView = createMockCipherView(number = 1)
|
||||
val initialState = DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_CONTENT_WITH_ATTACHMENTS,
|
||||
)
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Fail")
|
||||
coEvery {
|
||||
vaultRepository.deleteCipherAttachment(
|
||||
cipherId = cipherId,
|
||||
attachmentId = attachmentId,
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns DeleteAttachmentResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
mutableVaultItemStateFlow.value = DataState.Loaded(cipherView)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(initialState, awaitItem())
|
||||
viewModel.trySendAction(AttachmentsAction.DeleteClick(attachmentId))
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = AttachmentsState.DialogState.Loading(
|
||||
message = BitwardenString.deleting.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = AttachmentsState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeleteClick with deleteCipherAttachment success should emit ShowSnackbar`() = runTest {
|
||||
val cipherId = "mockId-1"
|
||||
|
||||
+218
@@ -355,6 +355,59 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ArchiveClick with ArchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, isArchived = false)
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
hasOrganizations = true,
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = cipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = null)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepo.archiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns ArchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ArchiveClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult Success should send snackbar event and NavigateBack`() =
|
||||
@@ -457,6 +510,69 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, isArchived = true)
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
hasOrganizations = true,
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(
|
||||
archived = true,
|
||||
currentCipher = cipherView,
|
||||
),
|
||||
)
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = cipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val viewModel = createViewModel(state = null)
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepo.unarchiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.UnarchiveClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON.copy(
|
||||
archived = true,
|
||||
currentCipher = cipherView,
|
||||
),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeleteClick should update state`() =
|
||||
runTest {
|
||||
@@ -631,6 +747,57 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ConfirmDeleteClick with DeleteCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
hasOrganizations = true,
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE
|
||||
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)
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepo.softDeleteCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns DeleteCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmDeleteClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ConfirmDeleteClick with deleted cipher should should invoke hardDeleteCipher`() =
|
||||
runTest {
|
||||
@@ -830,6 +997,57 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ConfirmRestoreClick with RestoreCipherResult error with errorMessage should display that message`() {
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(),
|
||||
hasOrganizations = true,
|
||||
)
|
||||
} 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)
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Fail")
|
||||
coEvery {
|
||||
vaultRepo.restoreCipher(
|
||||
cipherId = VAULT_ITEM_ID,
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
} returns RestoreCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.ConfirmRestoreClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialog = VaultItemState.DialogState.Generic(
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on EditClick should navigate password`() = runTest {
|
||||
val loginViewState = createViewState()
|
||||
|
||||
+80
@@ -1155,6 +1155,46 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ArchiveClick with ArchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.archiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns ArchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
VaultItemListingsAction.OverflowOptionClick(
|
||||
action = ListingItemOverflowAction.VaultAction.ArchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult Success should emit a ShowSnackbar event`() =
|
||||
runTest {
|
||||
@@ -1215,6 +1255,46 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.unarchiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
VaultItemListingsAction.OverflowOptionClick(
|
||||
action = ListingItemOverflowAction.VaultAction.UnarchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MasterPasswordRepromptSubmit for a request Error should show a generic error dialog`() =
|
||||
runTest {
|
||||
|
||||
+72
@@ -385,6 +385,78 @@ class VaultMoveToOrganizationViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `MoveClick with shareCipher error with errorMessage should show error dialog with that message`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
mutableCollectionFlow.tryEmit(value = DataState.Loaded(DEFAULT_COLLECTIONS))
|
||||
mutableVaultItemFlow.tryEmit(
|
||||
value = DataState.Loaded(createMockCipherView(number = 1)),
|
||||
)
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Fail")
|
||||
coEvery {
|
||||
vaultRepository.shareCipher(
|
||||
cipherId = "mockCipherId",
|
||||
organizationId = "mockOrganizationId-1",
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
collectionIds = listOf("mockId-1"),
|
||||
)
|
||||
} returns ShareCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
viewState = VaultMoveToOrganizationState.ViewState.Content(
|
||||
organizations = createMockOrganizationList(),
|
||||
selectedOrganizationId = "mockOrganizationId-1",
|
||||
cipherToMove = createMockCipherView(number = 1),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
viewModel.trySendAction(VaultMoveToOrganizationAction.MoveClick)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = VaultMoveToOrganizationState.DialogState.Loading(
|
||||
message = BitwardenString.saving.asText(),
|
||||
),
|
||||
viewState = VaultMoveToOrganizationState.ViewState.Content(
|
||||
organizations = createMockOrganizationList(),
|
||||
selectedOrganizationId = "mockOrganizationId-1",
|
||||
cipherToMove = createMockCipherView(number = 1),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = VaultMoveToOrganizationState.DialogState.Error(
|
||||
message = errorMessage.asText(),
|
||||
throwable = error,
|
||||
),
|
||||
viewState = VaultMoveToOrganizationState.ViewState.Content(
|
||||
organizations = createMockOrganizationList(),
|
||||
selectedOrganizationId = "mockOrganizationId-1",
|
||||
cipherToMove = createMockCipherView(number = 1),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify {
|
||||
vaultRepository.shareCipher(
|
||||
cipherId = "mockCipherId",
|
||||
organizationId = "mockOrganizationId-1",
|
||||
cipherView = createMockCipherView(number = 1),
|
||||
collectionIds = listOf("mockId-1"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MoveClick with shareCipher success should emit NavigateBack`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
@@ -750,6 +750,46 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ArchiveClick with ArchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.archiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns ArchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
VaultAction.OverflowOptionClick(
|
||||
overflowAction = ListingItemOverflowAction.VaultAction.ArchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = VaultState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult Success should emit a ShowSnackbar event`() =
|
||||
runTest {
|
||||
@@ -808,6 +848,46 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UnarchiveClick with UnarchiveCipherResult error with errorMessage should display that message`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(number = 1, clock = clock)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
val error = Throwable("Oh dang.")
|
||||
coEvery {
|
||||
vaultRepository.unarchiveCipher(
|
||||
cipherId = "mockId-1",
|
||||
cipherView = cipherView,
|
||||
)
|
||||
} returns UnarchiveCipherResult.Error(
|
||||
errorMessage = errorMessage,
|
||||
error = error,
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
VaultAction.OverflowOptionClick(
|
||||
overflowAction = ListingItemOverflowAction.VaultAction.UnarchiveClick(
|
||||
cipherId = "mockId-1",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = VaultState.DialogState.Error(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = errorMessage.asText(),
|
||||
error = error,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on LockAccountClick should call lockVault for the given account`() {
|
||||
val accountUserId = "userId"
|
||||
|
||||
Reference in new issue
Block a user