From 8a90d77fd7c31fa58d7351ca0b03d95a2a06a078 Mon Sep 17 00:00:00 2001 From: David Perez Date: Thu, 16 Oct 2025 10:43:52 -0500 Subject: [PATCH] Update Item listing and search screens to user immutable lists (#6037) --- .../feature/itemlisting/ItemListingScreen.kt | 8 +- .../itemlisting/ItemListingViewModel.kt | 40 ++++--- .../feature/search/ItemSearchViewModel.kt | 27 +++-- .../SharedVerificationCodesStateExtensions.kt | 5 +- .../listitem/model/SharedCodesDisplayState.kt | 7 +- .../itemlisting/ItemListingScreenTest.kt | 101 +++++++++--------- .../itemlisting/ItemListingViewModelTest.kt | 45 +++++--- .../feature/search/ItemSearchViewModelTest.kt | 16 +-- .../util/SharedVerificationCodesStateTest.kt | 15 +-- 9 files changed, 148 insertions(+), 116 deletions(-) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt index 2c36540758..24a667115f 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt @@ -665,8 +665,8 @@ private fun ContentPreview() { ItemListingContent( state = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf( + favoriteItems = persistentListOf(), + itemList = persistentListOf( VerificationCodeDisplayItem( id = "", title = "Local item", @@ -681,13 +681,13 @@ private fun ContentPreview() { ), ), sharedItems = SharedCodesDisplayState.Codes( - sections = listOf( + sections = persistentListOf( SharedCodesDisplayState.SharedCodesAccountSection( id = "id", label = "longemailaddress+verification+codes@email.com | Bitawrden.eu (1)" .asText(), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( id = "", title = "Shared item", diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt index 501c525b66..68359289e6 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt @@ -1,7 +1,7 @@ package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting -import android.net.Uri import android.os.Parcelable +import androidx.core.net.toUri import androidx.lifecycle.viewModelScope import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemAlgorithm import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity @@ -31,6 +31,9 @@ import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.Text import com.bitwarden.ui.util.asText import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn @@ -478,7 +481,7 @@ class ItemListingViewModel @Inject constructor( SharedVerificationCodesState.Loading, SharedVerificationCodesState.OsVersionNotSupported, SharedVerificationCodesState.SyncNotEnabled, - -> SharedCodesDisplayState.Codes(emptyList()) + -> SharedCodesDisplayState.Codes(persistentListOf()) is SharedVerificationCodesState.Success -> { val viewState = state.viewState as? ItemListingState.ViewState.Content @@ -511,7 +514,8 @@ class ItemListingViewModel @Inject constructor( .value, allowLongPressActions = true, ) - }, + } + .toImmutableList(), itemList = localItems .filter { it.source is AuthenticatorItem.Source.Local && !it.source.isFavorite } .map { @@ -522,7 +526,8 @@ class ItemListingViewModel @Inject constructor( .value, allowLongPressActions = true, ) - }, + } + .toImmutableList(), sharedItems = sharedItemsState, actionCard = action.sharedCodesState.toActionCard(), ) @@ -590,15 +595,18 @@ class ItemListingViewModel @Inject constructor( private fun handleSectionExpandedClick(action: ItemListingAction.SectionExpandedClick) { updateSharedItems { codes -> codes.copy( - sections = codes.sections.map { - it.copy( - isExpanded = if (it == action.section) { - !it.isExpanded - } else { - it.isExpanded - }, - ) - }, + sections = codes + .sections + .map { + it.copy( + isExpanded = if (it == action.section) { + !it.isExpanded + } else { + it.isExpanded + }, + ) + } + .toImmutableList(), ) } } @@ -631,7 +639,7 @@ class ItemListingViewModel @Inject constructor( } private fun String.toAuthenticatorEntityOrNull(): AuthenticatorItemEntity? { - val uri = Uri.parse(this) + val uri = this.toUri() val type = AuthenticatorItemType .entries @@ -747,8 +755,8 @@ data class ItemListingState( @Parcelize data class Content( val actionCard: ActionCardState, - val favoriteItems: List, - val itemList: List, + val favoriteItems: ImmutableList, + val itemList: ImmutableList, val sharedItems: SharedCodesDisplayState, ) : ViewState() { diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModel.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModel.kt index d4469b277b..af1f50b4f3 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModel.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModel.kt @@ -18,6 +18,9 @@ import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.Text import com.bitwarden.ui.util.asText import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -166,7 +169,7 @@ class ItemSearchViewModel @Inject constructor( SharedVerificationCodesState.Loading, SharedVerificationCodesState.OsVersionNotSupported, SharedVerificationCodesState.SyncNotEnabled, - -> SharedCodesDisplayState.Codes(emptyList()) + -> SharedCodesDisplayState.Codes(persistentListOf()) is SharedVerificationCodesState.Success -> { sharedData @@ -184,15 +187,17 @@ class ItemSearchViewModel @Inject constructor( else -> { ItemSearchState.ViewState.Content( - itemList = filteredLocalCodes.map { - it.toDisplayItem( - alertThresholdSeconds = 7, - sharedVerificationCodesState = authenticatorRepository - .sharedCodesStateFlow - .value, - allowLongPressActions = false, - ) - }, + itemList = filteredLocalCodes + .map { + it.toDisplayItem( + alertThresholdSeconds = 7, + sharedVerificationCodesState = authenticatorRepository + .sharedCodesStateFlow + .value, + allowLongPressActions = false, + ) + } + .toImmutableList(), sharedItems = sharedItemsState, ) } @@ -219,7 +224,7 @@ data class ItemSearchState( */ @Parcelize data class Content( - val itemList: List, + val itemList: ImmutableList, val sharedItems: SharedCodesDisplayState, ) : ViewState() { /** diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateExtensions.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateExtensions.kt index 0ed20f5023..313f991416 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateExtensions.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateExtensions.kt @@ -6,6 +6,7 @@ import com.bitwarden.authenticator.ui.platform.components.listitem.model.SharedC import com.bitwarden.authenticator.ui.platform.components.listitem.model.VerificationCodeDisplayItem import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.asText +import kotlinx.collections.immutable.toImmutableList /** * Convert [SharedVerificationCodesState.Success] into [SharedCodesDisplayState.Codes]. @@ -40,12 +41,12 @@ fun SharedVerificationCodesState.Success.toSharedCodesDisplayState( it.key.environmentLabel, it.value.size, ), - codes = it.value, + codes = it.value.toImmutableList(), isExpanded = currentSections .find { section -> section.id == it.key.userId } ?.isExpanded ?: true, ) } - .let { SharedCodesDisplayState.Codes(it) } + .let { SharedCodesDisplayState.Codes(it.toImmutableList()) } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/listitem/model/SharedCodesDisplayState.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/listitem/model/SharedCodesDisplayState.kt index f7a5097604..da949a1647 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/listitem/model/SharedCodesDisplayState.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/listitem/model/SharedCodesDisplayState.kt @@ -2,6 +2,7 @@ package com.bitwarden.authenticator.ui.platform.components.listitem.model import android.os.Parcelable import com.bitwarden.ui.util.Text +import kotlinx.collections.immutable.ImmutableList import kotlinx.parcelize.Parcelize /** @@ -19,7 +20,9 @@ sealed class SharedCodesDisplayState : Parcelable { * Display the given [sections] of verification codes. */ @Parcelize - data class Codes(val sections: List) : SharedCodesDisplayState() + data class Codes( + val sections: ImmutableList, + ) : SharedCodesDisplayState() /** * Models a section of shared authenticator codes to be displayed. @@ -28,7 +31,7 @@ sealed class SharedCodesDisplayState : Parcelable { data class SharedCodesAccountSection( val id: String, val label: Text, - val codes: List, + val codes: ImmutableList, val isExpanded: Boolean, ) : Parcelable diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt index d1fb02c132..3d61d814fe 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreenTest.kt @@ -9,10 +9,10 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTouchInput import androidx.core.net.toUri -import com.bitwarden.authenticator.ui.platform.components.listitem.model.VaultDropdownMenuAction -import com.bitwarden.authenticator.ui.platform.components.listitem.model.SharedCodesDisplayState -import com.bitwarden.authenticator.ui.platform.components.listitem.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.platform.base.AuthenticatorComposeTest +import com.bitwarden.authenticator.ui.platform.components.listitem.model.SharedCodesDisplayState +import com.bitwarden.authenticator.ui.platform.components.listitem.model.VaultDropdownMenuAction +import com.bitwarden.authenticator.ui.platform.components.listitem.model.VerificationCodeDisplayItem import com.bitwarden.authenticator.ui.platform.manager.permissions.FakePermissionManager import com.bitwarden.authenticator.ui.platform.util.startBitwardenAccountSettings import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow @@ -27,6 +27,7 @@ import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.runs import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import org.junit.Before @@ -117,8 +118,8 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = emptyList(), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), sharedItems = SharedCodesDisplayState.Error, ), ) @@ -130,9 +131,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) @@ -146,12 +147,10 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = emptyList(), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), sharedItems = SharedCodesDisplayState.Codes( - sections = listOf( - SHARED_ACCOUNTS_SECTION, - ), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION), ), ), ) @@ -229,9 +228,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.update { it.copy( viewState = ItemListingState.ViewState.Content( - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), actionCard = ItemListingState.ActionCardState.SyncWithBitwarden, ), ) @@ -247,9 +246,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.update { it.copy( viewState = ItemListingState.ViewState.Content( - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), actionCard = ItemListingState.ActionCardState.SyncWithBitwarden, ), ) @@ -279,9 +278,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { fun `on sync with bitwarden action card dismiss in full state should send SyncWithBitwardenDismiss`() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), actionCard = ItemListingState.ActionCardState.SyncWithBitwarden, ), ) @@ -308,9 +307,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { fun `on download bitwarden click in full state should send DownloadBitwardenClick`() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), actionCard = ItemListingState.ActionCardState.DownloadBitwardenApp, ), ) @@ -337,9 +336,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { fun `on download bitwarden dismiss in full state should send DownloadBitwardenDismiss`() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), actionCard = ItemListingState.ActionCardState.DownloadBitwardenApp, ), ) @@ -354,8 +353,8 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE), sharedItems = SharedCodesDisplayState.Error, ), ) @@ -383,8 +382,8 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE.copy(showMoveToBitwarden = false)), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE.copy(showMoveToBitwarden = false)), sharedItems = SharedCodesDisplayState.Error, ), ) @@ -403,9 +402,9 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = emptyList(), - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) } @@ -428,12 +427,10 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE), sharedItems = SharedCodesDisplayState.Codes( - sections = listOf( - SHARED_ACCOUNTS_SECTION, - ), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION), ), ), ) @@ -450,12 +447,12 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { @Test fun `shared codes header click should emit SectionExpandedClick`() { val sharedItems = SharedCodesDisplayState.Codes( - sections = listOf(SHARED_ACCOUNTS_SECTION), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION), ) val viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE), sharedItems = sharedItems, ) mutableStateFlow.value = DEFAULT_STATE.copy(viewState = viewState) @@ -472,12 +469,12 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { @Test fun `shared codes header should be displayed and collapsed when syncing is enabled`() { val sharedItems = SharedCodesDisplayState.Codes( - sections = listOf(SHARED_ACCOUNTS_SECTION), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION), ) val viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE), sharedItems = sharedItems, ) mutableStateFlow.value = DEFAULT_STATE.copy(viewState = viewState) @@ -493,7 +490,7 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = viewState.copy( sharedItems = sharedItems.copy( - sections = listOf(SHARED_ACCOUNTS_SECTION.copy(isExpanded = false)), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION.copy(isExpanded = false)), ), ), ) @@ -512,12 +509,10 @@ class ItemListingScreenTest : AuthenticatorComposeTest() { mutableStateFlow.value = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = listOf(LOCAL_CODE), + favoriteItems = persistentListOf(), + itemList = persistentListOf(LOCAL_CODE), sharedItems = SharedCodesDisplayState.Codes( - sections = listOf( - SHARED_ACCOUNTS_SECTION, - ), + sections = persistentListOf(SHARED_ACCOUNTS_SECTION), ), ), ) @@ -555,7 +550,7 @@ private val LOCAL_CODE = VerificationCodeDisplayItem( private val SHARED_ACCOUNTS_SECTION = SharedCodesDisplayState.SharedCodesAccountSection( id = "id", label = "test@test.com | bitwarden.com (1)".asText(), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( id = "1", title = "bitwarden.com", diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt index 4a9746c5b2..081c3290ea 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModelTest.kt @@ -25,6 +25,8 @@ import io.mockk.just import io.mockk.mockk import io.mockk.runs import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -109,7 +111,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.DownloadBitwardenApp, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) every { settingsRepository.hasUserDismissedDownloadBitwardenCard } returns false @@ -127,7 +129,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.None, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) every { settingsRepository.hasUserDismissedDownloadBitwardenCard } returns true @@ -160,8 +162,12 @@ class ItemListingViewModelTest : BaseViewModelTest() { val expectedState = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = LOCAL_FAVORITE_ITEMS.map { it.copy(showMoveToBitwarden = true) }, - itemList = LOCAL_NON_FAVORITE_ITEMS.map { it.copy(showMoveToBitwarden = true) }, + favoriteItems = LOCAL_FAVORITE_ITEMS + .map { it.copy(showMoveToBitwarden = true) } + .toImmutableList(), + itemList = LOCAL_NON_FAVORITE_ITEMS + .map { it.copy(showMoveToBitwarden = true) } + .toImmutableList(), sharedItems = SHARED_DISPLAY_ITEMS, ), ) @@ -193,8 +199,8 @@ class ItemListingViewModelTest : BaseViewModelTest() { val expectedState = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = emptyList(), - itemList = emptyList(), + favoriteItems = persistentListOf(), + itemList = persistentListOf(), sharedItems = SHARED_DISPLAY_ITEMS, ), ) @@ -223,7 +229,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.None, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) every { settingsRepository.hasUserDismissedDownloadBitwardenCard = true } just runs @@ -271,7 +277,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.None, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) mutableSharedCodesFlow.value = SharedVerificationCodesState.SyncNotEnabled @@ -340,7 +346,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.SyncWithBitwarden, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) every { settingsRepository.hasUserDismissedSyncWithBitwardenCard } returns false @@ -358,7 +364,7 @@ class ItemListingViewModelTest : BaseViewModelTest() { actionCard = ItemListingState.ActionCardState.None, favoriteItems = LOCAL_FAVORITE_ITEMS, itemList = LOCAL_NON_FAVORITE_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(emptyList()), + sharedItems = SharedCodesDisplayState.Codes(persistentListOf()), ), ) every { settingsRepository.hasUserDismissedSyncWithBitwardenCard } returns true @@ -511,10 +517,17 @@ class ItemListingViewModelTest : BaseViewModelTest() { val expectedState = DEFAULT_STATE.copy( viewState = ItemListingState.ViewState.Content( actionCard = ItemListingState.ActionCardState.None, - favoriteItems = LOCAL_FAVORITE_ITEMS.map { it.copy(showMoveToBitwarden = true) }, - itemList = LOCAL_NON_FAVORITE_ITEMS.map { it.copy(showMoveToBitwarden = true) }, + favoriteItems = LOCAL_FAVORITE_ITEMS + .map { it.copy(showMoveToBitwarden = true) } + .toImmutableList(), + itemList = LOCAL_NON_FAVORITE_ITEMS + .map { it.copy(showMoveToBitwarden = true) } + .toImmutableList(), sharedItems = SHARED_DISPLAY_ITEMS.copy( - sections = SHARED_DISPLAY_ITEMS.sections.map { it.copy(isExpanded = false) }, + sections = SHARED_DISPLAY_ITEMS + .sections + .map { it.copy(isExpanded = false) } + .toImmutableList(), ), ), ) @@ -614,5 +627,7 @@ private val LOCAL_DISPLAY_ITEMS = LOCAL_VERIFICATION_ITEMS.map { private val SHARED_DISPLAY_ITEMS = SharedVerificationCodesState.Success(SHARED_VERIFICATION_ITEMS) .toSharedCodesDisplayState(AUTHENTICATOR_ALERT_SECONDS) -private val LOCAL_FAVORITE_ITEMS = LOCAL_DISPLAY_ITEMS.filter { it.favorite } -private val LOCAL_NON_FAVORITE_ITEMS = LOCAL_DISPLAY_ITEMS.filterNot { it.favorite } +private val LOCAL_FAVORITE_ITEMS = LOCAL_DISPLAY_ITEMS.filter { it.favorite }.toImmutableList() +private val LOCAL_NON_FAVORITE_ITEMS = LOCAL_DISPLAY_ITEMS + .filterNot { it.favorite } + .toImmutableList() diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModelTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModelTest.kt index 1c28e8bfb9..b4839cdb03 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModelTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/search/ItemSearchViewModelTest.kt @@ -17,6 +17,8 @@ import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.asText import io.mockk.every import io.mockk.mockk +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -75,7 +77,7 @@ class ItemSearchViewModelTest : BaseViewModelTest() { assertEquals( ItemSearchState.ViewState.Content( itemList = LOCAL_DISPLAY_ITEMS, - sharedItems = SharedCodesDisplayState.Codes(sections = emptyList()), + sharedItems = SharedCodesDisplayState.Codes(sections = persistentListOf()), ), viewModel.stateFlow.value.viewState, ) @@ -91,8 +93,10 @@ class ItemSearchViewModelTest : BaseViewModelTest() { assertEquals( ItemSearchState.ViewState.Content( - itemList = LOCAL_DISPLAY_ITEMS.map { it.copy(showMoveToBitwarden = false) }, - sharedItems = SharedCodesDisplayState.Codes(sections = emptyList()), + itemList = LOCAL_DISPLAY_ITEMS + .map { it.copy(showMoveToBitwarden = false) } + .toImmutableList(), + sharedItems = SharedCodesDisplayState.Codes(sections = persistentListOf()), ), viewModel.stateFlow.value.viewState, ) @@ -122,7 +126,7 @@ private val SHARED_ITEMS = listOf( ) private val SHARED_DISPLAY_ITEMS = SharedCodesDisplayState.Codes( - sections = listOf( + sections = persistentListOf( SharedCodesDisplayState.SharedCodesAccountSection( id = "mockUserId-2", label = BitwardenString.shared_accounts_header.asText( @@ -130,7 +134,7 @@ private val SHARED_DISPLAY_ITEMS = SharedCodesDisplayState.Codes( "mockkEnvironmentLabel-2", 1, ), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( id = "mockId-2", title = "mockIssuer-2", @@ -149,7 +153,7 @@ private val SHARED_DISPLAY_ITEMS = SharedCodesDisplayState.Codes( ), ) -private val LOCAL_DISPLAY_ITEMS = listOf( +private val LOCAL_DISPLAY_ITEMS = persistentListOf( VerificationCodeDisplayItem( id = LOCAL_ITEMS[0].id, authCode = LOCAL_ITEMS[0].code, diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateTest.kt index fe83a58940..cb95b6323a 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/util/SharedVerificationCodesStateTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.authenticator.ui.platform.components.listitem.model.SharedC import com.bitwarden.authenticator.ui.platform.components.listitem.model.VerificationCodeDisplayItem import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.asText +import kotlinx.collections.immutable.persistentListOf import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -15,7 +16,7 @@ class SharedVerificationCodesStateTest { @Test fun `toSharedCodesDisplayState on empty list should return empty list`() { val state = SharedVerificationCodesState.Success(emptyList()) - val expected = SharedCodesDisplayState.Codes(emptyList()) + val expected = SharedCodesDisplayState.Codes(persistentListOf()) assertEquals( expected, state.toSharedCodesDisplayState(ALERT_THRESHOLD), @@ -59,7 +60,7 @@ class SharedVerificationCodesStateTest { ), ) val expected = SharedCodesDisplayState.Codes( - sections = listOf( + sections = persistentListOf( SharedCodesDisplayState.SharedCodesAccountSection( id = "user1", label = BitwardenString.shared_accounts_header.asText( @@ -67,7 +68,7 @@ class SharedVerificationCodesStateTest { "bitwarden.com", 1, ), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( authCode = "123456", periodSeconds = 30, @@ -90,7 +91,7 @@ class SharedVerificationCodesStateTest { "bitwarden.eu", 1, ), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( authCode = "987654", periodSeconds = 30, @@ -151,7 +152,7 @@ class SharedVerificationCodesStateTest { ), ) val expected = SharedCodesDisplayState.Codes( - sections = listOf( + sections = persistentListOf( SharedCodesDisplayState.SharedCodesAccountSection( id = "user1", label = BitwardenString.shared_accounts_header.asText( @@ -159,7 +160,7 @@ class SharedVerificationCodesStateTest { "bitwarden.com", 1, ), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( authCode = "123456", periodSeconds = 30, @@ -182,7 +183,7 @@ class SharedVerificationCodesStateTest { "bitwarden.eu", 1, ), - codes = listOf( + codes = persistentListOf( VerificationCodeDisplayItem( authCode = "987654", periodSeconds = 30,