1
0
Fork 0
mirror of https://github.com/bitwarden/android.git synced 2025-02-13 18:39:56 +03:00

PM-14009 Refactor storing first time values to the first time action manager ()

This commit is contained in:
Dave Severns 2024-10-25 11:40:18 -04:00 committed by GitHub
parent 53d4c4c03e
commit eb4ffebba0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 196 additions and 238 deletions
app/src
main/java/com/x8bit/bitwarden
test/java/com/x8bit/bitwarden

View file

@ -393,9 +393,4 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
* Update the value of the onboarding status for the user.
*/
fun setOnboardingStatus(userId: String, status: OnboardingStatus?)
/**
* Update the value of the showImportLogins status for the user.
*/
fun setShowImportLogins(showImportLogins: Boolean)
}

View file

@ -1327,11 +1327,6 @@ class AuthRepositoryImpl(
authDiskSource.storeOnboardingStatus(userId = userId, onboardingStatus = status)
}
override fun setShowImportLogins(showImportLogins: Boolean) {
val userId: String = activeUserId ?: return
authDiskSource.storeShowImportLogins(userId = userId, showImportLogins = showImportLogins)
}
@Suppress("CyclomaticComplexMethod")
private suspend fun validatePasswordAgainstPolicy(
password: String,

View file

@ -44,4 +44,21 @@ interface FirstTimeActionManager {
* a default configuration.
*/
val currentOrDefaultUserFirstTimeState: FirstTimeState
/**
* Stores the given value for whether or not the active user has signalled they want to
* set up unlock options later, during onboarding.
*/
fun storeShowUnlockSettingBadge(showBadge: Boolean)
/**
* Stores the given value for whether or not the active user has signalled they want to
* enable autofill later, during onboarding.
*/
fun storeShowAutoFillSettingBadge(showBadge: Boolean)
/**
* Update the value of the showImportLogins status for the active user.
*/
fun storeShowImportLogins(showImportLogins: Boolean)
}

View file

@ -183,4 +183,28 @@ class FirstTimeActionManagerImpl @Inject constructor(
showSetupUnlockCard = null,
showSetupAutofillCard = null,
)
override fun storeShowUnlockSettingBadge(showBadge: Boolean) {
val activeUserId = authDiskSource.userState?.activeUserId ?: return
settingsDiskSource.storeShowUnlockSettingBadge(
userId = activeUserId,
showBadge = showBadge,
)
}
override fun storeShowAutoFillSettingBadge(showBadge: Boolean) {
val activeUserId = authDiskSource.userState?.activeUserId ?: return
settingsDiskSource.storeShowAutoFillSettingBadge(
userId = activeUserId,
showBadge = showBadge,
)
}
override fun storeShowImportLogins(showImportLogins: Boolean) {
val activeUserId = authDiskSource.userState?.activeUserId ?: return
authDiskSource.storeShowImportLogins(
userId = activeUserId,
showImportLogins = showImportLogins,
)
}
}

View file

@ -254,38 +254,4 @@ interface SettingsRepository {
* Record that a user has logged in on this device.
*/
fun storeUserHasLoggedInValue(userId: String)
/**
* Gets whether or not the given [userId] has signalled they want to enable autofill
* later, during onboarding.
*/
fun getShowAutoFillSettingBadge(userId: String): Boolean
/**
* Stores the given value for whether or not the given [userId] has signalled they want to
* enable autofill later, during onboarding.
*/
fun storeShowAutoFillSettingBadge(userId: String, showBadge: Boolean)
/**
* Gets whether or not the given [userId] has signalled they want to enable unlock options
* later, during onboarding.
*/
fun getShowUnlockSettingBadge(userId: String): Boolean
/**
* Stores the given value for whether or not the given [userId] has signalled they want to
* set up unlock options later, during onboarding.
*/
fun storeShowUnlockSettingBadge(userId: String, showBadge: Boolean)
/**
* Gets whether or not the given [userId] has signalled they want to enable autofill
*/
fun getShowAutofillBadgeFlow(userId: String): Flow<Boolean>
/**
* Gets whether or not the given [userId] has signalled they want to enable unlock options
*/
fun getShowUnlockBadgeFlow(userId: String): Flow<Boolean>
}

