PM-14009 complete fix importlogins card show logic (#4175)

This commit is contained in:
Dave Severns 2024-10-28 14:22:30 -04:00 committed by ifernandezdiaz
parent 14a11680b1
commit 296ee17211
14 changed files with 205 additions and 36 deletions

View file

@ -298,4 +298,20 @@ interface SettingsDiskSource {
* Emits updates that track [getShowUnlockSettingBadge] for the given [userId]. * Emits updates that track [getShowUnlockSettingBadge] for the given [userId].
*/ */
fun getShowUnlockSettingBadgeFlow(userId: String): Flow<Boolean?> fun getShowUnlockSettingBadgeFlow(userId: String): Flow<Boolean?>
/**
* Gets whether or not the given [userId] has signalled they want to import logins later.
*/
fun getShowImportLoginsSettingBadge(userId: String): Boolean?
/**
* Stores the given value for whether or not the given [userId] has signalled they want to
* set import logins later, during first time usage.
*/
fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?)
/**
* Emits updates that track [getShowImportLoginsSettingBadge] for the given [userId].
*/
fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?>
} }

View file

@ -35,6 +35,7 @@ private const val INITIAL_AUTOFILL_DIALOG_SHOWN = "addSitePromptShown"
private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount" private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount"
private const val SHOW_AUTOFILL_SETTING_BADGE = "showAutofillSettingBadge" private const val SHOW_AUTOFILL_SETTING_BADGE = "showAutofillSettingBadge"
private const val SHOW_UNLOCK_SETTING_BADGE = "showUnlockSettingBadge" private const val SHOW_UNLOCK_SETTING_BADGE = "showUnlockSettingBadge"
private const val SHOW_IMPORT_LOGINS_SETTING_BADGE = "showImportLoginsSettingBadge"
private const val LAST_SCHEME_CHANGE_INSTANT = "lastDatabaseSchemeChangeInstant" private const val LAST_SCHEME_CHANGE_INSTANT = "lastDatabaseSchemeChangeInstant"
/** /**
@ -65,6 +66,9 @@ class SettingsDiskSourceImpl(
private val mutableShowUnlockSettingBadgeFlowMap = private val mutableShowUnlockSettingBadgeFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>() mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableShowImportLoginsSettingBadgeFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableIsIconLoadingDisabledFlow = bufferedMutableSharedFlow<Boolean?>() private val mutableIsIconLoadingDisabledFlow = bufferedMutableSharedFlow<Boolean?>()
private val mutableIsCrashLoggingEnabledFlow = bufferedMutableSharedFlow<Boolean?>() private val mutableIsCrashLoggingEnabledFlow = bufferedMutableSharedFlow<Boolean?>()
@ -412,6 +416,24 @@ class SettingsDiskSourceImpl(
getMutableShowUnlockSettingBadgeFlow(userId = userId) getMutableShowUnlockSettingBadgeFlow(userId = userId)
.onSubscription { emit(getShowUnlockSettingBadge(userId)) } .onSubscription { emit(getShowUnlockSettingBadge(userId)) }
override fun getShowImportLoginsSettingBadge(userId: String): Boolean? {
return getBoolean(
key = SHOW_IMPORT_LOGINS_SETTING_BADGE.appendIdentifier(userId),
)
}
override fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?) {
putBoolean(
key = SHOW_IMPORT_LOGINS_SETTING_BADGE.appendIdentifier(userId),
showBadge,
)
getMutableShowImportLoginsSettingBadgeFlow(userId).tryEmit(showBadge)
}
override fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?> =
getMutableShowImportLoginsSettingBadgeFlow(userId)
.onSubscription { emit(getShowImportLoginsSettingBadge(userId)) }
private fun getMutableLastSyncFlow( private fun getMutableLastSyncFlow(
userId: String, userId: String,
): MutableSharedFlow<Instant?> = ): MutableSharedFlow<Instant?> =
@ -455,4 +477,11 @@ class SettingsDiskSourceImpl(
mutableShowUnlockSettingBadgeFlowMap.getOrPut(userId) { mutableShowUnlockSettingBadgeFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1) bufferedMutableSharedFlow(replay = 1)
} }
private fun getMutableShowImportLoginsSettingBadgeFlow(
userId: String,
): MutableSharedFlow<Boolean?> =
mutableShowImportLoginsSettingBadgeFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1)
}
} }

View file

@ -61,4 +61,9 @@ interface FirstTimeActionManager {
* Update the value of the showImportLogins status for the active user. * Update the value of the showImportLogins status for the active user.
*/ */
fun storeShowImportLogins(showImportLogins: Boolean) fun storeShowImportLogins(showImportLogins: Boolean)
/**
* Update the value of the showImportLoginsSettingsBadge status for the active user.
*/
fun storeShowImportLoginsSettingsBadge(showBadge: Boolean)
} }

