mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 20:07:59 -06:00
PM-20152: Remove import logins flow feature flag (#5580)
This commit is contained in:
parent
2f2ec71fc4
commit
e665c386ff
@ -7,7 +7,6 @@ import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.CoachMarkTourType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@ -31,7 +30,6 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val autofillEnabledManager: AutofillEnabledManager,
|
||||
) : FirstTimeActionManager {
|
||||
|
||||
@ -101,16 +99,9 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||
.activeUserIdChangesFlow
|
||||
.filterNotNull()
|
||||
.flatMapLatest {
|
||||
combine(
|
||||
getShowImportLoginsSettingBadgeFlowInternal(userId = it),
|
||||
featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow),
|
||||
) { showImportLogins, importLoginsEnabled ->
|
||||
val shouldShowImportLoginsSettings = showImportLogins && importLoginsEnabled
|
||||
listOf(shouldShowImportLoginsSettings)
|
||||
}
|
||||
.map { list ->
|
||||
list.count { showImportLogins -> showImportLogins }
|
||||
}
|
||||
getShowImportLoginsSettingBadgeFlowInternal(userId = it)
|
||||
.map { showImportLogins -> listOf(showImportLogins) }
|
||||
.map { list -> list.count { showImportLogins -> showImportLogins } }
|
||||
}
|
||||
.stateIn(
|
||||
scope = unconfinedScope,
|
||||
|
||||
@ -353,14 +353,12 @@ object PlatformManagerModule {
|
||||
settingsDiskSource: SettingsDiskSource,
|
||||
vaultDiskSource: VaultDiskSource,
|
||||
dispatcherManager: DispatcherManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
autofillEnabledManager: AutofillEnabledManager,
|
||||
): FirstTimeActionManager = FirstTimeActionManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
dispatcherManager = dispatcherManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
autofillEnabledManager = autofillEnabledManager,
|
||||
)
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ sealed class FlagKey<out T : Any> {
|
||||
val activeFlags: List<FlagKey<*>> by lazy {
|
||||
listOf(
|
||||
EmailVerification,
|
||||
ImportLoginsFlow,
|
||||
CredentialExchangeProtocolImport,
|
||||
CredentialExchangeProtocolExport,
|
||||
SingleTapPasskeyCreation,
|
||||
@ -42,14 +41,6 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the import logins feature.
|
||||
*/
|
||||
data object ImportLoginsFlow : FlagKey<Boolean>() {
|
||||
override val keyName: String = "import-logins-flow"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding hte feature flag key for the Credential Exchange Protocol (CXP) import
|
||||
* feature.
|
||||
|
||||
@ -26,7 +26,6 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
}
|
||||
|
||||
FlagKey.EmailVerification,
|
||||
FlagKey.ImportLoginsFlow,
|
||||
FlagKey.CredentialExchangeProtocolImport,
|
||||
FlagKey.CredentialExchangeProtocolExport,
|
||||
FlagKey.CipherKeyEncryption,
|
||||
@ -77,7 +76,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
-> this.keyName
|
||||
|
||||
FlagKey.EmailVerification -> stringResource(BitwardenString.email_verification)
|
||||
FlagKey.ImportLoginsFlow -> stringResource(BitwardenString.import_logins_flow)
|
||||
FlagKey.CredentialExchangeProtocolImport -> stringResource(BitwardenString.cxp_import)
|
||||
FlagKey.CredentialExchangeProtocolExport -> stringResource(BitwardenString.cxp_export)
|
||||
FlagKey.CipherKeyEncryption -> stringResource(BitwardenString.cipher_key_encryption)
|
||||
|
||||
@ -21,7 +21,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitwarden.ui.platform.base.util.EventsEffect
|
||||
@ -34,13 +33,10 @@ import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.actionCardExitAnimation
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.rememberBitwardenSnackbarHostState
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
|
||||
/**
|
||||
* Displays the vault settings screen.
|
||||
@ -54,7 +50,6 @@ fun VaultSettingsScreen(
|
||||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToImportLogins: () -> Unit,
|
||||
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
@ -64,14 +59,7 @@ fun VaultSettingsScreen(
|
||||
VaultSettingsEvent.NavigateBack -> onNavigateBack()
|
||||
VaultSettingsEvent.NavigateToExportVault -> onNavigateToExportVault()
|
||||
VaultSettingsEvent.NavigateToFolders -> onNavigateToFolders()
|
||||
is VaultSettingsEvent.NavigateToImportVault -> {
|
||||
if (state.isNewImportLoginsFlowEnabled) {
|
||||
onNavigateToImportLogins()
|
||||
} else {
|
||||
intentManager.launchUri(event.url.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
is VaultSettingsEvent.NavigateToImportVault -> onNavigateToImportLogins()
|
||||
is VaultSettingsEvent.ShowSnackbar -> snackbarHostState.showSnackbar(event.data)
|
||||
}
|
||||
}
|
||||
@ -105,7 +93,7 @@ fun VaultSettingsScreen(
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
AnimatedVisibility(
|
||||
visible = state.shouldShowImportCard,
|
||||
visible = state.showImportActionCard,
|
||||
label = "ImportLoginsActionCard",
|
||||
exit = actionCardExitAnimation(),
|
||||
) {
|
||||
@ -159,38 +147,18 @@ fun VaultSettingsScreen(
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
if (state.isNewImportLoginsFlowEnabled) {
|
||||
BitwardenTextRow(
|
||||
text = stringResource(BitwardenString.import_items),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("ImportItemsLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
} else {
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(BitwardenString.import_items),
|
||||
onConfirmClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = false,
|
||||
dialogTitle = stringResource(id = BitwardenString.continue_to_web_app),
|
||||
dialogMessage = stringResource(
|
||||
id = BitwardenString.you_can_import_data_to_your_vault_on_x,
|
||||
state.importUrl,
|
||||
),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("ImportItemsLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
BitwardenTextRow(
|
||||
text = stringResource(BitwardenString.import_items),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("ImportItemsLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.data.repository.util.toBaseWebVaultImportUrl
|
||||
import com.bitwarden.ui.platform.base.BackgroundEvent
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
@ -24,31 +20,17 @@ import javax.inject.Inject
|
||||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
class VaultSettingsViewModel @Inject constructor(
|
||||
environmentRepository: EnvironmentRepository,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
snackbarRelayManager: SnackbarRelayManager,
|
||||
private val firstTimeActionManager: FirstTimeActionManager,
|
||||
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
|
||||
initialState = run {
|
||||
val firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState
|
||||
VaultSettingsState(
|
||||
importUrl = environmentRepository
|
||||
.environment
|
||||
.environmentUrlData
|
||||
.toBaseWebVaultImportUrl,
|
||||
isNewImportLoginsFlowEnabled = featureFlagManager
|
||||
.getFeatureFlag(FlagKey.ImportLoginsFlow),
|
||||
showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
|
||||
)
|
||||
},
|
||||
) {
|
||||
init {
|
||||
featureFlagManager
|
||||
.getFeatureFlagFlow(FlagKey.ImportLoginsFlow)
|
||||
.map { VaultSettingsAction.Internal.ImportLoginsFeatureFlagChanged(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
firstTimeActionManager
|
||||
.firstTimeStateFlow
|
||||
.map {
|
||||
@ -78,10 +60,6 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
|
||||
private fun handleInternalAction(action: VaultSettingsAction.Internal) {
|
||||
when (action) {
|
||||
is VaultSettingsAction.Internal.ImportLoginsFeatureFlagChanged -> {
|
||||
handleImportLoginsFeatureFlagChanged(action)
|
||||
}
|
||||
|
||||
is VaultSettingsAction.Internal.UserFirstTimeStateChanged -> {
|
||||
handleUserFirstTimeStateChanged(action)
|
||||
}
|
||||
@ -99,12 +77,12 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleImportLoginsCardDismissClicked() {
|
||||
if (!state.shouldShowImportCard) return
|
||||
if (!state.showImportActionCard) return
|
||||
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||
}
|
||||
|
||||
private fun handleImportLoginsCardClicked() {
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportVault(state.importUrl))
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportVault)
|
||||
}
|
||||
|
||||
private fun handleUserFirstTimeStateChanged(
|
||||
@ -115,14 +93,6 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleImportLoginsFeatureFlagChanged(
|
||||
action: VaultSettingsAction.Internal.ImportLoginsFeatureFlagChanged,
|
||||
) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isNewImportLoginsFlowEnabled = action.isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBackClicked() {
|
||||
sendEvent(VaultSettingsEvent.NavigateBack)
|
||||
}
|
||||
@ -136,9 +106,7 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleImportItemsClicked() {
|
||||
sendEvent(
|
||||
VaultSettingsEvent.NavigateToImportVault(state.importUrl),
|
||||
)
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportVault)
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,16 +114,8 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
* Models the state for the VaultSettingScreen.
|
||||
*/
|
||||
data class VaultSettingsState(
|
||||
val importUrl: String,
|
||||
val isNewImportLoginsFlowEnabled: Boolean,
|
||||
private val showImportActionCard: Boolean,
|
||||
) {
|
||||
/**
|
||||
* Should only show the import action card if the import logins feature flag is enabled.
|
||||
*/
|
||||
val shouldShowImportCard: Boolean
|
||||
get() = showImportActionCard && isNewImportLoginsFlowEnabled
|
||||
}
|
||||
val showImportActionCard: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Models events for the vault screen.
|
||||
@ -169,7 +129,7 @@ sealed class VaultSettingsEvent {
|
||||
/**
|
||||
* Navigate to the import vault URL.
|
||||
*/
|
||||
data class NavigateToImportVault(val url: String) : VaultSettingsEvent()
|
||||
data object NavigateToImportVault : VaultSettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the Export Vault screen.
|
||||
@ -225,14 +185,6 @@ sealed class VaultSettingsAction {
|
||||
* Internal actions not performed by user interation
|
||||
*/
|
||||
sealed class Internal : VaultSettingsAction() {
|
||||
|
||||
/**
|
||||
* Indicates that the import logins feature flag has changed.
|
||||
*/
|
||||
data class ImportLoginsFeatureFlagChanged(
|
||||
val isEnabled: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates user first time state has changed.
|
||||
*/
|
||||
|
||||
@ -164,14 +164,7 @@ class VaultViewModel @Inject constructor(
|
||||
|
||||
authRepository
|
||||
.userStateFlow
|
||||
.combine(
|
||||
featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow),
|
||||
) { userState, importLoginsEnabled ->
|
||||
VaultAction.Internal.UserStateUpdateReceive(
|
||||
userState = userState,
|
||||
importLoginsFlowEnabled = importLoginsEnabled,
|
||||
)
|
||||
}
|
||||
.map { VaultAction.Internal.UserStateUpdateReceive(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
@ -805,8 +798,6 @@ class VaultViewModel @Inject constructor(
|
||||
.any(),
|
||||
)
|
||||
val appBarTitle = vaultFilterData.toAppBarTitle()
|
||||
val shouldShowImportActionCard = action.importLoginsFlowEnabled &&
|
||||
firstTimeState.showImportLoginsCard
|
||||
|
||||
mutableStateFlow.update {
|
||||
val accountSummaries = userState.toAccountSummaries()
|
||||
@ -818,7 +809,7 @@ class VaultViewModel @Inject constructor(
|
||||
accountSummaries = accountSummaries,
|
||||
vaultFilterData = vaultFilterData,
|
||||
isPremium = userState.activeAccount.isPremium,
|
||||
showImportActionCard = shouldShowImportActionCard,
|
||||
showImportActionCard = firstTimeState.showImportLoginsCard,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1704,7 +1695,6 @@ sealed class VaultAction {
|
||||
*/
|
||||
data class UserStateUpdateReceive(
|
||||
val userState: UserState?,
|
||||
val importLoginsFlowEnabled: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
|
||||
@ -14,7 +14,6 @@ import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.CoachMarkTourType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@ -35,13 +34,6 @@ class FirstTimeActionManagerTest {
|
||||
private val vaultDiskSource = mockk<VaultDiskSource> {
|
||||
every { getCiphersFlow(any()) } returns mutableCiphersListFlow
|
||||
}
|
||||
|
||||
private val mutableImportLoginsFlow = MutableStateFlow(false)
|
||||
private val mutableOnboardingFeatureFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager = mockk<FeatureFlagManager> {
|
||||
every { getFeatureFlagFlow(FlagKey.ImportLoginsFlow) } returns mutableImportLoginsFlow
|
||||
}
|
||||
|
||||
private val mutableAutofillEnabledFlow = MutableStateFlow(false)
|
||||
private val autofillEnabledManager = mockk<AutofillEnabledManager> {
|
||||
every { isAutofillEnabledStateFlow } returns mutableAutofillEnabledFlow
|
||||
@ -52,14 +44,13 @@ class FirstTimeActionManagerTest {
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
settingsDiskSource = fakeSettingsDiskSource,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
featureFlagManager = featureFlagManager,
|
||||
dispatcherManager = FakeDispatcherManager(),
|
||||
autofillEnabledManager = autofillEnabledManager,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `allAutoFillSettingsBadgeCountFlow should emit the value of flags set to true and update when saved value is changed or autofill enabled state changes`() =
|
||||
fun `allAutoFillSettingsBadgeCountFlow should update when saved value is changed or autofill enabled state changes`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.allAutofillSettingsBadgeCountFlow.test {
|
||||
@ -84,81 +75,66 @@ class FirstTimeActionManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `allSecuritySettingsBadgeCountFlow should emit the value of flags set to true and update when changed`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.allSecuritySettingsBadgeCountFlow.test {
|
||||
assertEquals(0, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = false,
|
||||
)
|
||||
assertEquals(0, awaitItem())
|
||||
}
|
||||
fun `allSecuritySettingsBadgeCountFlow should update when changed`() = runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.allSecuritySettingsBadgeCountFlow.test {
|
||||
assertEquals(0, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = false,
|
||||
)
|
||||
assertEquals(0, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `allSettingsBadgeCountFlow should emit the value of all flags set to true and update when changed`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.allSettingsBadgeCountFlow.test {
|
||||
assertEquals(0, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(2, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = false,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
// for the import logins count it is dependent on the feature flag state and
|
||||
// cipher list being empty
|
||||
mutableImportLoginsFlow.update { true }
|
||||
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||
assertEquals(2, awaitItem())
|
||||
}
|
||||
fun `allSettingsBadgeCountFlow should update when changed`() = runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.allSettingsBadgeCountFlow.test {
|
||||
assertEquals(0, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = true,
|
||||
)
|
||||
assertEquals(2, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(
|
||||
userId = USER_ID,
|
||||
showBadge = false,
|
||||
)
|
||||
assertEquals(1, awaitItem())
|
||||
// for the import logins count it is dependent on the cipher list being empty
|
||||
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||
assertEquals(2, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `allVaultSettingsBadgeCountFlow should emit the value of all flags set to true and update when dependent states are changed changed`() =
|
||||
fun `allVaultSettingsBadgeCountFlow should update when dependent states are changed changed`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val mockCipher = mockk<SyncResponseJson.Cipher>(relaxed = true)
|
||||
// For the import logins count to register, the feature flag for ImportLoginsFlow must
|
||||
// be enabled, the cipher list must not be empty, and the value saved to disk should be
|
||||
// true.
|
||||
// For the import logins count to register, the cipher list must not be empty, and the
|
||||
// value saved to disk should be true.
|
||||
firstTimeActionManager.allVaultSettingsBadgeCountFlow.test {
|
||||
assertEquals(0, awaitItem())
|
||||
mutableImportLoginsFlow.update { true }
|
||||
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||
assertEquals(1, awaitItem())
|
||||
mutableImportLoginsFlow.update { false }
|
||||
assertEquals(0, awaitItem())
|
||||
mutableImportLoginsFlow.update { true }
|
||||
assertEquals(1, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, false)
|
||||
assertEquals(0, awaitItem())
|
||||
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||
assertEquals(1, awaitItem())
|
||||
mutableCiphersListFlow.update {
|
||||
listOf(mockCipher)
|
||||
}
|
||||
mutableCiphersListFlow.update { listOf(mockCipher) }
|
||||
assertEquals(0, awaitItem())
|
||||
}
|
||||
}
|
||||
@ -167,8 +143,7 @@ class FirstTimeActionManagerTest {
|
||||
fun `firstTimeStateFlow should emit changes when items in the first time state change`() =
|
||||
runTest {
|
||||
firstTimeActionManager.firstTimeStateFlow.test {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
assertEquals(
|
||||
FirstTimeState(
|
||||
showImportLoginsCard = true,
|
||||
@ -208,40 +183,35 @@ class FirstTimeActionManagerTest {
|
||||
|
||||
@Test
|
||||
fun `storeShowAutoFillSettingBadge should store value of false to disk`() {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.storeShowAutoFillSettingBadge(showBadge = false)
|
||||
assertFalse(fakeSettingsDiskSource.getShowAutoFillSettingBadge(userId = USER_ID)!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeShowAutoFillSettingBadge should store value of true to disk`() {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.storeShowAutoFillSettingBadge(showBadge = true)
|
||||
assertTrue(fakeSettingsDiskSource.getShowAutoFillSettingBadge(userId = USER_ID)!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getShowAutoFillSettingBadge should return the value saved to disk`() {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.storeShowAutoFillSettingBadge(showBadge = true)
|
||||
assertTrue(fakeSettingsDiskSource.getShowAutoFillSettingBadge(userId = USER_ID)!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeShowUnlockSettingBadge should store value of false to disk`() {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
|
||||
assertFalse(fakeSettingsDiskSource.getShowUnlockSettingBadge(userId = USER_ID)!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeShowUnlockSettingBadge should store value of true to disk`() {
|
||||
fakeAuthDiskSource.userState =
|
||||
MOCK_USER_STATE
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = true)
|
||||
assertTrue(fakeSettingsDiskSource.getShowUnlockSettingBadge(userId = USER_ID)!!)
|
||||
}
|
||||
@ -303,8 +273,6 @@ class FirstTimeActionManagerTest {
|
||||
@Test
|
||||
fun `shouldShowAddLoginCoachMarkFlow updates when disk source updates`() = runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
// Enable the feature for this test.
|
||||
mutableOnboardingFeatureFlow.update { true }
|
||||
firstTimeActionManager.shouldShowAddLoginCoachMarkFlow.test {
|
||||
assertTrue(awaitItem())
|
||||
fakeSettingsDiskSource.storeShouldShowAddLoginCoachMark(shouldShow = false)
|
||||
@ -325,8 +293,6 @@ class FirstTimeActionManagerTest {
|
||||
every { organizationId } returns null
|
||||
}
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
// Enable feature flag so flow emits updates from disk.
|
||||
mutableOnboardingFeatureFlow.update { true }
|
||||
mutableCiphersListFlow.update {
|
||||
listOf(
|
||||
mockJsonWithNoLogin,
|
||||
@ -356,8 +322,6 @@ class FirstTimeActionManagerTest {
|
||||
@Test
|
||||
fun `shouldShowGeneratorCoachMarkFlow updates when disk source updates`() = runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
// Enable feature flag so flow emits updates from disk.
|
||||
mutableOnboardingFeatureFlow.update { true }
|
||||
firstTimeActionManager.shouldShowGeneratorCoachMarkFlow.test {
|
||||
assertTrue(awaitItem())
|
||||
fakeSettingsDiskSource.storeShouldShowGeneratorCoachMark(shouldShow = false)
|
||||
@ -378,8 +342,6 @@ class FirstTimeActionManagerTest {
|
||||
every { organizationId } returns null
|
||||
}
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
// Enable feature flag so flow emits updates from disk.
|
||||
mutableOnboardingFeatureFlow.update { true }
|
||||
mutableCiphersListFlow.update {
|
||||
listOf(
|
||||
mockJsonWithNoLogin,
|
||||
@ -406,8 +368,6 @@ class FirstTimeActionManagerTest {
|
||||
every { organizationId } returns "1234"
|
||||
}
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
// Enable feature flag so flow emits updates from disk.
|
||||
mutableOnboardingFeatureFlow.update { true }
|
||||
mutableCiphersListFlow.update {
|
||||
listOf(mockJsonWithLoginAndWithOrganizationId)
|
||||
}
|
||||
|
||||
@ -13,10 +13,6 @@ class FlagKeyTest {
|
||||
FlagKey.EmailVerification.keyName,
|
||||
"email-verification",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.ImportLoginsFlow.keyName,
|
||||
"import-logins-flow",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.CredentialExchangeProtocolImport.keyName,
|
||||
"cxp-import-mobile",
|
||||
@ -56,7 +52,6 @@ class FlagKeyTest {
|
||||
assertTrue(
|
||||
listOf(
|
||||
FlagKey.EmailVerification,
|
||||
FlagKey.ImportLoginsFlow,
|
||||
FlagKey.CredentialExchangeProtocolImport,
|
||||
FlagKey.CredentialExchangeProtocolExport,
|
||||
FlagKey.SingleTapPasskeyCreation,
|
||||
|
||||
@ -145,7 +145,6 @@ class DebugMenuViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val DEFAULT_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf(
|
||||
FlagKey.EmailVerification to true,
|
||||
FlagKey.ImportLoginsFlow to true,
|
||||
FlagKey.CredentialExchangeProtocolImport to true,
|
||||
FlagKey.CredentialExchangeProtocolExport to true,
|
||||
FlagKey.SingleTapPasskeyCreation to true,
|
||||
@ -157,7 +156,6 @@ private val DEFAULT_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf
|
||||
|
||||
private val UPDATED_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf(
|
||||
FlagKey.EmailVerification to false,
|
||||
FlagKey.ImportLoginsFlow to false,
|
||||
FlagKey.CredentialExchangeProtocolImport to false,
|
||||
FlagKey.CredentialExchangeProtocolExport to false,
|
||||
FlagKey.SingleTapPasskeyCreation to false,
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsNotDisplayed
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.core.net.toUri
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
@ -22,7 +17,6 @@ import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -36,14 +30,9 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultSettingsEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(
|
||||
VaultSettingsState(
|
||||
importUrl = "testUrl/#/tools/import",
|
||||
isNewImportLoginsFlowEnabled = false,
|
||||
showImportActionCard = false,
|
||||
),
|
||||
)
|
||||
private val intentManager: IntentManager = mockk(relaxed = true) {
|
||||
every { launchUri(any()) } just runs
|
||||
}
|
||||
|
||||
val viewModel = mockk<VaultSettingsViewModel>(relaxed = true) {
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
@ -52,9 +41,7 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setContent(
|
||||
intentManager = intentManager,
|
||||
) {
|
||||
setContent {
|
||||
VaultSettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
@ -80,42 +67,6 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `import items click should display dialog and confirming should send ImportItemsClick`() {
|
||||
composeTestRule.onNodeWithText("Import items").performClick()
|
||||
composeTestRule
|
||||
.onNodeWithText("Continue")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `import items click should display dialog & canceling should not send ImportItemsClick`() {
|
||||
composeTestRule.onNodeWithText("Import items").performClick()
|
||||
composeTestRule
|
||||
.onNodeWithText("Cancel")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
verify(exactly = 0) {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `import items click should display dialog with importUrl`() {
|
||||
composeTestRule.onNodeWithText("Import items").performClick()
|
||||
composeTestRule
|
||||
.onNodeWithText(mutableStateFlow.value.importUrl, substring = true)
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateBack should call onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateBack)
|
||||
@ -135,43 +86,20 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToImportVault should invoke IntentManager not lambda`() {
|
||||
val testUrl = "testUrl"
|
||||
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToImportVault(testUrl))
|
||||
verify {
|
||||
intentManager.launchUri(testUrl.toUri())
|
||||
}
|
||||
assertFalse(onNavigateToImportLoginsCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when new logins feature flag is enabled send action right when import items is clicked`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isNewImportLoginsFlowEnabled = true)
|
||||
}
|
||||
fun `send action right when import items is clicked`() {
|
||||
composeTestRule.onNodeWithText("Import items").performClick()
|
||||
verify { viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when new logins feature flag is enabled NavigateToImportVault should invoke lambda`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isNewImportLoginsFlowEnabled = true)
|
||||
}
|
||||
val testUrl = "testUrl"
|
||||
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToImportVault(testUrl))
|
||||
fun `NavigateToImportVault should invoke lambda`() {
|
||||
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToImportVault)
|
||||
assertTrue(onNavigateToImportLoginsCalled)
|
||||
verify(exactly = 0) { intentManager.launchUri(testUrl.toUri()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when new show action card is true the import logins card should show`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
showImportActionCard = true,
|
||||
isNewImportLoginsFlowEnabled = true,
|
||||
)
|
||||
}
|
||||
mutableStateFlow.update { it.copy(showImportActionCard = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Import saved logins")
|
||||
.performScrollTo()
|
||||
@ -186,12 +114,7 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `when action card is visible clicking the close icon should send correct action`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
showImportActionCard = true,
|
||||
isNewImportLoginsFlowEnabled = true,
|
||||
)
|
||||
}
|
||||
mutableStateFlow.update { it.copy(showImportActionCard = true) }
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Close")
|
||||
.performScrollTo()
|
||||
@ -203,12 +126,7 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `when action card is visible get started button should send correct action`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
showImportActionCard = true,
|
||||
isNewImportLoginsFlowEnabled = true,
|
||||
)
|
||||
}
|
||||
mutableStateFlow.update { it.copy(showImportActionCard = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Get started")
|
||||
.performScrollTo()
|
||||
|
||||
@ -4,11 +4,8 @@ import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
@ -26,12 +23,6 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
private val environmentRepository = FakeEnvironmentRepository()
|
||||
private val mutableImportLoginsFlagFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager = mockk<FeatureFlagManager> {
|
||||
every { getFeatureFlagFlow(FlagKey.ImportLoginsFlow) } returns mutableImportLoginsFlagFlow
|
||||
every { getFeatureFlag(FlagKey.ImportLoginsFlow) } returns false
|
||||
}
|
||||
private val mutableFirstTimeStateFlow = MutableStateFlow(DEFAULT_FIRST_TIME_STATE)
|
||||
private val firstTimeActionManager = mockk<FirstTimeActionManager> {
|
||||
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
|
||||
@ -68,44 +59,25 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ImportItemsClick should emit send NavigateToImportVault with correct url`() = runTest {
|
||||
fun `ImportItemsClick should emit send NavigateToImportVault`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val expected = "https://vault.bitwarden.com/#/tools/import"
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||
assertEquals(
|
||||
VaultSettingsEvent.NavigateToImportVault(expected),
|
||||
VaultSettingsEvent.NavigateToImportVault,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ImportLoginsFeatureFlagChanged should update state`() {
|
||||
val viewModel = createViewModel()
|
||||
assertFalse(
|
||||
viewModel.stateFlow.value.isNewImportLoginsFlowEnabled,
|
||||
)
|
||||
mutableImportLoginsFlagFlow.update { true }
|
||||
assertTrue(viewModel.stateFlow.value.isNewImportLoginsFlowEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldShowImportCard should update when first time state changes`() = runTest {
|
||||
mutableImportLoginsFlagFlow.update { true }
|
||||
val viewModel = createViewModel()
|
||||
assertTrue(viewModel.stateFlow.value.shouldShowImportCard)
|
||||
assertTrue(viewModel.stateFlow.value.showImportActionCard)
|
||||
mutableFirstTimeStateFlow.update {
|
||||
it.copy(showImportLoginsCardInSettings = false)
|
||||
}
|
||||
assertFalse(viewModel.stateFlow.value.shouldShowImportCard)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldShowImportCard should be false when feature flag not enabled`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
mutableImportLoginsFlagFlow.update { false }
|
||||
assertFalse(viewModel.stateFlow.value.shouldShowImportCard)
|
||||
assertFalse(viewModel.stateFlow.value.showImportActionCard)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@ -113,12 +85,10 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
fun `ImportLoginsCardCtaClick action should set repository value to false and send navigation event`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
val expected = "https://vault.bitwarden.com/#/tools/import"
|
||||
mutableImportLoginsFlagFlow.update { true }
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardCtaClick)
|
||||
assertEquals(
|
||||
VaultSettingsEvent.NavigateToImportVault(url = expected),
|
||||
VaultSettingsEvent.NavigateToImportVault,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
@ -129,7 +99,6 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `ImportLoginsCardDismissClick action should set repository value to false `() = runTest {
|
||||
mutableImportLoginsFlagFlow.update { true }
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
||||
verify(exactly = 1) {
|
||||
@ -141,6 +110,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
@Test
|
||||
fun `ImportLoginsCardDismissClick action should not set repository value to false if already false`() =
|
||||
runTest {
|
||||
mutableFirstTimeStateFlow.update { it.copy(showImportLoginsCardInSettings = false) }
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
||||
verify(exactly = 0) {
|
||||
@ -159,8 +129,6 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel(
|
||||
environmentRepository = environmentRepository,
|
||||
featureFlagManager = featureFlagManager,
|
||||
firstTimeActionManager = firstTimeActionManager,
|
||||
snackbarRelayManager = snackbarRelayManager,
|
||||
)
|
||||
|
||||
@ -73,7 +73,6 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
@ -172,13 +171,9 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
every { trackEvent(event = any()) } just runs
|
||||
}
|
||||
|
||||
private val mutableImportLoginsFeatureFlow = MutableStateFlow(true)
|
||||
private val mutableRemoveCardPolicyFeatureFlow = MutableStateFlow(false)
|
||||
private val mutableSshKeyVaultItemsEnabledFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every {
|
||||
getFeatureFlagFlow(FlagKey.ImportLoginsFlow)
|
||||
} returns mutableImportLoginsFeatureFlow
|
||||
every {
|
||||
getFeatureFlagFlow(FlagKey.RemoveCardPolicy)
|
||||
} returns mutableRemoveCardPolicyFeatureFlow
|
||||
@ -2433,30 +2428,6 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when feature flag ImportLoginsFlow is disabled, should show action card should always be false`() =
|
||||
runTest {
|
||||
mutableImportLoginsFeatureFlow.update { false }
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(showImportActionCard = false),
|
||||
awaitItem(),
|
||||
)
|
||||
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
|
||||
accounts = DEFAULT_USER_STATE.accounts.map {
|
||||
it.copy(
|
||||
firstTimeState = DEFAULT_FIRST_TIME_STATE.copy(
|
||||
showImportLoginsCard = true,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when DismissImportActionCard is sent, repository called to showImportLogins to false and storeShowImportLoginsBadge to true`() {
|
||||
|
||||
@ -672,7 +672,6 @@ Do you want to switch to this account?</string>
|
||||
<string name="continue_to_device_settings">Continue to device Settings?</string>
|
||||
<string name="two_step_login_description_long">Make your account more secure by setting up two-step login in the Bitwarden web app.</string>
|
||||
<string name="change_master_password_description_long">You can change your master password on the Bitwarden web app.</string>
|
||||
<string name="you_can_import_data_to_your_vault_on_x">You can import data to your vault on %1$s.</string>
|
||||
<string name="learn_more_about_how_to_use_bitwarden_on_the_help_center">Learn more about how to use Bitwarden on the Help center.</string>
|
||||
<string name="privacy_policy_description_long">Check out our privacy policy on bitwarden.com.</string>
|
||||
<string name="explore_more_features_of_your_bitwarden_account_on_the_web_app">Explore more features of your Bitwarden account on the web app.</string>
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
<!-- region Debug Menu -->
|
||||
<string name="email_verification">Email Verification</string>
|
||||
<string name="import_logins_flow">Import Logins Flow</string>
|
||||
<string name="feature_flags">Feature Flags:</string>
|
||||
<string name="debug_menu">Debug Menu</string>
|
||||
<string name="reset_values">Reset values</string>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user