View file

@ -538,28 +538,6 @@ class SettingsRepositoryImpl(
settingsDiskSource.storeUseHasLoggedInPreviously(userId)
}
override fun getShowAutoFillSettingBadge(userId: String): Boolean =
settingsDiskSource.getShowAutoFillSettingBadge(userId) ?: false
override fun storeShowAutoFillSettingBadge(userId: String, showBadge: Boolean) {
settingsDiskSource.storeShowAutoFillSettingBadge(userId, showBadge)
}
override fun getShowUnlockSettingBadge(userId: String): Boolean =
settingsDiskSource.getShowUnlockSettingBadge(userId) ?: false
override fun storeShowUnlockSettingBadge(userId: String, showBadge: Boolean) {
settingsDiskSource.storeShowUnlockSettingBadge(userId, showBadge)
}
override fun getShowAutofillBadgeFlow(userId: String): Flow<Boolean> =
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId)
.map { it ?: false }
override fun getShowUnlockBadgeFlow(userId: String): Flow<Boolean> =
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId)
.map { it ?: false }
/**
* If there isn't already one generated, generate a symmetric sync key that would be used
* for communicating via IPC.

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
@ -25,6 +26,7 @@ class SetupAutoFillViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val settingsRepository: SettingsRepository,
private val authRepository: AuthRepository,
private val firstTimeActionManager: FirstTimeActionManager,
) :
BaseViewModel<SetupAutoFillState, SetupAutoFillEvent, SetupAutoFillAction>(
// We load the state from the savedStateHandle for testing purposes.
@ -97,7 +99,7 @@ class SetupAutoFillViewModel @Inject constructor(
}
private fun handleTurnOnLaterConfirmClick() {
settingsRepository.storeShowAutoFillSettingBadge(state.userId, true)
firstTimeActionManager.storeShowAutoFillSettingBadge(showBadge = true)
updateOnboardingStatusToNextStep()
}

View file

@ -7,6 +7,7 @@ import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
@ -32,6 +33,7 @@ class SetupUnlockViewModel @Inject constructor(
private val authRepository: AuthRepository,
private val settingsRepository: SettingsRepository,
private val biometricsEncryptionManager: BiometricsEncryptionManager,
private val firstTimeActionManager: FirstTimeActionManager,
) : BaseViewModel<SetupUnlockState, SetupUnlockEvent, SetupUnlockAction>(
// We load the state from the savedStateHandle for testing purposes.
initialState = savedStateHandle[KEY_STATE] ?: run {
@ -109,7 +111,7 @@ class SetupUnlockViewModel @Inject constructor(
}
private fun handleSetUpLaterClick() {
settingsRepository.storeShowUnlockSettingBadge(state.userId, true)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = true)
updateOnboardingStatusToNextStep()
}

View file

@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
import com.x8bit.bitwarden.data.auth.repository.util.policyInformation
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
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.FlagKey
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
@ -48,6 +49,7 @@ class AccountSecurityViewModel @Inject constructor(
private val environmentRepository: EnvironmentRepository,
private val biometricsEncryptionManager: BiometricsEncryptionManager,
private val featureFlagManager: FeatureFlagManager,
private val firstTimeActionManager: FirstTimeActionManager,
policyManager: PolicyManager,
savedStateHandle: SavedStateHandle,
) : BaseViewModel<AccountSecurityState, AccountSecurityEvent, AccountSecurityAction>(
@ -114,10 +116,10 @@ class AccountSecurityViewModel @Inject constructor(
}
.launchIn(viewModelScope)
settingsRepository
.getShowUnlockBadgeFlow(state.userId)
firstTimeActionManager
.firstTimeStateFlow
.map {
AccountSecurityAction.Internal.ShowUnlockBadgeUpdated(it)
AccountSecurityAction.Internal.ShowUnlockBadgeUpdated(it.showSetupUnlockCard)
}
.onEach(::sendAction)
.launchIn(viewModelScope)
@ -164,7 +166,6 @@ class AccountSecurityViewModel @Inject constructor(
}
private fun handleUnlockCardCtaClick() {
dismissUnlockNotificationBadge()
sendEvent(AccountSecurityEvent.NavigateToSetupUnlockScreen)
}
@ -443,8 +444,7 @@ class AccountSecurityViewModel @Inject constructor(
private fun dismissUnlockNotificationBadge() {
if (!state.shouldShowUnlockActionCard) return
settingsRepository.storeShowUnlockSettingBadge(
userId = state.userId,
firstTimeActionManager.storeShowUnlockSettingBadge(
showBadge = false,
)
}

View file

@ -5,6 +5,7 @@ import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
@ -29,6 +30,7 @@ class AutoFillViewModel @Inject constructor(
authRepository: AuthRepository,
private val savedStateHandle: SavedStateHandle,
private val settingsRepository: SettingsRepository,
private val firstTimeActionManager: FirstTimeActionManager,
) : BaseViewModel<AutoFillState, AutoFillEvent, AutoFillAction>(
initialState = savedStateHandle[KEY_STATE]
?: run {
@ -74,9 +76,9 @@ class AutoFillViewModel @Inject constructor(
.onEach(::sendAction)
.launchIn(viewModelScope)
settingsRepository
.getShowAutofillBadgeFlow(userId = state.activeUserId)
.map { AutoFillAction.Internal.UpdateShowAutofillActionCard(it) }
firstTimeActionManager
.firstTimeStateFlow
.map { AutoFillAction.Internal.UpdateShowAutofillActionCard(it.showSetupAutofillCard) }
.onEach(::sendAction)
.launchIn(viewModelScope)
}
@ -117,7 +119,6 @@ class AutoFillViewModel @Inject constructor(
}
private fun handleAutoFillActionCardCtClick() {
dismissShowAutofillActionCard()
sendEvent(AutoFillEvent.NavigateToSetupAutofill)
}
@ -194,8 +195,7 @@ class AutoFillViewModel @Inject constructor(
private fun dismissShowAutofillActionCard() {
if (!state.showAutofillActionCard) return
settingsRepository.storeShowAutoFillSettingBadge(
userId = state.activeUserId,
firstTimeActionManager.storeShowAutoFillSettingBadge(
showBadge = false,
)
}

View file

@ -1,7 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
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
@ -23,7 +22,6 @@ import javax.inject.Inject
class VaultSettingsViewModel @Inject constructor(
environmentRepository: EnvironmentRepository,
featureFlagManager: FeatureFlagManager,
private val authRepository: AuthRepository,
private val firstTimeActionManager: FirstTimeActionManager,
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
initialState = run {
@ -80,11 +78,11 @@ class VaultSettingsViewModel @Inject constructor(
}
private fun handleImportLoginsCardDismissClicked() {
dismissImportLoginsCard()
if (!state.shouldShowImportCard) return
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
}
private fun handleImportLoginsCardClicked() {
dismissImportLoginsCard()
sendEvent(VaultSettingsEvent.NavigateToImportVault(state.importUrl))
}
@ -121,11 +119,6 @@ class VaultSettingsViewModel @Inject constructor(
VaultSettingsEvent.NavigateToImportVault(state.importUrl),
)
}
private fun dismissImportLoginsCard() {
if (!state.shouldShowImportCard) return
authRepository.setShowImportLogins(showImportLogins = false)
}
}
/**

View file

@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
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.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
@ -71,6 +72,7 @@ class VaultViewModel @Inject constructor(
private val settingsRepository: SettingsRepository,
private val vaultRepository: VaultRepository,
private val featureFlagManager: FeatureFlagManager,
private val firstTimeActionManager: FirstTimeActionManager,
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
initialState = run {
val userState = requireNotNull(authRepository.userStateFlow.value)
@ -180,12 +182,12 @@ class VaultViewModel @Inject constructor(
}
private fun handleImportActionCardClick() {
dismissImportLoginCard()
sendEvent(VaultEvent.NavigateToImportLogins)
}
private fun handleDismissImportActionCard() {
dismissImportLoginCard()
if (!state.showImportActionCard) return
firstTimeActionManager.storeShowImportLogins(false)
}
private fun handleIconLoadingSettingReceive(
@ -631,11 +633,6 @@ class VaultViewModel @Inject constructor(
}
//endregion VaultAction Handlers
private fun dismissImportLoginCard() {
if (!state.showImportActionCard) return
authRepository.setShowImportLogins(false)
}
}
/**

View file

@ -6361,13 +6361,6 @@ class AuthRepositoryTest {
assertNull(fakeAuthDiskSource.getOnboardingStatus(USER_ID_1))
}
@Test
fun `setShowImportLogins should save the showImportLogins to disk`() {
fakeAuthDiskSource.userState = MULTI_USER_STATE
repository.setShowImportLogins(showImportLogins = true)
assertEquals(true, fakeAuthDiskSource.getShowImportLogins(USER_ID_1))
}
companion object {
private const val UNIQUE_APP_ID = "testUniqueAppId"
private const val NAME = "Example Name"

View file

@ -20,6 +20,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class FirstTimeActionManagerTest {
@ -184,6 +186,53 @@ class FirstTimeActionManagerTest {
firstTimeActionManager.currentOrDefaultUserFirstTimeState,
)
}
@Test
fun `storeShowAutoFillSettingBadge should store value of false to disk`() {
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
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
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
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
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = true)
assertTrue(fakeSettingsDiskSource.getShowUnlockSettingBadge(userId = USER_ID)!!)
}
@Test
fun `storeShowImportLogins should store value of false to disk`() {
fakeAuthDiskSource.userState = MOCK_USER_STATE
firstTimeActionManager.storeShowImportLogins(showImportLogins = true)
assertTrue(fakeAuthDiskSource.getShowImportLogins(userId = USER_ID)!!)
}
}
private const val USER_ID: String = "userId"

View file

@ -1150,90 +1150,6 @@ class SettingsRepositoryTest {
fakeAuthDiskSource.userState = MOCK_USER_STATE
assertFalse(settingsRepository.isAuthenticatorSyncEnabled)
}
@Test
fun `storeShowAutoFillSettingBadge should store value of false to disk`() {
val userId = "userId"
settingsRepository.storeShowAutoFillSettingBadge(userId = userId, showBadge = false)
assertFalse(fakeSettingsDiskSource.getShowAutoFillSettingBadge(userId = userId)!!)
}
@Test
fun `storeShowAutoFillSettingBadge should store value of true to disk`() {
val userId = "userId"
settingsRepository.storeShowAutoFillSettingBadge(userId = userId, showBadge = true)
assertTrue(fakeSettingsDiskSource.getShowAutoFillSettingBadge(userId = userId)!!)
}
@Test
fun `getShowAutoFillSettingBadge get value of false if does not exist`() {
val userId = "userId"
assertFalse(settingsRepository.getShowAutoFillSettingBadge(userId = userId))
}
@Test
fun `getShowAutoFillSettingBadge should return the value saved to disk`() {
val userId = "userId"
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(userId = userId, showBadge = true)
assertTrue(settingsRepository.getShowAutoFillSettingBadge(userId = userId))
}
@Test
fun `storeShowUnlockSettingBadge should store value of false to disk`() {
val userId = "userId"
settingsRepository.storeShowUnlockSettingBadge(userId = userId, showBadge = false)
assertFalse(fakeSettingsDiskSource.getShowUnlockSettingBadge(userId = userId)!!)
}
@Test
fun `storeShowUnlockSettingBadge should store value of true to disk`() {
val userId = "userId"
settingsRepository.storeShowUnlockSettingBadge(userId = userId, showBadge = true)
assertTrue(fakeSettingsDiskSource.getShowUnlockSettingBadge(userId = userId)!!)
}
@Test
fun `getUnlockSettingBadge get value of false if does not exist`() {
val userId = "userId"
assertFalse(settingsRepository.getShowUnlockSettingBadge(userId = userId))
}
@Test
fun `getShowUnlockSettingBadge should return the value saved to disk`() {
val userId = "userId"
fakeSettingsDiskSource.storeShowUnlockSettingBadge(userId = userId, showBadge = true)
assertTrue(settingsRepository.getShowUnlockSettingBadge(userId = userId))
}
@Suppress("MaxLineLength")
@Test
fun `getShowAutoFillBadgeFlow should emit the values saved to disk and update when they change`() =
runTest {
val userId = "userId"
settingsRepository.getShowAutofillBadgeFlow(userId).test {
assertFalse(awaitItem())
fakeSettingsDiskSource.storeShowAutoFillSettingBadge(
userId = userId,
showBadge = true,
)
assertTrue(awaitItem())
}
}
@Suppress("MaxLineLength")
@Test
fun `getShowUnlockBadgeFlow should emit the values saved to disk and update when they change`() =
runTest {
val userId = "userId"
settingsRepository.getShowUnlockBadgeFlow(userId).test {
assertFalse(awaitItem())
fakeSettingsDiskSource.storeShowUnlockSettingBadge(
userId = userId,
showBadge = true,
)
assertTrue(awaitItem())
}
}
}
private const val USER_ID: String = "userId"

View file

@ -5,6 +5,8 @@ import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
import io.mockk.every
@ -26,7 +28,12 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
private val settingsRepository = mockk<SettingsRepository> {
every { isAutofillEnabledStateFlow } returns mutableAutoFillEnabledStateFlow
every { disableAutofill() } just runs
every { storeShowAutoFillSettingBadge(any(), any()) } just runs
}
private val mutableFirstTimeStateFlow = MutableStateFlow(FirstTimeState())
private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowAutoFillSettingBadge(any()) } just runs
}
private val mockUserState = mockk<UserState> {
@ -153,8 +160,7 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(SetupAutoFillAction.TurnOnLaterConfirmClick)
verify {
settingsRepository.storeShowAutoFillSettingBadge(
userId = DEFAULT_USER_ID,
firstTimeActionManager.storeShowAutoFillSettingBadge(
showBadge = true,
)
}
@ -180,6 +186,7 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
),
settingsRepository = settingsRepository,
authRepository = authRepository,
firstTimeActionManager = firstTimeActionManager,
)
}

View file

@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
@ -41,7 +42,11 @@ class SetupUnlockViewModelTest : BaseViewModelTest() {
every { isUnlockWithPinEnabled } returns false
every { isUnlockWithBiometricsEnabled } returns false
every { isAutofillEnabledStateFlow } returns mutableAutofillEnabledStateFlow
every { storeShowUnlockSettingBadge(any(), any()) } just runs
}
private val mutableFirstTimeStateFlow = MutableStateFlow(FirstTimeState())
private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowUnlockSettingBadge(any()) } just runs
}
private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk {
every { getOrCreateCipher(userId = DEFAULT_USER_ID) } returns CIPHER
@ -107,7 +112,7 @@ class SetupUnlockViewModelTest : BaseViewModelTest() {
userId = DEFAULT_USER_ID,
status = OnboardingStatus.AUTOFILL_SETUP,
)
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_USER_ID, true)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = true)
}
}
@ -359,6 +364,7 @@ class SetupUnlockViewModelTest : BaseViewModelTest() {
authRepository = authRepository,
settingsRepository = settingsRepository,
biometricsEncryptionManager = biometricsEncryptionManager,
firstTimeActionManager = firstTimeActionManager,
)
}

View file

@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
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
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
@ -56,7 +57,6 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
every { userStateFlow } returns mutableUserStateFlow
}
private val vaultRepository: VaultRepository = mockk(relaxed = true)
private val mutableShowUnlockBadgeFlow = MutableStateFlow(false)
private val settingsRepository: SettingsRepository = mockk {
every { isAuthenticatorSyncEnabled } returns false
every { isUnlockWithBiometricsEnabled } returns false
@ -64,8 +64,12 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
every { vaultTimeout } returns VaultTimeout.ThirtyMinutes
every { vaultTimeoutAction } returns VaultTimeoutAction.LOCK
coEvery { getUserFingerprint() } returns UserFingerprintResult.Success(FINGERPRINT)
every { getShowUnlockBadgeFlow(any()) } returns mutableShowUnlockBadgeFlow
every { storeShowUnlockSettingBadge(any(), false) } just runs
}
private val mutableFirstTimeStateFlow = MutableStateFlow(FirstTimeState())
private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowUnlockSettingBadge(any()) } just runs
}
private val mutableActivePolicyFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Policy>>()
private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk {
@ -367,18 +371,18 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
}
verify(exactly = 0) {
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@Test
fun `on EnableBiometricsClick should update user show unlock badge status if shown`() {
mutableShowUnlockBadgeFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupUnlockCard = true) }
val viewModel = createViewModel()
viewModel.trySendAction(AccountSecurityAction.EnableBiometricsClick)
verify(exactly = 1) {
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@ -599,14 +603,14 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
}
verify(exactly = 0) {
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@Suppress("MaxLineLength")
@Test
fun `on UnlockWithPinToggle Enabled should update show unlock badge state if card is visible`() {
mutableShowUnlockBadgeFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupUnlockCard = true) }
val initialState = DEFAULT_STATE.copy(
isUnlockWithPinEnabled = false,
)
@ -630,7 +634,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
pin = "1234",
shouldRequireMasterPasswordOnRestart = true,
)
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@ -713,18 +717,18 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.stateFlow.test {
assertEquals(DEFAULT_STATE, awaitItem())
mutableShowUnlockBadgeFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupUnlockCard = true) }
assertEquals(DEFAULT_STATE.copy(shouldShowUnlockActionCard = true), awaitItem())
}
}
@Test
fun `when UnlockActionCardDismiss action received, should dismiss unlock action card`() {
mutableShowUnlockBadgeFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupUnlockCard = true) }
val viewModel = createViewModel()
viewModel.trySendAction(AccountSecurityAction.UnlockActionCardDismiss)
verify {
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@ -732,7 +736,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
@Test
fun `when UnlockActionCardCtaClick action received, should dismiss unlock action card and send NavigateToSetupUnlockScreen event`() =
runTest {
mutableShowUnlockBadgeFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupUnlockCard = true) }
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.trySendAction(AccountSecurityAction.UnlockActionCardCtaClick)
@ -741,8 +745,8 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
awaitItem(),
)
}
verify {
settingsRepository.storeShowUnlockSettingBadge(DEFAULT_STATE.userId, false)
verify(exactly = 0) {
firstTimeActionManager.storeShowUnlockSettingBadge(showBadge = false)
}
}
@ -767,6 +771,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
savedStateHandle = SavedStateHandle().apply {
set("state", initialState)
},
firstTimeActionManager = firstTimeActionManager,
)
}

View file

@ -4,6 +4,8 @@ import android.os.Build
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
@ -31,7 +33,13 @@ class AutoFillViewModelTest : BaseViewModelTest() {
private val authRepository: AuthRepository = mockk {
every { userStateFlow.value?.activeUserId } returns "activeUserId"
}
private val mutableShowAutofillActionCardFlow = MutableStateFlow(false)
private val mutableFirstTimeStateFlow = MutableStateFlow(FirstTimeState())
private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowAutoFillSettingBadge(any()) } just runs
}
private val settingsRepository: SettingsRepository = mockk {
every { isInlineAutofillEnabled } returns true
every { isInlineAutofillEnabled = any() } just runs
@ -44,8 +52,6 @@ class AutoFillViewModelTest : BaseViewModelTest() {
every { isAccessibilityEnabledStateFlow } returns mutableIsAccessibilityEnabledStateFlow
every { isAutofillEnabledStateFlow } returns mutableIsAutofillEnabledStateFlow
every { disableAutofill() } just runs
every { getShowAutofillBadgeFlow(any()) } returns mutableShowAutofillActionCardFlow
every { storeShowAutoFillSettingBadge(any(), any()) } just runs
}
@BeforeEach
@ -207,7 +213,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
@Test
fun `on AutoFillServicesClick should update show autofill in repository if card shown`() {
mutableShowAutofillActionCardFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupAutofillCard = true) }
val viewModel = createViewModel()
assertEquals(
DEFAULT_STATE.copy(showAutofillActionCard = true),
@ -215,8 +221,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
)
viewModel.trySendAction(AutoFillAction.AutoFillServicesClick(true))
verify(exactly = 1) {
settingsRepository.storeShowAutoFillSettingBadge(
DEFAULT_STATE.activeUserId,
firstTimeActionManager.storeShowAutoFillSettingBadge(
false,
)
}
@ -232,8 +237,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
)
viewModel.trySendAction(AutoFillAction.AutoFillServicesClick(true))
verify(exactly = 0) {
settingsRepository.storeShowAutoFillSettingBadge(
DEFAULT_STATE.activeUserId,
firstTimeActionManager.storeShowAutoFillSettingBadge(
false,
)
}
@ -314,7 +318,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.stateFlow.test {
assertEquals(DEFAULT_STATE, awaitItem())
mutableShowAutofillActionCardFlow.emit(true)
mutableFirstTimeStateFlow.update { it.copy(showSetupAutofillCard = true) }
assertEquals(DEFAULT_STATE.copy(showAutofillActionCard = true), awaitItem())
}
}
@ -323,7 +327,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
@Test
fun `when AutoFillActionCardCtaClick action is sent should update show autofill in repository and send NavigateToSetupAutofill event`() =
runTest {
mutableShowAutofillActionCardFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupAutofillCard = true) }
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick)
@ -332,20 +336,19 @@ class AutoFillViewModelTest : BaseViewModelTest() {
awaitItem(),
)
}
verify {
settingsRepository.storeShowAutoFillSettingBadge(DEFAULT_STATE.activeUserId, false)
verify(exactly = 0) {
firstTimeActionManager.storeShowAutoFillSettingBadge(false)
}
}
@Suppress("MaxLineLength")
@Test
fun `when DismissShowAutofillActionCard action is sent should update show autofill in repository`() {
mutableShowAutofillActionCardFlow.update { true }
mutableFirstTimeStateFlow.update { it.copy(showSetupAutofillCard = true) }
val viewModel = createViewModel()
viewModel.trySendAction(AutoFillAction.DismissShowAutofillActionCard)
verify {
settingsRepository.storeShowAutoFillSettingBadge(
DEFAULT_STATE.activeUserId,
firstTimeActionManager.storeShowAutoFillSettingBadge(
false,
)
}
@ -357,6 +360,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
savedStateHandle = SavedStateHandle().apply { set("state", state) },
settingsRepository = settingsRepository,
authRepository = authRepository,
firstTimeActionManager = firstTimeActionManager,
)
}

View file

@ -1,7 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
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
@ -28,13 +27,11 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
every { getFeatureFlagFlow(FlagKey.ImportLoginsFlow) } returns mutableImportLoginsFlagFlow
every { getFeatureFlag(FlagKey.ImportLoginsFlow) } returns false
}
private val authRepository = mockk<AuthRepository> {
every { setShowImportLogins(any()) } just runs
}
private val mutableFirstTimeStateFlow = MutableStateFlow(DEFAULT_FIRST_TIME_STATE)
private val firstTimeActionManager = mockk<FirstTimeActionManager> {
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLogins(any()) } just runs
}
@Test
@ -113,15 +110,19 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
awaitItem(),
)
}
verify(exactly = 1) { authRepository.setShowImportLogins(false) }
verify(exactly = 0) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
}
}
@Test
fun `ImportLoginsCardDismissClick action should set repository value to false `() = runTest {
val viewModel = createViewModel()
mutableImportLoginsFlagFlow.update { true }
val viewModel = createViewModel()
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
verify(exactly = 1) { authRepository.setShowImportLogins(false) }
verify(exactly = 1) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
}
}
@Suppress("MaxLineLength")
@ -130,13 +131,14 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
runTest {
val viewModel = createViewModel()
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
verify(exactly = 0) { authRepository.setShowImportLogins(false) }
verify(exactly = 0) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
}
}
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel(
environmentRepository = environmentRepository,
featureFlagManager = featureFlagManager,
authRepository = authRepository,
firstTimeActionManager = firstTimeActionManager,
)
}

View file

@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
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.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
@ -81,6 +82,12 @@ class VaultViewModelTest : BaseViewModelTest() {
private var switchAccountResult: SwitchAccountResult = SwitchAccountResult.NoChange
private val mutableFirstTimeStateFlow = MutableStateFlow(FirstTimeState())
private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLogins(any()) } just runs
}
private val authRepository: AuthRepository =
mockk {
every { userStateFlow } returns mutableUserStateFlow
@ -88,7 +95,6 @@ class VaultViewModelTest : BaseViewModelTest() {
every { hasPendingAccountAddition = any() } just runs
every { logout(any()) } just runs
every { switchAccount(any()) } answers { switchAccountResult }
every { setShowImportLogins(any()) } just runs
}
private val settingsRepository: SettingsRepository = mockk {
@ -1554,7 +1560,7 @@ class VaultViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.DismissImportActionCard)
verify(exactly = 1) {
authRepository.setShowImportLogins(false)
firstTimeActionManager.storeShowImportLogins(false)
}
}
@ -1573,21 +1579,21 @@ class VaultViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.DismissImportActionCard)
verify(exactly = 0) {
authRepository.setShowImportLogins(false)
firstTimeActionManager.storeShowImportLogins(false)
}
}
@Suppress("MaxLineLength")
@Test
fun `when ImportActionCardClick is sent, repository called to set value to false and NavigateToImportLogins event is sent`() =
fun `when ImportActionCardClick is sent, NavigateToImportLogins event is sent`() =
runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.trySendAction(VaultAction.ImportActionCardClick)
assertEquals(VaultEvent.NavigateToImportLogins, awaitItem())
}
verify(exactly = 1) {
authRepository.setShowImportLogins(false)
verify(exactly = 0) {
firstTimeActionManager.storeShowImportLogins(false)
}
}
@ -1606,7 +1612,7 @@ class VaultViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.ImportActionCardClick)
verify(exactly = 0) {
authRepository.setShowImportLogins(false)
firstTimeActionManager.storeShowImportLogins(false)
}
}
@ -1620,6 +1626,7 @@ class VaultViewModelTest : BaseViewModelTest() {
vaultRepository = vaultRepository,
organizationEventManager = organizationEventManager,
featureFlagManager = featureFlagManager,
firstTimeActionManager = firstTimeActionManager,
)
}