View file

@ -99,11 +99,11 @@ class FirstTimeActionManagerImpl @Inject constructor(
.filterNotNull() .filterNotNull()
.flatMapLatest { .flatMapLatest {
combine( combine(
getShowImportLoginsFlowInternal(userId = it), getShowImportLoginsSettingBadgeFlowInternal(userId = it),
featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow), featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow),
) { showImportLogins, importLoginsEnabled -> ) { showImportLogins, importLoginsEnabled ->
val shouldShowImportLogins = showImportLogins && importLoginsEnabled val shouldShowImportLoginsSettings = showImportLogins && importLoginsEnabled
listOf(shouldShowImportLogins) listOf(shouldShowImportLoginsSettings)
} }
.map { list -> .map { list ->
list.count { showImportLogins -> showImportLogins } list.count { showImportLogins -> showImportLogins }
@ -129,12 +129,14 @@ class FirstTimeActionManagerImpl @Inject constructor(
getShowImportLoginsFlowInternal(userId = activeUserId), getShowImportLoginsFlowInternal(userId = activeUserId),
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = activeUserId), settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = activeUserId),
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = activeUserId), settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = activeUserId),
getShowImportLoginsSettingBadgeFlowInternal(userId = activeUserId),
), ),
) { ) {
FirstTimeState( FirstTimeState(
showImportLoginsCard = it[0], showImportLoginsCard = it[0],
showSetupUnlockCard = it[1], showSetupUnlockCard = it[1],
showSetupAutofillCard = it[2], showSetupAutofillCard = it[2],
showImportLoginsCardInSettings = it[3],
) )
} }
} }
@ -144,24 +146,12 @@ class FirstTimeActionManagerImpl @Inject constructor(
showImportLoginsCard = null, showImportLoginsCard = null,
showSetupUnlockCard = null, showSetupUnlockCard = null,
showSetupAutofillCard = null, showSetupAutofillCard = null,
showImportLoginsCardInSettings = null,
), ),
) )
} }
.distinctUntilChanged() .distinctUntilChanged()
/**
* Internal implementation to get a flow of the showImportLogins value which takes
* into account if the vault is empty.
*/
private fun getShowImportLoginsFlowInternal(userId: String): Flow<Boolean> {
return authDiskSource.getShowImportLoginsFlow(userId)
.combine(
vaultDiskSource.getCiphers(userId),
) { showImportLogins, ciphers ->
showImportLogins ?: true && ciphers.isEmpty()
}
}
/** /**
* Get the current [FirstTimeState] of the active user if available, otherwise return * Get the current [FirstTimeState] of the active user if available, otherwise return
* a default configuration. * a default configuration.
@ -176,12 +166,15 @@ class FirstTimeActionManagerImpl @Inject constructor(
showImportLoginsCard = authDiskSource.getShowImportLogins(it), showImportLoginsCard = authDiskSource.getShowImportLogins(it),
showSetupUnlockCard = settingsDiskSource.getShowUnlockSettingBadge(it), showSetupUnlockCard = settingsDiskSource.getShowUnlockSettingBadge(it),
showSetupAutofillCard = settingsDiskSource.getShowAutoFillSettingBadge(it), showSetupAutofillCard = settingsDiskSource.getShowAutoFillSettingBadge(it),
showImportLoginsCardInSettings = settingsDiskSource
.getShowImportLoginsSettingBadge(it),
) )
} }
?: FirstTimeState( ?: FirstTimeState(
showImportLoginsCard = null, showImportLoginsCard = null,
showSetupUnlockCard = null, showSetupUnlockCard = null,
showSetupAutofillCard = null, showSetupAutofillCard = null,
showImportLoginsCardInSettings = null,
) )
override fun storeShowUnlockSettingBadge(showBadge: Boolean) { override fun storeShowUnlockSettingBadge(showBadge: Boolean) {
@ -207,4 +200,40 @@ class FirstTimeActionManagerImpl @Inject constructor(
showImportLogins = showImportLogins, showImportLogins = showImportLogins,
) )
} }
override fun storeShowImportLoginsSettingsBadge(showBadge: Boolean) {
val activeUserId = authDiskSource.userState?.activeUserId ?: return
settingsDiskSource.storeShowImportLoginsSettingBadge(
userId = activeUserId,
showBadge = showBadge,
)
}
/**
* Internal implementation to get a flow of the showImportLogins value which takes
* into account if the vault is empty.
*/
private fun getShowImportLoginsFlowInternal(userId: String): Flow<Boolean> {
return authDiskSource
.getShowImportLoginsFlow(userId)
.combine(
vaultDiskSource.getCiphers(userId),
) { showImportLogins, ciphers ->
showImportLogins ?: true && ciphers.isEmpty()
}
}
/**
* Internal implementation to get a flow of the showImportLogins value which takes
* into account if the vault is empty.
*/
private fun getShowImportLoginsSettingBadgeFlowInternal(userId: String): Flow<Boolean> {
return settingsDiskSource
.getShowImportLoginsSettingBadgeFlow(userId)
.combine(
vaultDiskSource.getCiphers(userId),
) { showImportLogins, ciphers ->
showImportLogins ?: false && ciphers.isEmpty()
}
}
} }

