From d8a9c596b27c9a68b63304d49784117bbebc6683 Mon Sep 17 00:00:00 2001 From: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:00:06 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=92[PM-33394]=20fix:=20Surface=20Cooki?= =?UTF-8?q?eRedirectException=20message=20during=20sync-on-unlock=20(#6645?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/vault/feature/vault/VaultViewModel.kt | 4 +- .../vault/feature/vault/VaultViewModelTest.kt | 97 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index f652a381c1..0ac95440fb 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -46,6 +46,7 @@ import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository +import com.x8bit.bitwarden.data.platform.util.userFriendlyMessage import com.x8bit.bitwarden.data.vault.manager.model.GetCipherResult import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.ArchiveCipherResult @@ -1190,7 +1191,8 @@ class VaultViewModel @Inject constructor( isPremium = state.isPremium, hasMasterPassword = state.hasMasterPassword, errorTitle = BitwardenString.an_error_has_occurred.asText(), - errorMessage = BitwardenString.generic_error_message.asText(), + errorMessage = vaultData.error.userFriendlyMessage?.asText() + ?: BitwardenString.generic_error_message.asText(), isRefreshing = false, restrictItemTypesPolicyOrgIds = state.restrictItemTypesPolicyOrgIds, isArchiveEnabled = state.isArchiveEnabled, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index bf67396d34..edd2076376 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet import com.bitwarden.data.repository.model.Environment import com.bitwarden.data.repository.util.baseIconUrl +import com.bitwarden.network.exception.CookieRedirectException import com.bitwarden.network.model.PolicyTypeJson import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.createMockPolicy @@ -1387,6 +1388,31 @@ class VaultViewModelTest : BaseViewModelTest() { ) } + @Suppress("MaxLineLength") + @Test + fun `vaultDataStateFlow Error with CookieRedirectException should show user-friendly message`() = + runTest { + mutableVaultDataStateFlow.tryEmit( + value = DataState.Error( + error = CookieRedirectException(hostname = "example.com"), + ), + ) + + val viewModel = createViewModel() + + assertEquals( + createMockVaultState( + viewState = VaultState.ViewState.Error( + message = ( + "Your request was interrupted because the app needed to " + + "re-authenticate. Please try again." + ).asText(), + ), + ), + viewModel.stateFlow.value, + ) + } + @Suppress("MaxLineLength") @Test fun `vaultDataStateFlow Error with items should update state to Content and show an error dialog`() = @@ -1455,6 +1481,77 @@ class VaultViewModelTest : BaseViewModelTest() { ) } + @Suppress("MaxLineLength") + @Test + fun `vaultDataStateFlow Error with CookieRedirectException with items should show user-friendly error dialog`() = + runTest { + mutableVaultDataStateFlow.tryEmit( + value = DataState.Error( + error = CookieRedirectException(hostname = "example.com"), + data = VaultData( + decryptCipherListResult = createMockDecryptCipherListResult( + number = 1, + successes = listOf(createMockCipherListView(number = 1)), + ), + collectionViewList = listOf(createMockCollectionView(number = 1)), + folderViewList = listOf(createMockFolderView(number = 1)), + sendViewList = listOf(createMockSendView(number = 1)), + ), + ), + ) + + val viewModel = createViewModel() + + assertEquals( + createMockVaultState( + viewState = VaultState.ViewState.Content( + loginItemsCount = 1, + cardItemsCount = 0, + identityItemsCount = 0, + secureNoteItemsCount = 0, + favoriteItems = listOf(), + folderItems = listOf( + VaultState.ViewState.FolderItem( + id = "mockId-1", + name = "mockName-1".asText(), + itemCount = 1, + ), + VaultState.ViewState.FolderItem( + id = null, + name = BitwardenString.folder_none.asText(), + itemCount = 0, + ), + ), + collectionItems = listOf( + VaultState.ViewState.CollectionItem( + id = "mockId-1", + name = "mockName-1", + itemCount = 1, + ), + ), + noFolderItems = listOf(), + trashItemsCount = 0, + totpItemsCount = 1, + itemTypesCount = 5, + sshKeyItemsCount = 0, + archivedItemsCount = 0, + archiveEnabled = true, + archiveSubText = null, + archiveEndIcon = null, + showCardGroup = true, + ), + dialog = VaultState.DialogState.Error( + title = BitwardenString.an_error_has_occurred.asText(), + message = ( + "Your request was interrupted because the app needed to " + + "re-authenticate. Please try again." + ).asText(), + ), + ), + viewModel.stateFlow.value, + ) + } + @Suppress("MaxLineLength") @Test fun `vaultDataStateFlow Error with empty items should update state to NoItems and show an error dialog`() =