From 36c892e1887b5051270ead6021afb16420663566 Mon Sep 17 00:00:00 2001 From: David Perez Date: Thu, 9 Jul 2026 15:51:57 -0500 Subject: [PATCH] Chore: Account Summaries list in UI should be immutable (#7147) --- .../ui/auth/feature/landing/LandingScreen.kt | 3 +- .../auth/feature/landing/LandingViewModel.kt | 18 ++++++----- .../ui/auth/feature/login/LoginScreen.kt | 3 +- .../ui/auth/feature/login/LoginViewModel.kt | 7 ++-- .../feature/vaultunlock/VaultUnlockScreen.kt | 3 +- .../vaultunlock/VaultUnlockViewModel.kt | 6 ++-- .../itemlisting/VaultItemListingScreen.kt | 3 +- .../itemlisting/VaultItemListingViewModel.kt | 2 +- .../ui/vault/feature/vault/VaultScreen.kt | 2 +- .../ui/vault/feature/vault/VaultViewModel.kt | 4 +-- .../feature/vault/util/UserStateExtensions.kt | 12 +++---- .../auth/feature/landing/LandingScreenTest.kt | 21 ++++++------ .../feature/landing/LandingViewModelTest.kt | 3 +- .../ui/auth/feature/login/LoginScreenTest.kt | 23 ++++++------- .../auth/feature/login/LoginViewModelTest.kt | 3 +- .../vaultunlock/VaultUnlockScreenTest.kt | 7 ++-- .../vaultunlock/VaultUnlockViewModelTest.kt | 32 +++++++++++-------- .../itemlisting/VaultItemListingScreenTest.kt | 6 ++-- .../ui/vault/feature/vault/VaultScreenTest.kt | 4 +-- .../vault/feature/vault/VaultViewModelTest.kt | 8 ++--- 20 files changed, 92 insertions(+), 78 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreen.kt index ca1a29a495..122c94def4 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreen.kt @@ -55,7 +55,6 @@ import com.bitwarden.ui.platform.resource.BitwardenDrawable import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.ui.platform.components.dropdown.EnvironmentSelector -import kotlinx.collections.immutable.toImmutableList /** * The top level composable for the Landing screen. @@ -139,7 +138,7 @@ fun LandingScreen( overlay = { BitwardenAccountSwitcher( isVisible = isAccountMenuVisible, - accountSummaries = state.accountSummaries.toImmutableList(), + accountSummaries = state.accountSummaries, onSwitchAccountClick = { viewModel.trySendAction(LandingAction.SwitchAccountClick(it)) }, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt index a3510be7ff..bf1c9cd8f3 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt @@ -55,7 +55,12 @@ class LandingViewModel @Inject constructor( selectedEnvironmentType = environmentRepository.environment.type, selectedEnvironmentLabel = environmentRepository.environment.label, dialog = null, - accountSummaries = authRepository.userStateFlow.value?.toAccountSummaries().orEmpty(), + accountSummaries = authRepository + .userStateFlow + .value + ?.toAccountSummaries() + .orEmpty() + .toImmutableList(), isFedRampEnabled = featureFlagManager.getFeatureFlag(FlagKey.FedRamp), ), ) { @@ -68,12 +73,9 @@ class LandingViewModel @Inject constructor( get() { val currentEmail = state.emailInput val currentEnvironmentLabel = state.selectedEnvironmentLabel - val accountSummaries = state.accountSummaries - return accountSummaries - .find { - it.email == currentEmail && - it.environmentLabel == currentEnvironmentLabel - } + return state + .accountSummaries + .find { it.email == currentEmail && it.environmentLabel == currentEnvironmentLabel } ?.takeUnless { !it.isLoggedIn } } @@ -299,7 +301,7 @@ data class LandingState( val selectedEnvironmentType: Environment.Type, val selectedEnvironmentLabel: String, val dialog: DialogState?, - val accountSummaries: List, + val accountSummaries: ImmutableList, val isFedRampEnabled: Boolean, ) : Parcelable { /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt index 8a13831fd3..81743fafc0 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt @@ -50,7 +50,6 @@ import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.R import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList /** * The top level composable for the Login screen. @@ -134,7 +133,7 @@ fun LoginScreen( overlay = { BitwardenAccountSwitcher( isVisible = isAccountMenuVisible, - accountSummaries = state.accountSummaries.toImmutableList(), + accountSummaries = state.accountSummaries, onSwitchAccountClick = { viewModel.trySendAction(LoginAction.SwitchAccountClick(it)) }, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt index 160e9a2640..f7396b2b4a 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt @@ -20,6 +20,8 @@ import com.x8bit.bitwarden.data.platform.util.toUriOrNull import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummaries import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.parcelize.IgnoredOnParcel @@ -52,7 +54,8 @@ class LoginViewModel @Inject constructor( .userStateFlow .value ?.toAccountSummaries() - .orEmpty(), + .orEmpty() + .toImmutableList(), shouldShowLoginWithDevice = false, ) }, @@ -296,7 +299,7 @@ data class LoginState( val environmentLabel: String, val isLoginButtonEnabled: Boolean, val dialogState: DialogState?, - val accountSummaries: List, + val accountSummaries: ImmutableList, val shouldShowLoginWithDevice: Boolean, ) : Parcelable { /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt index 990bf47949..c73f1942d5 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt @@ -64,7 +64,6 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager import com.x8bit.bitwarden.ui.platform.composition.LocalCredentialProviderCompletionManager import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay import javax.crypto.Cipher @@ -222,7 +221,7 @@ fun VaultUnlockScreen( overlay = { BitwardenAccountSwitcher( isVisible = accountMenuVisible, - accountSummaries = state.accountSummaries.toImmutableList(), + accountSummaries = state.accountSummaries, onSwitchAccountClick = { viewModel.trySendAction(VaultUnlockAction.SwitchAccountClick(it)) }, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt index 1bd1dc9b54..afb6aee4c7 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt @@ -36,6 +36,8 @@ import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummaries import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummary import com.x8bit.bitwarden.ui.vault.feature.vault.util.toActiveAccountSummary import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update @@ -71,7 +73,7 @@ class VaultUnlockViewModel @Inject constructor( // get us out of here. UserState.EMPTY_ACCOUNT } - val accountSummaries = userState?.toAccountSummaries().orEmpty() + val accountSummaries = userState?.toAccountSummaries().orEmpty().toImmutableList() val activeAccountSummary = activeAccount.toAccountSummary(isActive = true) val vaultUnlockType = activeAccount.vaultUnlockType val hasNoMasterPassword = !activeAccount.hasMasterPassword @@ -445,7 +447,7 @@ class VaultUnlockViewModel @Inject constructor( */ @Parcelize data class VaultUnlockState( - val accountSummaries: List, + val accountSummaries: ImmutableList, private val avatarColorString: String, val hideInput: Boolean, val initials: String, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt index fc7738d590..c570824d12 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreen.kt @@ -68,7 +68,6 @@ import com.x8bit.bitwarden.ui.vault.feature.itemlisting.handlers.VaultItemListin import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList /** * Displays the vault item listing screen. @@ -501,7 +500,7 @@ private fun VaultItemListingScaffold( overlay = { BitwardenAccountSwitcher( isVisible = isAccountMenuVisible, - accountSummaries = state.accountSummaries.toImmutableList(), + accountSummaries = state.accountSummaries, onSwitchAccountClick = vaultItemListingHandlers.switchAccountClick, onLockAccountClick = vaultItemListingHandlers.lockAccountClick, onLogoutAccountClick = vaultItemListingHandlers.logoutAccountClick, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 5e84beb8b6..d61b6d41d7 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -2933,7 +2933,7 @@ class VaultItemListingViewModel @Inject constructor( data class VaultItemListingState( val itemListingType: ItemListingType, val activeAccountSummary: AccountSummary, - val accountSummaries: List, + val accountSummaries: ImmutableList, val viewState: ViewState, val vaultFilterType: VaultFilterType, val baseWebSendUrl: String, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt index 5aef95618a..4cdc7d0f56 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt @@ -310,7 +310,7 @@ private fun VaultScreenScaffold( overlay = { BitwardenAccountSwitcher( isVisible = accountMenuVisible, - accountSummaries = state.accountSummaries.toImmutableList(), + accountSummaries = state.accountSummaries, onSwitchAccountClick = vaultHandlers.accountSwitchClickAction, onLockAccountClick = vaultHandlers.accountLockClickAction, onLogoutAccountClick = vaultHandlers.accountLogoutClickAction, 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 91cc21c3b9..8cc52387f3 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 @@ -127,7 +127,7 @@ class VaultViewModel @Inject constructor( ) : BaseViewModel( initialState = run { val userState = authRepository.userStateFlow.value - val accountSummaries = userState?.toAccountSummaries().orEmpty() + val accountSummaries = userState?.toAccountSummaries().orEmpty().toImmutableList() val activeAccount = userState?.activeAccount ?: run { // We use this empty account to avoid a crash that can occur during a race condition. // The state-based navigation brought us here but the UserState has now been set to @@ -1756,7 +1756,7 @@ data class VaultState( val appBarTitle: Text, private val avatarColorString: String, val initials: String, - val accountSummaries: List, + val accountSummaries: ImmutableList, val vaultFilterData: VaultFilterData? = null, val viewState: ViewState, val dialog: DialogState? = null, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt index de9d937259..c35fa441ac 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt @@ -4,17 +4,17 @@ import com.bitwarden.ui.platform.components.account.model.AccountSummary import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import java.util.Locale /** * Converts the given [UserState] to a list of [AccountSummary]. */ -fun UserState.toAccountSummaries(): List = - accounts.map { account -> - account.toAccountSummary( - isActive = this.activeUserId == account.userId, - ) - } +fun UserState.toAccountSummaries(): ImmutableList = + accounts + .map { it.toAccountSummary(isActive = this.activeUserId == it.userId) } + .toImmutableList() /** * Converts the given [UserState] to an [AccountSummary] with a [AccountSummary.status] of diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt index 6772619d74..277cc45563 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt @@ -38,6 +38,7 @@ import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import io.mockk.every import io.mockk.mockk import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import org.junit.Before @@ -80,7 +81,7 @@ class LandingScreenTest : BitwardenComposeTest() { composeTestRule.onNodeWithContentDescription("Account").assertDoesNotExist() mutableStateFlow.update { - it.copy(accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)) + it.copy(accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY)) } composeTestRule.onNodeWithContentDescription("Account").assertIsDisplayed() @@ -88,7 +89,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `account menu icon click should show the account switcher`() { - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -105,7 +106,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `account click in the account switcher should send SwitchAccountClick and close switcher`() { // Show the account switcher - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -125,7 +126,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `account long click in the account switcher should show the lock-or-logout dialog and close the switcher`() { // Show the account switcher - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -145,7 +146,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `lock button click in the lock-or-logout dialog should send LockAccountClick action and close the dialog`() { // Show the lock-or-logout dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -162,7 +163,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() { // Show the lock-or-logout dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -180,7 +181,7 @@ class LandingScreenTest : BitwardenComposeTest() { @Test fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() { // Show the logout confirmation dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -200,7 +201,7 @@ class LandingScreenTest : BitwardenComposeTest() { // Show the lock-or-logout dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -216,7 +217,7 @@ class LandingScreenTest : BitwardenComposeTest() { // Show the remove account confirmation dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -522,6 +523,6 @@ private val DEFAULT_STATE = LandingState( selectedEnvironmentType = Environment.Type.US, selectedEnvironmentLabel = Environment.Us.label, dialog = null, - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), isFedRampEnabled = true, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt index 0aaf99cf72..716d9afde5 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt @@ -28,6 +28,7 @@ import io.mockk.just import io.mockk.mockk import io.mockk.runs import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals @@ -659,6 +660,6 @@ private val DEFAULT_STATE = LandingState( selectedEnvironmentType = Environment.Type.US, selectedEnvironmentLabel = Environment.Us.label, dialog = null, - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), isFedRampEnabled = true, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt index 976a7ba926..20661795c4 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt @@ -37,6 +37,7 @@ import io.mockk.mockk import io.mockk.runs import io.mockk.verify import junit.framework.TestCase.assertTrue +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import org.junit.Before @@ -83,7 +84,7 @@ class LoginScreenTest : BitwardenComposeTest() { composeTestRule.onNodeWithContentDescription("Account").assertDoesNotExist() mutableStateFlow.update { - it.copy(accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)) + it.copy(accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY)) } composeTestRule.onNodeWithContentDescription("Account").assertIsDisplayed() @@ -91,7 +92,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `account menu icon click should show the account switcher`() { - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -107,7 +108,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `account click in the account switcher should send SwitchAccountClick and close switcher`() { // Show the account switcher - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -127,7 +128,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `add account click in the account switcher should send AddAccountClick and close switcher`() { // Open the Account Switcher - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -145,7 +146,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `account long click in the account switcher should show the lock-or-logout dialog and close the switcher`() { // Show the account switcher - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -165,7 +166,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `lock button click in the lock-or-logout dialog should send LockAccountClick action and close the dialog`() { // Show the lock-or-logout dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -182,7 +183,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() { // Show the lock-or-logout dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -200,7 +201,7 @@ class LoginScreenTest : BitwardenComposeTest() { @Test fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() { // Show the logout confirmation dialog - val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY) + val accountSummaries = persistentListOf(ACTIVE_ACCOUNT_SUMMARY) mutableStateFlow.update { it.copy(accountSummaries = accountSummaries) } @@ -220,7 +221,7 @@ class LoginScreenTest : BitwardenComposeTest() { // Show the lock-or-logout dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -236,7 +237,7 @@ class LoginScreenTest : BitwardenComposeTest() { // Show the remove account confirmation dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -355,6 +356,6 @@ private val DEFAULT_STATE = passwordInput = "", environmentLabel = "", dialogState = null, - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), shouldShowLoginWithDevice = false, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt index e31c4e22c0..b9b86137b6 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt @@ -27,6 +27,7 @@ import io.mockk.mockkStatic import io.mockk.runs import io.mockk.unmockkStatic import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach @@ -721,7 +722,7 @@ class LoginViewModelTest : BaseViewModelTest() { isLoginButtonEnabled = false, environmentLabel = Environment.Us.label, dialogState = null, - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), shouldShowLoginWithDevice = false, ) } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt index fed4531d84..afe9970ab7 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt @@ -49,6 +49,7 @@ import io.mockk.mockk import io.mockk.runs import io.mockk.slot import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.runTest @@ -344,7 +345,7 @@ class VaultUnlockScreenTest : BitwardenComposeTest() { // Show the lock-or-logout dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -360,7 +361,7 @@ class VaultUnlockScreenTest : BitwardenComposeTest() { // Show the remove account confirmation dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -628,7 +629,7 @@ private val LOCKED_ACCOUNT_SUMMARY = AccountSummary( isVaultUnlocked = false, ) -private val ACCOUNT_SUMMARIES = listOf( +private val ACCOUNT_SUMMARIES = persistentListOf( ACTIVE_ACCOUNT_SUMMARY, LOCKED_ACCOUNT_SUMMARY, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index 62025b9055..499dcdb214 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -36,6 +36,7 @@ import io.mockk.mockkStatic import io.mockk.runs import io.mockk.unmockkStatic import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update @@ -119,7 +120,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { val viewModel = createViewModel() assertEquals( DEFAULT_STATE.copy( - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), avatarColorString = "#ff000000", initials = "", email = "", @@ -326,7 +327,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { avatarColorString = "#00aaaa", initials = "OU", email = "active+test@bitwarden.com", - accountSummaries = listOf( + accountSummaries = persistentListOf( AccountSummary( userId = "activeUserId", name = "Other User", @@ -349,8 +350,9 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { val password = "abc1234" val initialState = DEFAULT_STATE.copy( input = password, - accountSummaries = listOf( - DEFAULT_ACCOUNT.copy(isVaultUnlocked = false) + accountSummaries = persistentListOf( + DEFAULT_ACCOUNT + .copy(isVaultUnlocked = false) .toAccountSummary(true), ), ) @@ -379,8 +381,9 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { val mockFido2GetCredentialsRequest = createMockGetCredentialsRequest(number = 1) val initialState = DEFAULT_STATE.copy( getCredentialsRequest = mockFido2GetCredentialsRequest, - accountSummaries = listOf( - DEFAULT_ACCOUNT.copy(isVaultUnlocked = false) + accountSummaries = persistentListOf( + DEFAULT_ACCOUNT + .copy(isVaultUnlocked = false) .toAccountSummary(isActive = true), ), ) @@ -412,8 +415,9 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { ) val initialState = DEFAULT_STATE.copy( getCredentialsRequest = mockFido2GetCredentialsRequest, - accountSummaries = listOf( - DEFAULT_ACCOUNT.copy(isVaultUnlocked = false) + accountSummaries = persistentListOf( + DEFAULT_ACCOUNT + .copy(isVaultUnlocked = false) .toAccountSummary(isActive = true), ), userId = mockFido2GetCredentialsRequest.userId, @@ -444,8 +448,9 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { createMockFido2CredentialAssertionRequest(number = 1) val initialState = DEFAULT_STATE.copy( fido2CredentialAssertionRequest = mockFido2CredentialAssertionRequest, - accountSummaries = listOf( - DEFAULT_ACCOUNT.copy(isVaultUnlocked = false) + accountSummaries = persistentListOf( + DEFAULT_ACCOUNT + .copy(isVaultUnlocked = false) .toAccountSummary(isActive = true), ), ) @@ -478,8 +483,9 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { ) val initialState = DEFAULT_STATE.copy( fido2CredentialAssertionRequest = mockFido2CredentialAssertionRequest, - accountSummaries = listOf( - DEFAULT_ACCOUNT.copy(isVaultUnlocked = false) + accountSummaries = persistentListOf( + DEFAULT_ACCOUNT + .copy(isVaultUnlocked = false) .toAccountSummary(isActive = true), ), userId = mockFido2CredentialAssertionRequest.userId, @@ -1365,7 +1371,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { private val CIPHER = mockk() private const val USER_ID: String = "activeUserId" private val DEFAULT_STATE: VaultUnlockState = VaultUnlockState( - accountSummaries = listOf( + accountSummaries = persistentListOf( AccountSummary( userId = "activeUserId", name = "Active User", diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt index 59b4320feb..3ccc2d836e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt @@ -372,7 +372,7 @@ class VaultItemListingScreenTest : BitwardenComposeTest() { val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { it.copy( - accountSummaries = listOf(activeAccountSummary), + accountSummaries = persistentListOf(activeAccountSummary), autofillSelectionData = AUTOFILL_SELECTION_DATA, ) } @@ -391,7 +391,7 @@ class VaultItemListingScreenTest : BitwardenComposeTest() { val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { it.copy( - accountSummaries = listOf(activeAccountSummary), + accountSummaries = persistentListOf(activeAccountSummary), autofillSelectionData = AUTOFILL_SELECTION_DATA, ) } @@ -2651,7 +2651,7 @@ private val LOCKED_ACCOUNT_SUMMARY = AccountSummary( isVaultUnlocked = false, ) -private val ACCOUNT_SUMMARIES = listOf( +private val ACCOUNT_SUMMARIES = persistentListOf( ACTIVE_ACCOUNT_SUMMARY, LOCKED_ACCOUNT_SUMMARY, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index 60e4dfb394..dd697ee593 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -384,7 +384,7 @@ class VaultScreenTest : BitwardenComposeTest() { // Show the lock-or-logout dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) @@ -400,7 +400,7 @@ class VaultScreenTest : BitwardenComposeTest() { // Show the remove account confirmation dialog val activeAccountSummary = ACTIVE_ACCOUNT_SUMMARY.copy(isLoggedIn = false) mutableStateFlow.update { - it.copy(accountSummaries = listOf(activeAccountSummary)) + it.copy(accountSummaries = persistentListOf(activeAccountSummary)) } composeTestRule.performAccountIconClick() composeTestRule.performAccountLongClick(activeAccountSummary) 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 786af7446f..057c15b5ea 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 @@ -265,7 +265,7 @@ class VaultViewModelTest : BaseViewModelTest() { val viewModel = createViewModel() assertEquals( DEFAULT_STATE.copy( - accountSummaries = emptyList(), + accountSummaries = persistentListOf(), avatarColorString = "#ff000000", initials = "", showImportActionCard = false, @@ -681,7 +681,7 @@ class VaultViewModelTest : BaseViewModelTest() { appBarTitle = BitwardenString.vaults.asText(), avatarColorString = "#00aaaa", initials = "OU", - accountSummaries = listOf( + accountSummaries = persistentListOf( AccountSummary( userId = "activeUserId", name = "Other User", @@ -768,7 +768,7 @@ class VaultViewModelTest : BaseViewModelTest() { appBarTitle = BitwardenString.vaults.asText(), avatarColorString = "#00aaaa", initials = "OU", - accountSummaries = listOf( + accountSummaries = persistentListOf( AccountSummary( userId = "activeUserId", name = "Other User", @@ -4735,7 +4735,7 @@ private fun createMockVaultState( appBarTitle = BitwardenString.my_vault.asText(), avatarColorString = "#aa00aa", initials = "AU", - accountSummaries = listOf( + accountSummaries = persistentListOf( AccountSummary( userId = "activeUserId", name = "Active User",