View file

@ -5,6 +5,7 @@ package com.x8bit.bitwarden.data.platform.manager.model
*/ */
data class FirstTimeState( data class FirstTimeState(
val showImportLoginsCard: Boolean, val showImportLoginsCard: Boolean,
val showImportLoginsCardInSettings: Boolean,
val showSetupUnlockCard: Boolean, val showSetupUnlockCard: Boolean,
val showSetupAutofillCard: Boolean, val showSetupAutofillCard: Boolean,
) { ) {
@ -16,9 +17,11 @@ data class FirstTimeState(
showImportLoginsCard: Boolean? = null, showImportLoginsCard: Boolean? = null,
showSetupUnlockCard: Boolean? = null, showSetupUnlockCard: Boolean? = null,
showSetupAutofillCard: Boolean? = null, showSetupAutofillCard: Boolean? = null,
showImportLoginsCardInSettings: Boolean? = null,
) : this( ) : this(
showImportLoginsCard = showImportLoginsCard ?: true, showImportLoginsCard = showImportLoginsCard ?: true,
showSetupUnlockCard = showSetupUnlockCard ?: false, showSetupUnlockCard = showSetupUnlockCard ?: false,
showSetupAutofillCard = showSetupAutofillCard ?: false, showSetupAutofillCard = showSetupAutofillCard ?: false,
showImportLoginsCardInSettings = showImportLoginsCardInSettings ?: false,
) )
} }

View file

