PM-37568: Feat: Remove feature flags (#6955)

This commit is contained in:
David Perez authored and GitHub committed 2026-05-20 22:28:30 +00:00
1 parent 8f72c10f8e
commit ebc3bd8081
19 files changed
+112 -545

No files matched your search

@@ -2,18 +2,15 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.vault
import androidx.lifecycle.viewModelScope
import com.bitwarden.core.data.manager.BuildInfoManager
import com.bitwarden.core.data.manager.model.FlagKey
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.ui.platform.base.BackgroundEvent
import com.bitwarden.ui.platform.base.BaseViewModel
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.ui.platform.model.SnackbarRelay
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -29,17 +26,13 @@ class VaultSettingsViewModel @Inject constructor(
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
private val buildInfoManager: BuildInfoManager,
private val firstTimeActionManager: FirstTimeActionManager,
private val featureFlagManager: FeatureFlagManager,
private val policyManager: PolicyManager,
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
initialState = run {
val firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState
VaultSettingsState(
showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
showImportItemsChevron = !buildInfoManager.isFdroid &&
featureFlagManager.getFeatureFlag(
key = FlagKey.CredentialExchangeProtocolImport,
),
showImportItemsChevron = !buildInfoManager.isFdroid,
)
},
) {
@@ -60,15 +53,11 @@ class VaultSettingsViewModel @Inject constructor(
.onEach(::sendAction)
.launchIn(viewModelScope)
featureFlagManager
.getFeatureFlagFlow(key = FlagKey.CredentialExchangeProtocolImport)
.combine(
policyManager.getActivePoliciesFlow(type = PolicyTypeJson.PERSONAL_OWNERSHIP),
) { isEnabled, policies ->
policyManager
.getActivePoliciesFlow(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
.map { policies ->
VaultSettingsAction.Internal.CredentialExchangeAvailabilityChanged(
isEnabled = isEnabled &&
!buildInfoManager.isFdroid &&
policies.isEmpty(),
isEnabled = !buildInfoManager.isFdroid && policies.isEmpty(),
)
}
.onEach(::sendAction)
@@ -144,7 +133,6 @@ class VaultSettingsViewModel @Inject constructor(
private fun handleImportItemsClicked() {
if (!buildInfoManager.isFdroid &&
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) &&
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP).isEmpty()
) {
sendEvent(VaultSettingsEvent.NavigateToImportItems)
@@ -37,10 +37,8 @@ import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.components.animation.AnimateNullableContentVisibility
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedErrorButton
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.bitwarden.ui.platform.components.field.BitwardenPasswordField
import com.bitwarden.ui.platform.components.field.BitwardenTextField
import com.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
@@ -161,26 +159,24 @@ fun AddEditSendContent(
)
}
if (state.common.isSendEmailVerificationEnabled) {
Spacer(modifier = Modifier.height(height = 8.dp))
AddEditSendAuthTypeChooser(
sendAuth = state.common.sendAuth,
onAuthTypeSelect = addSendHandlers.onAuthTypeSelect,
onPasswordChange = addSendHandlers.onAuthPasswordChange,
onEmailValueChange = addSendHandlers.onEmailValueChange,
onRemoveEmailClick = addSendHandlers.onEmailsRemoveClick,
onAddNewEmailClick = addSendHandlers.onAddNewEmailClick,
onOpenPasswordGeneratorClick = addSendHandlers.onOpenPasswordGeneratorClick,
onPasswordCopyClick = addSendHandlers.onPasswordCopyClick,
password = state.common.passwordInput,
isEnabled = !policyDisablesSend,
isSendsRestrictedByPolicy = policyDisablesSend,
modifier = Modifier
.testTag("SendAuthTypeChooser")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
Spacer(modifier = Modifier.height(height = 8.dp))
AddEditSendAuthTypeChooser(
sendAuth = state.common.sendAuth,
onAuthTypeSelect = addSendHandlers.onAuthTypeSelect,
onPasswordChange = addSendHandlers.onAuthPasswordChange,
onEmailValueChange = addSendHandlers.onEmailValueChange,
onRemoveEmailClick = addSendHandlers.onEmailsRemoveClick,
onAddNewEmailClick = addSendHandlers.onAddNewEmailClick,
onOpenPasswordGeneratorClick = addSendHandlers.onOpenPasswordGeneratorClick,
onPasswordCopyClick = addSendHandlers.onPasswordCopyClick,
password = state.common.passwordInput,
isEnabled = !policyDisablesSend,
isSendsRestrictedByPolicy = policyDisablesSend,
modifier = Modifier
.testTag("SendAuthTypeChooser")
.fillMaxWidth()
.standardHorizontalMargin(),
)
AddEditSendOptions(
state = state,
@@ -452,43 +448,6 @@ private fun AddEditSendOptions(
.standardHorizontalMargin(),
)
if (!state.common.isSendEmailVerificationEnabled) {
Spacer(modifier = Modifier.height(8.dp))
BitwardenPasswordField(
label = stringResource(id = BitwardenString.new_password),
supportingText = stringResource(id = BitwardenString.password_info),
readOnly = isSendsRestrictedByPolicy,
value = state.common.passwordInput,
onValueChange = addSendHandlers.onPasswordChange,
passwordFieldTestTag = "SendNewPasswordEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
) {
BitwardenStandardIconButton(
vectorIconRes = BitwardenDrawable.ic_generate,
contentDescription = stringResource(id = BitwardenString.generate_password),
onClick = {
if (state.common.passwordInput.isEmpty()) {
addSendHandlers.onOpenPasswordGeneratorClick()
} else {
shouldShowDialog = true
}
},
modifier = Modifier.testTag(tag = "RegeneratePasswordButton"),
)
BitwardenStandardIconButton(
vectorIconRes = BitwardenDrawable.ic_copy,
contentDescription = stringResource(id = BitwardenString.copy_password),
isEnabled = state.common.passwordInput.isNotEmpty(),
onClick = {
addSendHandlers.onPasswordCopyClick(state.common.passwordInput)
},
modifier = Modifier.testTag(tag = "CopyPasswordButton"),
)
}
}
if (shouldShowDialog) {
BitwardenTwoButtonDialog(
title = stringResource(id = BitwardenString.password),
@@ -4,7 +4,6 @@ import android.net.Uri
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.bitwarden.core.data.manager.model.FlagKey
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.core.data.repository.util.takeUntilLoaded
import com.bitwarden.data.repository.util.baseWebSendUrl
@@ -22,9 +21,8 @@ import com.bitwarden.ui.util.Text
import com.bitwarden.ui.util.asText
import com.bitwarden.ui.util.concat
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
@@ -90,7 +88,6 @@ class AddEditSendViewModel @Inject constructor(
private val policyManager: PolicyManager,
private val networkConnectionManager: NetworkConnectionManager,
private val snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
private val featureFlagManager: FeatureFlagManager,
private val premiumStateManager: PremiumStateManager,
) : BaseViewModel<AddEditSendState, AddEditSendEvent, AddEditSendAction>(
// We load the state from the savedStateHandle for testing purposes.
@@ -126,8 +123,6 @@ class AddEditSendViewModel @Inject constructor(
expirationDate = null,
sendUrl = null,
hasPassword = false,
isSendEmailVerificationEnabled = featureFlagManager
.getFeatureFlag(key = FlagKey.SendEmailVerification),
sendAuth = SendAuth.None,
),
selectedType = shareSendType ?: when (sendType) {
@@ -203,7 +198,6 @@ class AddEditSendViewModel @Inject constructor(
is AddEditSendAction.MaxAccessCountChange -> handleMaxAccessCountChange(action)
is AddEditSendAction.TextChange -> handleTextChange(action)
is AddEditSendAction.NoteChange -> handleNoteChange(action)
is AddEditSendAction.PasswordChange -> handlePasswordChange(action)
is AddEditSendAction.HideByDefaultToggle -> handleHideByDefaultToggle(action)
is AddEditSendAction.DeactivateThisSendToggle -> handleDeactivateThisSendToggle(action)
is AddEditSendAction.HideMyEmailToggle -> handleHideMyEmailToggle(action)
@@ -413,7 +407,6 @@ class AddEditSendViewModel @Inject constructor(
.environmentUrlData
.baseWebSendUrl,
isHideEmailAddressEnabled = isHideEmailAddressEnabled,
isSendEmailVerificationEnabled = isSendEmailVerificationEnabled,
)
?: AddEditSendState.ViewState.Error(
message = BitwardenString.generic_error_message.asText(),
@@ -454,7 +447,6 @@ class AddEditSendViewModel @Inject constructor(
.environmentUrlData
.baseWebSendUrl,
isHideEmailAddressEnabled = isHideEmailAddressEnabled,
isSendEmailVerificationEnabled = isSendEmailVerificationEnabled,
)
?: AddEditSendState.ViewState.Error(
message = BitwardenString.generic_error_message.asText(),
@@ -526,19 +518,6 @@ class AddEditSendViewModel @Inject constructor(
}
}
private fun handlePasswordChange(action: AddEditSendAction.PasswordChange) {
updateCommonContent {
it.copy(
passwordInput = action.input,
sendAuth = when {
action.input.isNotEmpty() -> SendAuth.Password
!isSendEmailVerificationEnabled -> SendAuth.None
else -> it.sendAuth
},
)
}
}
private fun handleNoteChange(action: AddEditSendAction.NoteChange) {
updateCommonContent {
it.copy(noteInput = action.input)
@@ -854,10 +833,6 @@ class AddEditSendViewModel @Inject constructor(
.getActivePolicies<PolicyInformation.SendOptions>()
.any { it.shouldDisableHideEmail ?: false }
private val isSendEmailVerificationEnabled: Boolean
get() = featureFlagManager
.getFeatureFlag(key = FlagKey.SendEmailVerification)
private inline fun onContent(
crossinline block: (AddEditSendState.ViewState.Content) -> Unit,
) {
@@ -1007,7 +982,6 @@ data class AddEditSendState(
val expirationDate: Instant?,
val sendUrl: String?,
val hasPassword: Boolean,
val isSendEmailVerificationEnabled: Boolean,
val sendAuth: SendAuth,
) : Parcelable
@@ -1193,11 +1167,6 @@ sealed class AddEditSendAction {
*/
data class TextChange(val input: String) : AddEditSendAction()
/**
* Value of the password field updated.
*/
data class PasswordChange(val input: String) : AddEditSendAction()
/**
* Value of the note text field updated.
*/
@@ -18,7 +18,6 @@ data class AddEditSendHandlers(
val onTextChange: (String) -> Unit,
val onIsHideByDefaultToggle: (Boolean) -> Unit,
val onMaxAccessCountChange: (Int) -> Unit,
val onPasswordChange: (String) -> Unit,
val onNoteChange: (String) -> Unit,
val onHideEmailToggle: (Boolean) -> Unit,
val onDeactivateSendToggle: (Boolean) -> Unit,
@@ -54,9 +53,6 @@ data class AddEditSendHandlers(
onMaxAccessCountChange = {
viewModel.trySendAction(AddEditSendAction.MaxAccessCountChange(it))
},
onPasswordChange = {
viewModel.trySendAction(AddEditSendAction.PasswordChange(it))
},
onNoteChange = { viewModel.trySendAction(AddEditSendAction.NoteChange(it)) },
onHideEmailToggle = {
viewModel.trySendAction(AddEditSendAction.HideMyEmailToggle(it))
@@ -24,10 +24,7 @@ fun AddEditSendState.ViewState.Content.toSendView(
key = common.originalSendView?.key,
newPassword = common
.passwordInput
.takeIf {
common.sendAuth is SendAuth.Password ||
!common.isSendEmailVerificationEnabled
}
.takeIf { common.sendAuth is SendAuth.Password }
.orNullIfBlank(),
hasPassword = false,
type = selectedType.toSendType(),
@@ -7,13 +7,13 @@ import com.x8bit.bitwarden.ui.tools.feature.send.addedit.model.AuthEmail
import com.x8bit.bitwarden.ui.tools.feature.send.addedit.model.SendAuth
import com.x8bit.bitwarden.ui.tools.feature.send.util.toSendUrl
import kotlinx.collections.immutable.toImmutableList
/**
* Transforms [SendView] into [AddEditSendState.ViewState.Content].
*/
fun SendView.toViewState(
baseWebSendUrl: String,
isHideEmailAddressEnabled: Boolean,
isSendEmailVerificationEnabled: Boolean,
): AddEditSendState.ViewState.Content =
AddEditSendState.ViewState.Content(
common = AddEditSendState.ViewState.Content.Common(
@@ -32,7 +32,6 @@ fun SendView.toViewState(
sendUrl = this.toSendUrl(baseWebSendUrl),
hasPassword = this.hasPassword,
isHideEmailAddressEnabled = isHideEmailAddressEnabled,
isSendEmailVerificationEnabled = isSendEmailVerificationEnabled,
sendAuth = when {
hasPassword -> SendAuth.Password
emails.isNotEmpty() -> {
@@ -121,7 +121,7 @@ class VaultViewModel @Inject constructor(
private val browserAutofillDialogManager: BrowserAutofillDialogManager,
private val credentialExchangeRegistryManager: CredentialExchangeRegistryManager,
private val buildInfoManager: BuildInfoManager,
private val featureFlagManager: FeatureFlagManager,
featureFlagManager: FeatureFlagManager,
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
initialState = run {
@@ -292,16 +292,16 @@ class VaultViewModel @Inject constructor(
.onEach(::sendAction)
.launchIn(viewModelScope)
featureFlagManager.getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolExport)
.map { VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
featureFlagManager.getFeatureFlagFlow(FlagKey.NewItemTypes)
.map { VaultAction.Internal.NewItemTypesFlagUpdateReceive(isEnabled = it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
if (!buildInfoManager.isFdroid) {
viewModelScope.launch {
credentialExchangeRegistryManager.register()
}
}
viewModelScope.launch {
delay(timeMillis = BROWSER_AUTOFILL_DIALOG_DELAY)
mutableStateFlow.update { vaultState ->
@@ -1131,10 +1131,6 @@ class VaultViewModel @Inject constructor(
handleNewItemTypesFlagUpdateReceive(action)
}
is VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive -> {
handleCredentialExchangeProtocolExportFlagUpdateReceive(action)
}
is VaultAction.Internal.ArchiveCipherReceive -> handleArchiveCipherReceive(action)
is VaultAction.Internal.UnarchiveCipherReceive -> handleUnarchiveCipherReceive(action)
is VaultAction.Internal.IntroducingArchiveActionCardDismissedFlowReceive -> {
@@ -1221,20 +1217,6 @@ class VaultViewModel @Inject constructor(
}
}
private fun handleCredentialExchangeProtocolExportFlagUpdateReceive(
action: VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive,
) {
viewModelScope.launch {
if (action.isCredentialExchangeProtocolExportEnabled &&
!buildInfoManager.isFdroid
) {
credentialExchangeRegistryManager.register()
} else {
credentialExchangeRegistryManager.unregister()
}
}
}
private fun handleArchiveCipherReceive(action: VaultAction.Internal.ArchiveCipherReceive) {
when (val result = action.result) {
is ArchiveCipherResult.Error -> {
@@ -2696,13 +2678,6 @@ sealed class VaultAction {
*/
data object KdfSyncCompletedReceive : Internal()
/**
* Indicates that the Credential Exchange Protocol export flag has been updated.
*/
data class CredentialExchangeProtocolExportFlagUpdateReceive(
val isCredentialExchangeProtocolExportEnabled: Boolean,
) : Internal()
/**
* Indicates that the New Item Types feature flag has been updated.
*/
@@ -105,8 +105,8 @@ class DebugMenuRepositoryTest {
debugMenuRepository.resetFeatureFlagOverrides()
verify(exactly = 1) {
mockFeatureFlagOverrideDiskSource.saveFeatureFlag(
FlagKey.CredentialExchangeProtocolImport,
FlagKey.CredentialExchangeProtocolImport.defaultValue,
FlagKey.AttachmentUpdates,
FlagKey.AttachmentUpdates.defaultValue,
)
}
debugMenuRepository.featureFlagOverridesUpdatedFlow.test {
@@ -175,6 +175,7 @@ class DebugMenuRepositoryTest {
mockSettingsDiskSource.storeShouldShowAddLoginCoachMark(shouldShow = null)
}
}
@Test
fun `clearSsoCookies should call clearCookies on CookieDiskSource`() {
debugMenuRepository.clearSsoCookies()
@@ -2,7 +2,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.vault
import app.cash.turbine.test
import com.bitwarden.core.data.manager.BuildInfoManager
import com.bitwarden.core.data.manager.model.FlagKey
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson
@@ -10,7 +9,6 @@ import com.bitwarden.ui.platform.base.BaseViewModelTest
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
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.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
@@ -38,13 +36,6 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLoginsSettingsBadge(any()) } just runs
}
private val mutableFeatureFlagFlow = bufferedMutableSharedFlow<Boolean>()
private val featureFlagManager = mockk<FeatureFlagManager> {
every { getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) } returns true
every {
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolImport)
} returns mutableFeatureFlagFlow
}
private val mutablePoliciesFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Policy>>()
private val policyManager = mockk<PolicyManager> {
every { getActivePolicies(any()) } returns emptyList()
@@ -79,28 +70,8 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `ImportItemsClick should emit NavigateToImportVault when CredentialExchangeProtocolImport is disabled`() =
runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
every {
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport)
} returns false
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
assertEquals(
VaultSettingsEvent.NavigateToImportVault,
awaitItem(),
)
}
}
@Test
fun `ImportItemsClick should emit NavigateToImportVault when policy is not empty`() = runTest {
every {
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport)
} returns true
every {
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
} returns listOf(mockk())
@@ -115,22 +86,17 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `ImportItemsClick should emit NavigateToImportItems when CredentialExchangeProtocolImport is enabled`() =
runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
every {
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport)
} returns true
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
assertEquals(
VaultSettingsEvent.NavigateToImportItems,
awaitItem(),
)
}
fun `ImportItemsClick should emit NavigateToImportItems when policy is empty`() = runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
assertEquals(
VaultSettingsEvent.NavigateToImportItems,
awaitItem(),
)
}
}
@Test
fun `shouldShowImportCard should update when first time state changes`() = runTest {
@@ -191,44 +157,34 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
}
@Test
fun `showImportItemsChevron should display based on feature flag and policies`() {
fun `showImportItemsChevron should display based on policies`() = runTest {
val viewModel = createViewModel()
// Verify chevron is shown when feature flag is enabled and no policies (default state)
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
)
viewModel.stateFlow.test {
// Verify chevron is shown when there are no policies (default state)
assertEquals(
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
awaitItem(),
)
// Verify chevron is hidden when feature flag is disabled and no policies
mutableFeatureFlagFlow.tryEmit(false)
mutablePoliciesFlow.tryEmit(emptyList())
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
)
// Verify chevron is hidden when policies exist
mutablePoliciesFlow.tryEmit(listOf(mockk()))
assertEquals(
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
awaitItem(),
)
// Verify chevron is hidden when feature flag is enabled and policies exist
mutableFeatureFlagFlow.tryEmit(true)
mutablePoliciesFlow.tryEmit(listOf(mockk()))
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
)
// Verify chevron is hidden when feature flag is disabled and no policies
mutableFeatureFlagFlow.tryEmit(false)
mutablePoliciesFlow.tryEmit(emptyList())
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
)
// Verify chevron is hidden when there are no policies
mutablePoliciesFlow.tryEmit(emptyList())
assertEquals(
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
awaitItem(),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `showImportItemsChevron should be false when feature flag is enabled but policy exists`() {
fun `showImportItemsChevron should be false when policy exists`() {
val viewModel = createViewModel()
mutableFeatureFlagFlow.tryEmit(true)
mutablePoliciesFlow.tryEmit(listOf(mockk()))
assertEquals(
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
@@ -267,7 +223,6 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
buildInfoManager = buildInfoManager,
firstTimeActionManager = firstTimeActionManager,
snackbarRelayManager = snackbarRelayManager,
featureFlagManager = featureFlagManager,
policyManager = policyManager,
)
}
@@ -208,15 +208,13 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
@Test
fun `on copyPassword click should send PasswordCopyClick when passwordInput is not empty`() {
// Expand options section:
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(passwordInput = "somePass"),
common = DEFAULT_COMMON_STATE.copy(
sendAuth = SendAuth.Password,
passwordInput = "somePass",
),
),
)
}
@@ -227,25 +225,20 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
verify {
viewModel.trySendAction(
AddEditSendAction.PasswordCopyClick(
"somePass",
),
AddEditSendAction.PasswordCopyClick(password = "somePass"),
)
}
}
@Test
fun `on copyPassword click should not send PasswordCopyClick when passwordInput is empty`() {
// Expand options section:
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(passwordInput = ""),
common = DEFAULT_COMMON_STATE.copy(
sendAuth = SendAuth.Password,
passwordInput = "",
),
),
)
}
@@ -254,20 +247,22 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
.performScrollTo()
.performClick()
verify(exactly = 0) {
viewModel.trySendAction(
AddEditSendAction.PasswordCopyClick(password = ""),
)
viewModel.trySendAction(any())
}
}
@Suppress("MaxLineLength")
@Test
fun `on generatePassword click should send OpenPasswordGeneratorClick when passwordInput is empty`() {
// Expand options section:
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
sendAuth = SendAuth.Password,
),
),
)
}
composeTestRule
.onNodeWithTag(testTag = "RegeneratePasswordButton")
.performScrollTo()
@@ -282,15 +277,13 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `on generatePassword click should show Password Overwrite dialog when passwordInput is not empty`() {
// Expand options section:
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(passwordInput = "somePass"),
common = DEFAULT_COMMON_STATE.copy(
sendAuth = SendAuth.Password,
passwordInput = "somePass",
),
),
)
}
@@ -636,9 +629,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
composeTestRule
.onNodeWithText("Maximum access count")
.assertExists()
composeTestRule
.onNodeWithText("New password")
.assertExists()
composeTestRule
.onNodeWithText("Private notes")
.assertExists()
@@ -698,41 +688,29 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
verify { viewModel.trySendAction(AddEditSendAction.MaxAccessCountChange(1)) }
}
@Test
fun `on password input change should send PasswordChange`() = runTest {
// Expand options section:
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
composeTestRule
.onNodeWithText("New password")
.performTextInput("input")
verify { viewModel.trySendAction(AddEditSendAction.PasswordChange("input")) }
}
@Test
fun `password input should change according to the state`() {
// Expand options section:
val commonState = DEFAULT_COMMON_STATE.copy(
sendAuth = SendAuth.Password,
passwordInput = "",
)
mutableStateFlow.update {
it.copy(viewState = DEFAULT_VIEW_STATE.copy(common = commonState))
}
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
composeTestRule
.onNodeWithText("New password")
.assertTextEquals("New password", "")
.onNodeWithText("Password")
.assertTextEquals("Password", "")
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(passwordInput = "input"),
common = commonState.copy(passwordInput = "input"),
),
)
}
composeTestRule
.onNodeWithText("New password")
.assertTextEquals("New password", "•••••")
.onNodeWithText("Password")
.assertTextEquals("Password", "•••••")
}
@Test
@@ -1033,52 +1011,12 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
//region Authentication UI Tests
@Test
fun `auth type chooser should be displayed when feature flag is enabled`() {
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
),
),
)
}
composeTestRule
.onNodeWithTag("SendAuthTypeChooser")
.performScrollTo()
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Who can view")
.assertIsDisplayed()
}
@Test
fun `auth type chooser should not be displayed when feature flag is disabled`() {
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = false,
),
),
)
}
composeTestRule
.onNodeWithTag("SendAuthTypeChooser")
.assertDoesNotExist()
}
@Test
fun `selecting EMAIL auth type should display email fields`() {
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.None,
),
),
@@ -1112,7 +1050,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
hasPassword = false,
sendAuth = SendAuth.None,
),
@@ -1147,7 +1084,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
hasPassword = false,
isSendEmailVerificationEnabled = true,
passwordInput = "",
sendAuth = SendAuth.None,
),
@@ -1169,7 +1105,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "",
sendAuth = SendAuth.Password,
),
@@ -1192,7 +1127,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.None,
),
),
@@ -1214,7 +1148,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(emails = persistentListOf(testEmail)),
),
),
@@ -1244,7 +1177,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
hasPassword = false,
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(
emails = persistentListOf(
AuthEmail(
@@ -1277,7 +1209,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
hasPassword = false,
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(emails = persistentListOf(email1, email2)),
),
),
@@ -1298,7 +1229,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(
emails = persistentListOf(email1, email2),
),
@@ -1331,7 +1261,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
isPremium = true,
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.None,
),
),
@@ -1350,46 +1279,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
.assertIsDisplayed()
}
@Test
fun `legacy password field should be hidden when auth chooser is displayed`() {
// Expand options section
composeTestRule
.onNodeWithText("Additional options")
.performScrollTo()
.performClick()
// With feature flag OFF, legacy password field should be visible
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = false,
),
),
)
}
composeTestRule
.onNodeWithText("New password")
.performScrollTo()
.assertIsDisplayed()
// With feature flag ON, legacy password field should be hidden
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
),
),
)
}
composeTestRule
.onNodeWithText("New password")
.assertDoesNotExist()
}
@Test
fun `email fields should display provided emails from state`() {
val email1 = AuthEmail(id = "id1", value = "user1@example.com")
@@ -1398,7 +1287,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(emails = persistentListOf(email1, email2)),
),
),
@@ -1420,7 +1308,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(emails = persistentListOf(email1, email2)),
),
),
@@ -1439,7 +1326,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.None,
),
),
@@ -1461,7 +1347,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
sendAuth = SendAuth.Email(
emails = persistentListOf(
AuthEmail(
@@ -1489,7 +1374,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "",
sendAuth = SendAuth.Password,
),
@@ -1514,7 +1398,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "existing-password",
sendAuth = SendAuth.Password,
),
@@ -1546,7 +1429,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "existing-password",
sendAuth = SendAuth.Password,
),
@@ -1578,7 +1460,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "existing-password",
sendAuth = SendAuth.Password,
),
@@ -1610,7 +1491,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "test-password",
sendAuth = SendAuth.Password,
),
@@ -1635,7 +1515,6 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
it.copy(
viewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
isSendEmailVerificationEnabled = true,
passwordInput = "",
sendAuth = SendAuth.Password,
),
@@ -1665,7 +1544,6 @@ private val DEFAULT_COMMON_STATE = AddEditSendState.ViewState.Content.Common(
sendUrl = null,
hasPassword = true,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
sendAuth = SendAuth.None,
)
@@ -3,7 +3,6 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addedit
import android.net.Uri
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.bitwarden.core.data.manager.model.FlagKey
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.data.repository.model.Environment
@@ -19,10 +18,9 @@ import com.bitwarden.ui.util.Text
import com.bitwarden.ui.util.asText
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
@@ -113,15 +111,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
private val premiumStateManager: PremiumStateManager = mockk {
every { isInAppUpgradeAvailable() } returns false
}
private val mutableSendEmailVerificationFeatureFlagFlow = MutableStateFlow(false)
private val featureFlagManager: FeatureFlagManager = mockk {
every {
getFeatureFlagFlow(FlagKey.SendEmailVerification)
} returns mutableSendEmailVerificationFeatureFlagFlow
every {
getFeatureFlag(FlagKey.SendEmailVerification)
} answers { mutableSendEmailVerificationFeatureFlagFlow.value }
}
@BeforeEach
fun setup() {
@@ -324,7 +313,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns viewState
every { viewState.toSendView(clock) } returns mockSendView
@@ -368,7 +356,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns viewState
every { viewState.toSendView(clock) } returns mockSendView
@@ -612,7 +599,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns viewState
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -674,7 +660,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns DEFAULT_VIEW_STATE
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -720,7 +705,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns DEFAULT_VIEW_STATE
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -795,7 +779,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns DEFAULT_VIEW_STATE
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -848,7 +831,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns DEFAULT_VIEW_STATE
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -916,7 +898,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
mockSendView.toViewState(
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
} returns viewState
mutableSendDataStateFlow.value = DataState.Loaded(mockSendView)
@@ -1096,23 +1077,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
}
}
@Test
fun `PasswordChange should update note input`() = runTest {
val viewModel = createViewModel()
val expectedViewState = DEFAULT_VIEW_STATE.copy(
common = DEFAULT_COMMON_STATE.copy(
passwordInput = "input",
sendAuth = SendAuth.Password,
),
)
viewModel.stateFlow.test {
assertEquals(DEFAULT_STATE, awaitItem())
viewModel.trySendAction(AddEditSendAction.PasswordChange("input"))
assertEquals(DEFAULT_STATE.copy(viewState = expectedViewState), awaitItem())
}
}
@Test
fun `DeactivateThisSendToggle should update isDeactivateChecked`() = runTest {
val viewModel = createViewModel()
@@ -1515,7 +1479,6 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
networkConnectionManager = networkConnectionManager,
snackbarRelayManager = snackbarRelayManager,
generatorRepository = generatorRepository,
featureFlagManager = featureFlagManager,
premiumStateManager = premiumStateManager,
)
}
@@ -1533,7 +1496,6 @@ private val DEFAULT_COMMON_STATE = AddEditSendState.ViewState.Content.Common(
sendUrl = null,
hasPassword = false,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
sendAuth = SendAuth.None,
)
@@ -157,7 +157,6 @@ private val DEFAULT_COMMON_STATE = AddEditSendState.ViewState.Content.Common(
sendUrl = null,
hasPassword = false,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
sendAuth = SendAuth.Password,
)
@@ -17,7 +17,6 @@ class SendViewExtensionsTest {
val result = sendView.toViewState(
baseWebSendUrl = "www.test.com/",
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
assertEquals(
@@ -38,7 +37,6 @@ class SendViewExtensionsTest {
val result = sendView.toViewState(
baseWebSendUrl = "www.test.com/",
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
)
assertEquals(
@@ -68,7 +66,6 @@ private val DEFAULT_COMMON: AddEditSendState.ViewState.Content.Common =
sendUrl = "www.test.com/mockAccessId-1/mockKey-1",
hasPassword = true,
isHideEmailAddressEnabled = true,
isSendEmailVerificationEnabled = false,
sendAuth = SendAuth.None,
)
@@ -232,12 +232,8 @@ class VaultViewModelTest : BaseViewModelTest() {
coEvery { register() } returns RegisterExportResult.Success
coEvery { unregister() } returns UnregisterExportResult.Success
}
private val mutableCxpExportFeatureFlagFlow = MutableStateFlow(false)
private val mutableNewItemTypesFlagFlow = MutableStateFlow(false)
private val featureFlagManager: FeatureFlagManager = mockk {
every {
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolExport)
} returns mutableCxpExportFeatureFlagFlow
every { getFeatureFlagFlow(FlagKey.NewItemTypes) } returns mutableNewItemTypesFlagFlow
}
@@ -1260,7 +1256,6 @@ class VaultViewModelTest : BaseViewModelTest() {
)
}
@Suppress("MaxLineLength")
@Test
fun `on AddAccountClick should set hasPendingAccountAddition to true on the AuthRepository`() {
val viewModel = createViewModel()
@@ -3945,7 +3940,6 @@ class VaultViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `when ImportActionCardClick is sent, NavigateToImportLogins event is sent`() =
runTest {
@@ -3959,7 +3953,6 @@ class VaultViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `when ImportActionCardClick is sent, repository is not called if value is already false`() {
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
@@ -4411,70 +4404,24 @@ class VaultViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `CredentialExchangeProtocolExportFlagUpdateReceive should register for export when flag is enabled`() =
runTest {
mutableCxpExportFeatureFlagFlow.value = false
coEvery { credentialExchangeRegistryManager.register() } just awaits
fun `credentialExchangeRegistryManager should register when not fdroid`() = runTest {
createViewModel()
val viewModel = createViewModel()
viewModel.trySendAction(
VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive(
isCredentialExchangeProtocolExportEnabled = true,
),
)
coVerify {
credentialExchangeRegistryManager.register()
}
coVerify(exactly = 1) {
credentialExchangeRegistryManager.register()
}
}
@Suppress("MaxLineLength")
@Test
fun `CredentialExchangeProtocolExportFlagUpdateReceive should unregister when flag is disabled`() =
runTest {
mutableCxpExportFeatureFlagFlow.value = true
every { settingsRepository.isAppRegisteredForExport() } returns true
coEvery { credentialExchangeRegistryManager.unregister() } just awaits
fun `credentialExchangeRegistryManager should not register when fdroid`() = runTest {
every { buildInfoManager.isFdroid } returns true
createViewModel()
val viewModel = createViewModel()
viewModel.trySendAction(
VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive(
isCredentialExchangeProtocolExportEnabled = false,
),
)
coVerify {
credentialExchangeRegistryManager.unregister()
}
}
@Suppress("MaxLineLength")
@Test
fun `CredentialExchangeProtocolExportFlagUpdateReceive should unregister when flag is enabled but isFdroid is true`() =
runTest {
every { buildInfoManager.isFdroid } returns true
mutableCxpExportFeatureFlagFlow.value = false
coEvery { credentialExchangeRegistryManager.unregister() } just awaits
val viewModel = createViewModel()
viewModel.trySendAction(
VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive(
isCredentialExchangeProtocolExportEnabled = true,
),
)
coVerify {
credentialExchangeRegistryManager.unregister()
}
coVerify(exactly = 0) {
credentialExchangeRegistryManager.register()
}
coVerify(exactly = 0) {
credentialExchangeRegistryManager.register()
}
}
@Test
fun `vault data loaded with premium user should set validTotpIds from getValidTotpCipherIds`() =
@@ -30,12 +30,9 @@ sealed class FlagKey<out T : Any> {
*/
val activePasswordManagerFlags: List<FlagKey<*>> by lazy {
listOf(
CredentialExchangeProtocolImport,
CredentialExchangeProtocolExport,
ForceUpdateKdfSettings,
NoLogoutOnKdfChange,
MigrateMyVaultToMyItems,
SendEmailVerification,
CardScanner,
MobilePremiumUpgrade,
ManageDevices,
@@ -50,24 +47,6 @@ sealed class FlagKey<out T : Any> {
}
}
/**
* Data object holding hte feature flag key for the Credential Exchange Protocol (CXP) import
* feature.
*/
data object CredentialExchangeProtocolImport : FlagKey<Boolean>() {
override val keyName: String = "cxp-import-mobile"
override val defaultValue: Boolean = false
}
/**
* Data object holding the feature flag key for the Credential Exchange Protocol (CXP) export
* feature.
*/
data object CredentialExchangeProtocolExport : FlagKey<Boolean>() {
override val keyName: String = "cxp-export-mobile"
override val defaultValue: Boolean = false
}
/**
* Indicates the state of Bitwarden authentication.
*/
@@ -100,14 +79,6 @@ sealed class FlagKey<out T : Any> {
override val defaultValue: Boolean = false
}
/**
* Data object holding the feature flag key for the Send Email Verification feature.
*/
data object SendEmailVerification : FlagKey<Boolean>() {
override val keyName: String = "pm-19051-send-email-verification"
override val defaultValue: Boolean = false
}
/**
* Data object holding the feature flag key for the card scanner feature.
*/
@@ -8,14 +8,6 @@ class FlagKeyTest {
@Test
fun `Feature flags have the correct key name set`() {
assertEquals(
FlagKey.CredentialExchangeProtocolImport.keyName,
"cxp-import-mobile",
)
assertEquals(
FlagKey.CredentialExchangeProtocolExport.keyName,
"cxp-export-mobile",
)
assertEquals(
FlagKey.BitwardenAuthenticationEnabled.keyName,
"bitwarden-authentication-enabled",
@@ -28,10 +20,6 @@ class FlagKeyTest {
FlagKey.MigrateMyVaultToMyItems.keyName,
"pm-20558-migrate-myvault-to-myitems",
)
assertEquals(
FlagKey.SendEmailVerification.keyName,
"pm-19051-send-email-verification",
)
assertEquals(
FlagKey.MobilePremiumUpgrade.keyName,
"pm-31697-premium-upgrade-path",
@@ -70,12 +58,9 @@ class FlagKeyTest {
fun `All feature flags have the correct default value set`() {
assertTrue(
listOf(
FlagKey.CredentialExchangeProtocolImport,
FlagKey.CredentialExchangeProtocolExport,
FlagKey.BitwardenAuthenticationEnabled,
FlagKey.ForceUpdateKdfSettings,
FlagKey.MigrateMyVaultToMyItems,
FlagKey.SendEmailVerification,
FlagKey.MobilePremiumUpgrade,
FlagKey.AttachmentUpdates,
FlagKey.V2EncryptionJitPassword,
@@ -24,13 +24,10 @@ fun <T : Any> FlagKey<T>.ListItemContent(
FlagKey.DummyBoolean,
FlagKey.BitwardenAuthenticationEnabled,
FlagKey.CredentialExchangeProtocolImport,
FlagKey.CredentialExchangeProtocolExport,
FlagKey.ForceUpdateKdfSettings,
FlagKey.NoLogoutOnKdfChange,
FlagKey.MigrateMyVaultToMyItems,
FlagKey.CardScanner,
FlagKey.SendEmailVerification,
FlagKey.MobilePremiumUpgrade,
FlagKey.ManageDevices,
FlagKey.AttachmentUpdates,
@@ -81,8 +78,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
FlagKey.DummyString,
-> this.keyName
FlagKey.CredentialExchangeProtocolImport -> stringResource(BitwardenString.cxp_import)
FlagKey.CredentialExchangeProtocolExport -> stringResource(BitwardenString.cxp_export)
FlagKey.ForceUpdateKdfSettings -> stringResource(BitwardenString.force_update_kdf_settings)
FlagKey.NoLogoutOnKdfChange -> stringResource(BitwardenString.avoid_logout_on_kdf_change)
FlagKey.BitwardenAuthenticationEnabled -> {
@@ -91,7 +86,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
FlagKey.MigrateMyVaultToMyItems -> stringResource(BitwardenString.migrate_my_vault_to_my_items)
FlagKey.CardScanner -> stringResource(BitwardenString.scan_card)
FlagKey.SendEmailVerification -> stringResource(BitwardenString.send_email_verification)
FlagKey.MobilePremiumUpgrade -> stringResource(BitwardenString.mobile_premium_upgrade)
FlagKey.ManageDevices -> stringResource(BitwardenString.manage_devices)
FlagKey.AttachmentUpdates -> stringResource(BitwardenString.attachment_updates)
-4
View File
@@ -472,8 +472,6 @@ Scanning will happen automatically.</string>
<string name="maximum_access_count_info">If set, users will no longer be able to access this Send once the maximum access count is reached.</string>
<string name="maximum_access_count_reached">Max access count reached</string>
<string name="current_access_count">Current access count: %s</string>
<string name="new_password">New password</string>
<string name="password_info">Require this password to view the Send.</string>
<string name="remove_password">Remove password</string>
<string name="removing_send_password">Removing password</string>
<string name="password_removed">Password removed</string>
@@ -892,8 +890,6 @@ Do you want to switch to this account?</string>
<string name="copy_address">Copy address</string>
<string name="biometrics_no_longer_supported_title">Biometrics are no longer supported on this device</string>
<string name="biometrics_no_longer_supported">You’ve been logged out because your device’s biometrics don’t meet the latest security requirements. To update settings, log in once again or contact your administrator for access.</string>
<string name="cxp_import">CXP Import</string>
<string name="cxp_export">CXP Export</string>
<string name="choose_three_or_four_random_words">Choose three or four random words</string>
<string name="pick_three_or_four_random_unrelated_words">Pick three or four random, unrelated words that you can easily remember. Think of <annotation emphasis="bold">objects, places, or things</annotation> you like.</string>
<string name="combine_those_words_together">Combine those words together</string>
@@ -42,7 +42,6 @@
<string name="force_update_kdf_settings">Force update KDF settings</string>
<string name="avoid_logout_on_kdf_change">Avoid logout on KDF change</string>
<string name="migrate_my_vault_to_my_items">Migrate My Vault to My Items</string>
<string name="send_email_verification">Send Email Verification</string>
<string name="trigger_cookie_acquisition">Trigger cookie acquisition</string>
<string name="clear_sso_cookies">Clear SSO cookies</string>
<string name="mobile_premium_upgrade">Mobile Premium Upgrade</string>