BITAU-182 BITAU-107 Don't show authetnicator sync toggle below API 31 (#4156)

This commit is contained in:
Andrew Haisting 2024-10-25 16:27:59 -05:00 committed by GitHub
parent 0960f61c37
commit 164cc09f19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 9 deletions

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
import android.os.Build
import android.os.Parcelable import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -19,6 +20,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
@ -71,9 +73,9 @@ class AccountSecurityViewModel @Inject constructor(
?.activeAccount ?.activeAccount
?.hasMasterPassword != false, ?.hasMasterPassword != false,
isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled, isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled,
shouldShowEnableAuthenticatorSync = featureFlagManager.getFeatureFlag( shouldShowEnableAuthenticatorSync =
key = FlagKey.AuthenticatorSync, featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) &&
), !isBuildVersionBelow(Build.VERSION_CODES.S),
userId = userId, userId = userId,
vaultTimeout = settingsRepository.vaultTimeout, vaultTimeout = settingsRepository.vaultTimeout,
vaultTimeoutAction = settingsRepository.vaultTimeoutAction, vaultTimeoutAction = settingsRepository.vaultTimeoutAction,
@ -372,9 +374,11 @@ class AccountSecurityViewModel @Inject constructor(
private fun handleAuthenticatorSyncFeatureFlagUpdate( private fun handleAuthenticatorSyncFeatureFlagUpdate(
action: AccountSecurityAction.Internal.AuthenticatorSyncFeatureFlagUpdate, action: AccountSecurityAction.Internal.AuthenticatorSyncFeatureFlagUpdate,
) { ) {
val shouldShowAuthenticatorSync =
action.isEnabled && !isBuildVersionBelow(Build.VERSION_CODES.S)
mutableStateFlow.update { mutableStateFlow.update {
it.copy( it.copy(
shouldShowEnableAuthenticatorSync = action.isEnabled, shouldShowEnableAuthenticatorSync = shouldShowAuthenticatorSync,
) )
} }
} }

View file

@ -1001,7 +1001,7 @@ Do you want to switch to this account?</string>
<string name="please_restart_registration_or_try_logging_in">Please restart registration or try logging in. You may already have an account.</string> <string name="please_restart_registration_or_try_logging_in">Please restart registration or try logging in. You may already have an account.</string>
<string name="restart_registration">Restart registration</string> <string name="restart_registration">Restart registration</string>
<string name="authenticator_sync">Authenticator Sync</string> <string name="authenticator_sync">Authenticator Sync</string>
<string name="allow_bitwarden_authenticator_syncing">Allow Bitwarden Authenticator Syncing</string> <string name="allow_bitwarden_authenticator_syncing">Allow authenticator syncing</string>
<string name="there_was_an_issue_validating_the_registration_token">There was an issue validating the registration token.</string> <string name="there_was_an_issue_validating_the_registration_token">There was an issue validating the registration token.</string>
<string name="turn_on_autofill">Turn on autofill</string> <string name="turn_on_autofill">Turn on autofill</string>
<string name="use_autofill_to_log_into_your_accounts">Use autofill to log into your accounts with a single tap.</string> <string name="use_autofill_to_log_into_your_accounts">Use autofill to log into your accounts with a single tap.</string>

View file

@ -1465,7 +1465,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
@Test @Test
fun `sync with Bitwarden authenticator UI should be displayed according to state`() { fun `sync with Bitwarden authenticator UI should be displayed according to state`() {
val toggleText = "Allow Bitwarden Authenticator Syncing" val toggleText = "Allow authenticator syncing"
composeTestRule.onNodeWithText(toggleText).assertDoesNotExist() composeTestRule.onNodeWithText(toggleText).assertDoesNotExist()
mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) } mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) }
@ -1486,7 +1486,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
fun `sync with Bitwarden authenticator click should send AuthenticatorSyncToggle action`() { fun `sync with Bitwarden authenticator click should send AuthenticatorSyncToggle action`() {
mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) } mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) }
composeTestRule composeTestRule
.onNodeWithText("Allow Bitwarden Authenticator Syncing") .onNodeWithText("Allow authenticator syncing")
.performScrollTo() .performScrollTo()
.performClick() .performClick()
verify { viewModel.trySendAction(AccountSecurityAction.AuthenticatorSyncToggle(true)) } verify { viewModel.trySendAction(AccountSecurityAction.AuthenticatorSyncToggle(true)) }

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
import android.os.Build
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test import app.cash.turbine.test
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
@ -22,6 +23,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
@ -35,7 +37,9 @@ import io.mockk.coVerify
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs import io.mockk.runs
import io.mockk.unmockkStatic
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
@ -44,7 +48,9 @@ import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import javax.crypto.Cipher import javax.crypto.Cipher
@ -91,6 +97,16 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
every { getFeatureFlag(FlagKey.AuthenticatorSync) } returns false every { getFeatureFlag(FlagKey.AuthenticatorSync) } returns false
} }
@BeforeEach
fun setup() {
mockkStatic(::isBuildVersionBelow)
}
@AfterEach
fun teardown() {
unmockkStatic(::isBuildVersionBelow)
}
@Test @Test
fun `initial state should be correct when saved state is set`() { fun `initial state should be correct when saved state is set`() {
val viewModel = createViewModel(initialState = DEFAULT_STATE) val viewModel = createViewModel(initialState = DEFAULT_STATE)
@ -679,7 +695,8 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `when featureFlagManger returns true for AuthenticatorSync, should show authenticator sync UI`() { fun `when featureFlagManger returns true for AuthenticatorSync, and version is at least 31 should show authenticator sync UI`() {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
val vm = createViewModel( val vm = createViewModel(
initialState = null, initialState = null,
featureFlagManager = mockk { featureFlagManager = mockk {
@ -693,8 +710,22 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
) )
} }
@Suppress("MaxLineLength")
@Test
fun `when featureFlagManger returns true for AuthenticatorSync, and version is under 31 should not show authenticator sync UI`() {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
every { featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) } returns true
every { featureFlagManager.getFeatureFlagFlow(FlagKey.AuthenticatorSync) } returns emptyFlow()
val vm = createViewModel(initialState = null)
assertEquals(
DEFAULT_STATE,
vm.stateFlow.value,
)
}
@Test @Test
fun `when featureFlagManger updates value AuthenticatorSync, should update UI`() = runTest { fun `when featureFlagManger updates value AuthenticatorSync, should update UI`() = runTest {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
val featureFlagFlow = MutableStateFlow(false) val featureFlagFlow = MutableStateFlow(false)
val vm = createViewModel( val vm = createViewModel(
initialState = null, initialState = null,
@ -712,6 +743,25 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
} }
} }
@Test
@Suppress("MaxLineLength")
fun `when featureFlagManger updates value AuthenticatorSync, authenticator sync row should never show if below API 31`() =
runTest {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
val featureFlagFlow = MutableStateFlow(false)
every { featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) } returns false
every {
featureFlagManager.getFeatureFlagFlow(FlagKey.AuthenticatorSync)
} returns featureFlagFlow
val vm = createViewModel(initialState = null)
vm.stateFlow.test {
assertEquals(DEFAULT_STATE, awaitItem())
featureFlagFlow.value = true
featureFlagFlow.value = false
expectNoEvents()
}
}
@Test @Test
fun `when showUnlockBadgeFlow updates value, should update state`() = runTest { fun `when showUnlockBadgeFlow updates value, should update state`() = runTest {
val viewModel = createViewModel() val viewModel = createViewModel()

View file

@ -7,4 +7,4 @@ import android.os.Build
* *
* @see Build.VERSION_CODES * @see Build.VERSION_CODES
*/ */
fun isBuildVersionBelow(version: Int): Boolean = version > Build.VERSION.SDK_INT internal fun isBuildVersionBelow(version: Int): Boolean = version > Build.VERSION.SDK_INT