@ -33,7 +33,7 @@ class VaultSettingsViewModel @Inject constructor(
.toBaseWebVaultImportUrl, .toBaseWebVaultImportUrl,
isNewImportLoginsFlowEnabled = featureFlagManager isNewImportLoginsFlowEnabled = featureFlagManager
.getFeatureFlag(FlagKey.ImportLoginsFlow), .getFeatureFlag(FlagKey.ImportLoginsFlow),
showImportActionCard = firstTimeState.showImportLoginsCard, showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
) )
}, },
) { ) {
@ -48,7 +48,7 @@ class VaultSettingsViewModel @Inject constructor(
.firstTimeStateFlow .firstTimeStateFlow
.map { .map {
VaultSettingsAction.Internal.UserFirstTimeStateChanged( VaultSettingsAction.Internal.UserFirstTimeStateChanged(
showImportLoginsCard = it.showImportLoginsCard, showImportLoginsCard = it.showImportLoginsCardInSettings,
) )
} }
.onEach(::sendAction) .onEach(::sendAction)
@ -79,7 +79,7 @@ class VaultSettingsViewModel @Inject constructor(
private fun handleImportLoginsCardDismissClicked() { private fun handleImportLoginsCardDismissClicked() {
if (!state.shouldShowImportCard) return if (!state.shouldShowImportCard) return
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
} }
private fun handleImportLoginsCardClicked() { private fun handleImportLoginsCardClicked() {

View file

@ -97,6 +97,7 @@ class ImportLoginsViewModel @Inject constructor(
is SyncVaultDataResult.Success -> { is SyncVaultDataResult.Success -> {
if (result.itemsAvailable) { if (result.itemsAvailable) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
mutableStateFlow.update { mutableStateFlow.update {
it.copy( it.copy(
showBottomSheet = true, showBottomSheet = true,
@ -160,6 +161,8 @@ class ImportLoginsViewModel @Inject constructor(
private fun handleConfirmImportLater() { private fun handleConfirmImportLater() {
dismissDialog() dismissDialog()
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = true)
sendEvent(ImportLoginsEvent.NavigateBack) sendEvent(ImportLoginsEvent.NavigateBack)
} }

View file

@ -186,6 +186,7 @@ class VaultViewModel @Inject constructor(
} }
private fun handleDismissImportActionCard() { private fun handleDismissImportActionCard() {
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
if (!state.showImportActionCard) return if (!state.showImportActionCard) return
firstTimeActionManager.storeShowImportLogins(false) firstTimeActionManager.storeShowImportLogins(false)
} }

View file

@ -1062,10 +1062,10 @@ class SettingsDiskSourceTest {
@Test @Test
fun `storeShowAutoFillSettingBadge should update the flow value`() = runTest { fun `storeShowAutoFillSettingBadge should update the flow value`() = runTest {
val mockUserId = "mockUserId" val mockUserId = "mockUserId"
settingsDiskSource.storeShowAutoFillSettingBadge(mockUserId, true)
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = mockUserId).test { settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = mockUserId).test {
// The initial values of the Flow are in sync // The initial values of the Flow are in sync
assertTrue(awaitItem() ?: false) assertFalse(awaitItem() ?: false)
settingsDiskSource.storeShowAutoFillSettingBadge(mockUserId, true)
assertTrue(awaitItem() ?: false) assertTrue(awaitItem() ?: false)
// update the value to false // update the value to false
@ -1103,10 +1103,10 @@ class SettingsDiskSourceTest {
@Test @Test
fun `storeShowUnlockSettingsBadge should update the flow value`() = runTest { fun `storeShowUnlockSettingsBadge should update the flow value`() = runTest {
val mockUserId = "mockUserId" val mockUserId = "mockUserId"
settingsDiskSource.storeShowUnlockSettingBadge(mockUserId, true)
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = mockUserId).test { settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = mockUserId).test {
// The initial values of the Flow are in sync // The initial values of the Flow are in sync
assertTrue(awaitItem() ?: false) assertFalse(awaitItem() ?: false)
settingsDiskSource.storeShowUnlockSettingBadge(mockUserId, true)
assertTrue(awaitItem() ?: false) assertTrue(awaitItem() ?: false)
// update the value to false // update the value to false
@ -1165,4 +1165,47 @@ class SettingsDiskSourceTest {
actual, actual,
) )
} }
@Test
fun `getShowImportLoginsSettingBadge should pull from shared preferences`() {
val mockUserId = "mockUserId"
val showImportLoginsSettingBadgeKey =
"bwPreferencesStorage:showImportLoginsSettingBadge_$mockUserId"
fakeSharedPreferences.edit {
putBoolean(showImportLoginsSettingBadgeKey, true)
}
assertTrue(
settingsDiskSource.getShowImportLoginsSettingBadge(userId = mockUserId)!!,
)
}
@Test
fun `storeShowImportLoginsSettingBadge should update SharedPreferences`() {
val mockUserId = "mockUserId"
val showImportLoginsSettingBadgeKey =
"bwPreferencesStorage:showImportLoginsSettingBadge_$mockUserId"
settingsDiskSource.storeShowImportLoginsSettingBadge(mockUserId, true)
assertTrue(
fakeSharedPreferences.getBoolean(showImportLoginsSettingBadgeKey, false),
)
}
@Test
fun `storeShowImportLoginsSettingBadge should update the flow value`() = runTest {
val mockUserId = "mockUserId"
settingsDiskSource.getShowImportLoginsSettingBadgeFlow(mockUserId).test {
// The initial values of the Flow are in sync
assertFalse(awaitItem() ?: false)
settingsDiskSource.storeShowImportLoginsSettingBadge(
userId = mockUserId,
showBadge = true,
)
assertTrue(awaitItem() ?: false)
// update the value to false
settingsDiskSource.storeShowImportLoginsSettingBadge(
userId = mockUserId, false,
)
assertFalse(awaitItem() ?: true)
}
}
} }

View file

@ -63,6 +63,7 @@ class FakeSettingsDiskSource : SettingsDiskSource {
private val userSignIns = mutableMapOf<String, Boolean>() private val userSignIns = mutableMapOf<String, Boolean>()
private val userShowAutoFillBadge = mutableMapOf<String, Boolean?>() private val userShowAutoFillBadge = mutableMapOf<String, Boolean?>()
private val userShowUnlockBadge = mutableMapOf<String, Boolean?>() private val userShowUnlockBadge = mutableMapOf<String, Boolean?>()
private val userShowImportLoginsBadge = mutableMapOf<String, Boolean?>()
private var storedLastDatabaseSchemeChangeInstant: Instant? = null private var storedLastDatabaseSchemeChangeInstant: Instant? = null
private val mutableShowAutoFillSettingBadgeFlowMap = private val mutableShowAutoFillSettingBadgeFlowMap =
@ -71,6 +72,9 @@ class FakeSettingsDiskSource : SettingsDiskSource {
private val mutableShowUnlockSettingBadgeFlowMap = private val mutableShowUnlockSettingBadgeFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>() mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableShowImportLoginsSettingBadgeFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
override var appLanguage: AppLanguage? = null override var appLanguage: AppLanguage? = null
override var appTheme: AppTheme override var appTheme: AppTheme
@ -323,6 +327,19 @@ class FakeSettingsDiskSource : SettingsDiskSource {
emit(getShowUnlockSettingBadge(userId = userId)) emit(getShowUnlockSettingBadge(userId = userId))
} }
override fun getShowImportLoginsSettingBadge(userId: String): Boolean? =
userShowImportLoginsBadge[userId]
override fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?) {
userShowImportLoginsBadge[userId] = showBadge
getMutableShowImportLoginsSettingBadgeFlow(userId).tryEmit(showBadge)
}
override fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?> =
getMutableShowImportLoginsSettingBadgeFlow(userId = userId).onSubscription {
emit(getShowImportLoginsSettingBadge(userId = userId))
}
//region Private helper functions //region Private helper functions
private fun getMutableScreenCaptureAllowedFlow(userId: String): MutableSharedFlow<Boolean?> { private fun getMutableScreenCaptureAllowedFlow(userId: String): MutableSharedFlow<Boolean?> {
return mutableScreenCaptureAllowedFlowMap.getOrPut(userId) { return mutableScreenCaptureAllowedFlowMap.getOrPut(userId) {
@ -369,5 +386,11 @@ class FakeSettingsDiskSource : SettingsDiskSource {
bufferedMutableSharedFlow(replay = 1) bufferedMutableSharedFlow(replay = 1)
} }
private fun getMutableShowImportLoginsSettingBadgeFlow(
userId: String,
): MutableSharedFlow<Boolean?> = mutableShowImportLoginsSettingBadgeFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1)
}
//endregion Private helper functions //endregion Private helper functions
} }

View file

@ -110,7 +110,7 @@ class FirstTimeActionManagerTest {
// for the import logins count it is dependent on the feature flag state and // for the import logins count it is dependent on the feature flag state and
// cipher list being empty // cipher list being empty
mutableImportLoginsFlow.update { true } mutableImportLoginsFlow.update { true }
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
assertEquals(2, awaitItem()) assertEquals(2, awaitItem())
} }
} }
@ -127,15 +127,15 @@ class FirstTimeActionManagerTest {
firstTimeActionManager.allVaultSettingsBadgeCountFlow.test { firstTimeActionManager.allVaultSettingsBadgeCountFlow.test {
assertEquals(0, awaitItem()) assertEquals(0, awaitItem())
mutableImportLoginsFlow.update { true } mutableImportLoginsFlow.update { true }
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
assertEquals(1, awaitItem()) assertEquals(1, awaitItem())
mutableImportLoginsFlow.update { false } mutableImportLoginsFlow.update { false }
assertEquals(0, awaitItem()) assertEquals(0, awaitItem())
mutableImportLoginsFlow.update { true } mutableImportLoginsFlow.update { true }
assertEquals(1, awaitItem()) assertEquals(1, awaitItem())
fakeAuthDiskSource.storeShowImportLogins(USER_ID, false) fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, false)
assertEquals(0, awaitItem()) assertEquals(0, awaitItem())
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
assertEquals(1, awaitItem()) assertEquals(1, awaitItem())
mutableCiphersListFlow.update { mutableCiphersListFlow.update {
listOf(mockCipher) listOf(mockCipher)
@ -156,7 +156,7 @@ class FirstTimeActionManagerTest {
), ),
awaitItem(), awaitItem(),
) )
fakeAuthDiskSource.storeShowImportLogins(USER_ID, false) firstTimeActionManager.storeShowImportLogins(false)
assertEquals( assertEquals(
FirstTimeState( FirstTimeState(
showImportLoginsCard = false, showImportLoginsCard = false,
@ -233,6 +233,13 @@ class FirstTimeActionManagerTest {
firstTimeActionManager.storeShowImportLogins(showImportLogins = true) firstTimeActionManager.storeShowImportLogins(showImportLogins = true)
assertTrue(fakeAuthDiskSource.getShowImportLogins(userId = USER_ID)!!) assertTrue(fakeAuthDiskSource.getShowImportLogins(userId = USER_ID)!!)
} }
@Test
fun `storeShowImportLoginsSettingsBadge should store value of true to disk`() {
fakeAuthDiskSource.userState = MOCK_USER_STATE
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
assertFalse(fakeSettingsDiskSource.getShowImportLoginsSettingBadge(userId = USER_ID)!!)
}
} }
private const val USER_ID: String = "userId" private const val USER_ID: String = "userId"

View file

@ -31,7 +31,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
private val firstTimeActionManager = mockk<FirstTimeActionManager> { private val firstTimeActionManager = mockk<FirstTimeActionManager> {
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLogins(any()) } just runs every { storeShowImportLoginsSettingsBadge(any()) } just runs
} }
@Test @Test
@ -84,7 +84,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel() val viewModel = createViewModel()
assertTrue(viewModel.stateFlow.value.shouldShowImportCard) assertTrue(viewModel.stateFlow.value.shouldShowImportCard)
mutableFirstTimeStateFlow.update { mutableFirstTimeStateFlow.update {
it.copy(showImportLoginsCard = false) it.copy(showImportLoginsCardInSettings = false)
} }
assertFalse(viewModel.stateFlow.value.shouldShowImportCard) assertFalse(viewModel.stateFlow.value.shouldShowImportCard)
} }
@ -111,7 +111,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
) )
} }
verify(exactly = 0) { verify(exactly = 0) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
} }
} }
@ -121,7 +121,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick) viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
verify(exactly = 1) { verify(exactly = 1) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
} }
} }
@ -132,7 +132,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick) viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
verify(exactly = 0) { verify(exactly = 0) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
} }
} }
@ -143,4 +143,4 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
) )
} }
private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(showImportLoginsCard = true) private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(showImportLoginsCardInSettings = true)

View file

@ -28,6 +28,7 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
private val firstTimeActionManager: FirstTimeActionManager = mockk { private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { storeShowImportLogins(any()) } just runs every { storeShowImportLogins(any()) } just runs
every { storeShowImportLoginsSettingsBadge(any()) } just runs
} }
@Test @Test
@ -100,8 +101,9 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
} }
} }
@Suppress("MaxLineLength")
@Test @Test
fun `ConfirmImportLater sets dialog state to null and sends NavigateBack event`() = runTest { fun `ConfirmImportLater sets dialog state to null, sends NavigateBack event, and stores first time values`() = runTest {
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.stateEventFlow(backgroundScope) { stateFlow, eventFlow -> viewModel.stateEventFlow(backgroundScope) { stateFlow, eventFlow ->
// Initial state // Initial state
@ -133,6 +135,10 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
eventFlow.awaitItem(), eventFlow.awaitItem(),
) )
} }
verify(exactly = 1) {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = true)
}
} }
@Test @Test
@ -320,6 +326,7 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
) )
verify { verify {
firstTimeActionManager.storeShowImportLogins(showImportLogins = false) firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
} }
} }

View file

@ -86,6 +86,7 @@ class VaultViewModelTest : BaseViewModelTest() {
private val firstTimeActionManager: FirstTimeActionManager = mockk { private val firstTimeActionManager: FirstTimeActionManager = mockk {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLogins(any()) } just runs every { storeShowImportLogins(any()) } just runs
every { storeShowImportLoginsSettingsBadge(any()) } just runs
} }
private val authRepository: AuthRepository = private val authRepository: AuthRepository =
@ -1555,12 +1556,14 @@ class VaultViewModelTest : BaseViewModelTest() {
} }
} }
@Suppress("MaxLineLength")
@Test @Test
fun `when DismissImportActionCard is sent, repository called to set value to false`() { fun `when DismissImportActionCard is sent, repository called to showImportLogins to false and storeShowImportLoginsBadge to true`() {
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.DismissImportActionCard) viewModel.trySendAction(VaultAction.DismissImportActionCard)
verify(exactly = 1) { verify(exactly = 1) {
firstTimeActionManager.storeShowImportLogins(false) firstTimeActionManager.storeShowImportLogins(false)
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
} }
} }