BIT-1094: Store default URI match detection type (#747)

This commit is contained in:
Brian Yencho 2024-01-24 09:57:08 -06:00 committed by Álison Fernandes
parent 45592a7369
commit 8a16672b4d
14 changed files with 283 additions and 39 deletions

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.data.platform.datasource.disk
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
@ -95,6 +96,16 @@ interface SettingsDiskSource {
vaultTimeoutAction: VaultTimeoutAction?,
)
/**
* Gets the default [UriMatchType] for the given [userId].
*/
fun getDefaultUriMatchType(userId: String): UriMatchType?
/**
* Stores the given default [uriMatchType] for the given [userId].
*/
fun storeDefaultUriMatchType(userId: String, uriMatchType: UriMatchType?)
/**
* Gets the current state of the pull to refresh feature for the given [userId].
*/

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.datasource.disk
import android.content.SharedPreferences
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
@ -21,6 +22,7 @@ private const val BLOCKED_AUTOFILL_URIS_KEY = "$BASE_KEY:autofillBlacklistedUris
private const val VAULT_LAST_SYNC_TIME = "$BASE_KEY:vaultLastSyncTime"
private const val VAULT_TIMEOUT_ACTION_KEY = "$BASE_KEY:vaultTimeoutAction"
private const val VAULT_TIME_IN_MINUTES_KEY = "$BASE_KEY:vaultTimeout"
private const val DEFAULT_URI_MATCH_TYPE_KEY = "$BASE_KEY:defaultUriMatch"
private const val DISABLE_ICON_LOADING_KEY = "$BASE_KEY:disableFavicon"
private const val APPROVE_PASSWORDLESS_LOGINS_KEY = "$BASE_KEY:approvePasswordlessLogins"
@ -94,6 +96,7 @@ class SettingsDiskSourceImpl(
override fun clearData(userId: String) {
storeVaultTimeoutInMinutes(userId = userId, vaultTimeoutInMinutes = null)
storeVaultTimeoutAction(userId = userId, vaultTimeoutAction = null)
storeDefaultUriMatchType(userId = userId, uriMatchType = null)
storePullToRefreshEnabled(userId = userId, isPullToRefreshEnabled = null)
storeInlineAutofillEnabled(userId = userId, isInlineAutofillEnabled = null)
storeBlockedAutofillUris(userId = userId, blockedAutofillUris = null)
@ -157,6 +160,21 @@ class SettingsDiskSourceImpl(
getMutableVaultTimeoutActionFlow(userId = userId).tryEmit(vaultTimeoutAction)
}
override fun getDefaultUriMatchType(userId: String): UriMatchType? =
getInt(key = "${DEFAULT_URI_MATCH_TYPE_KEY}_$userId")?.let { storedValue ->
UriMatchType.entries.find { it.value == storedValue }
}
override fun storeDefaultUriMatchType(
userId: String,
uriMatchType: UriMatchType?,
) {
putInt(
key = "${DEFAULT_URI_MATCH_TYPE_KEY}_$userId",
value = uriMatchType?.value,
)
}
override fun getPullToRefreshEnabled(userId: String): Boolean? =
getBoolean(key = "${PULL_TO_REFRESH_KEY}_$userId")

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.data.platform.repository
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
@ -58,6 +59,12 @@ interface SettingsRepository {
*/
var vaultTimeoutAction: VaultTimeoutAction
/**
* The default [UriMatchType] for the current user that should be used when matching URIs for
* items that have "default" as their chosen type.
*/
var defaultUriMatchType: UriMatchType
/**
* Whether or not PIN unlocking is enabled for the current user.
*/

View file

@ -5,6 +5,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
@ -127,6 +128,21 @@ class SettingsRepositoryImpl(
vaultTimeoutAction = value,
)
}
override var defaultUriMatchType: UriMatchType
get() = activeUserId
?.let {
settingsDiskSource.getDefaultUriMatchType(userId = it)
}
?: UriMatchType.DOMAIN
set(value) {
val userId = activeUserId ?: return
settingsDiskSource.storeDefaultUriMatchType(
userId = userId,
uriMatchType = value,
)
}
override val isUnlockWithPinEnabled: Boolean
get() = activeUserId
?.let { authDiskSource.getEncryptedPin(userId = it) != null }

View file

@ -0,0 +1,42 @@
package com.x8bit.bitwarden.data.platform.repository.model
/**
* Represents a way to match a known URI against a "test" one.
*
* The [value] is used for consistent storage purposes.
*/
@Suppress("MagicNumber")
enum class UriMatchType(
val value: Int,
) {
/**
* The URIs match if their top-level and second-level domains match.
*/
DOMAIN(0),
/**
* The URIs match if their hostnames (and ports if specified) match.
*/
HOST(1),
/**
* The URIs match if the "test" URI starts with the known URI.
*/
STARTS_WITH(2),
/**
* The URIs match if the "test" URI matches the known URI according to a specified regular
* expression for the item.
*/
REGULAR_EXPRESSION(3),
/**
* The URIs match if they are exactly the same.
*/
EXACT(4),
/**
* The URIs should never match.
*/
NEVER(5),
}

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
@ -40,6 +41,7 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenSelectionRow
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextRow
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
import com.x8bit.bitwarden.ui.platform.components.BitwardenWideSwitch
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.displayLabel
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
@ -193,8 +195,8 @@ fun AutoFillScreen(
@Composable
private fun UriMatchDetectionDialog(
selectedUriDetection: AutoFillState.UriDetectionMethod,
onDetectionSelect: (AutoFillState.UriDetectionMethod) -> Unit,
selectedUriDetection: UriMatchType,
onDetectionSelect: (UriMatchType) -> Unit,
) {
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
@ -205,7 +207,7 @@ private fun UriMatchDetectionDialog(
modifier = Modifier.fillMaxWidth(),
) {
Text(
text = selectedUriDetection.text(),
text = selectedUriDetection.displayLabel(),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@ -216,14 +218,15 @@ private fun UriMatchDetectionDialog(
title = stringResource(id = R.string.default_uri_match_detection),
onDismissRequest = { shouldShowDialog = false },
) {
AutoFillState.UriDetectionMethod.values().forEach { option ->
val uriMatchTypes = UriMatchType.entries
uriMatchTypes.forEach { option ->
BitwardenSelectionRow(
text = option.text,
text = option.displayLabel,
isSelected = option == selectedUriDetection,
onClick = {
shouldShowDialog = false
onDetectionSelect(
AutoFillState.UriDetectionMethod.values().first { it == option },
uriMatchTypes.first { it == option },
)
},
)

View file

@ -3,8 +3,8 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
@ -33,7 +33,7 @@ class AutoFillViewModel @Inject constructor(
isAutoFillServicesEnabled = settingsRepository.isAutofillEnabledStateFlow.value,
isCopyTotpAutomaticallyEnabled = false,
isUseInlineAutoFillEnabled = settingsRepository.isInlineAutofillEnabled,
uriDetectionMethod = AutoFillState.UriDetectionMethod.DEFAULT,
uriDetectionMethod = settingsRepository.defaultUriMatchType,
),
) {
@ -96,8 +96,7 @@ class AutoFillViewModel @Inject constructor(
}
private fun handleUriDetectionMethodSelect(action: AutoFillAction.UriDetectionMethodSelect) {
// TODO BIT-1094: Persist selection
sendEvent(AutoFillEvent.ShowToast("Not yet implemented.".asText()))
settingsRepository.defaultUriMatchType = action.uriDetectionMethod
mutableStateFlow.update {
it.copy(uriDetectionMethod = action.uriDetectionMethod)
}
@ -125,7 +124,7 @@ data class AutoFillState(
val isAutoFillServicesEnabled: Boolean,
val isCopyTotpAutomaticallyEnabled: Boolean,
val isUseInlineAutoFillEnabled: Boolean,
val uriDetectionMethod: UriDetectionMethod,
val uriDetectionMethod: UriMatchType,
) : Parcelable {
/**
@ -134,19 +133,6 @@ data class AutoFillState(
*/
val canInteractWithInlineAutofillToggle: Boolean
get() = isAutoFillServicesEnabled
/**
* A representation of the URI detection methods.
*/
enum class UriDetectionMethod(val text: Text) {
DEFAULT(text = R.string.default_text.asText()),
BASE_DOMAIN(text = R.string.base_domain.asText()),
HOST(text = R.string.host.asText()),
STARTS_WITH(text = R.string.starts_with.asText()),
REGULAR_EXPRESSION(text = R.string.reg_ex.asText()),
EXACT(text = R.string.exact.asText()),
NEVER(text = R.string.never.asText()),
}
}
/**
@ -207,10 +193,10 @@ sealed class AutoFillAction {
) : AutoFillAction()
/**
* User selected a [AutoFillState.UriDetectionMethod].
* User selected a [UriMatchType].
*/
data class UriDetectionMethodSelect(
val uriDetectionMethod: AutoFillState.UriDetectionMethod,
val uriDetectionMethod: UriMatchType,
) : AutoFillAction()
/**

View file

@ -0,0 +1,20 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
/**
* Returns a human-readable display label for the given [UriMatchType].
*/
val UriMatchType.displayLabel: Text
get() = when (this) {
UriMatchType.DOMAIN -> R.string.base_domain
UriMatchType.HOST -> R.string.host
UriMatchType.STARTS_WITH -> R.string.starts_with
UriMatchType.REGULAR_EXPRESSION -> R.string.reg_ex
UriMatchType.EXACT -> R.string.exact
UriMatchType.NEVER -> R.string.never
}
.asText()

View file

@ -4,6 +4,7 @@ import androidx.core.content.edit
import app.cash.turbine.test
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
@ -74,6 +75,10 @@ class SettingsDiskSourceTest {
userId = userId,
vaultTimeoutAction = VaultTimeoutAction.LOCK,
)
settingsDiskSource.storeDefaultUriMatchType(
userId = userId,
uriMatchType = UriMatchType.REGULAR_EXPRESSION,
)
settingsDiskSource.storePullToRefreshEnabled(
userId = userId,
isPullToRefreshEnabled = true,
@ -99,6 +104,7 @@ class SettingsDiskSourceTest {
assertNull(settingsDiskSource.getVaultTimeoutInMinutes(userId = userId))
assertNull(settingsDiskSource.getVaultTimeoutAction(userId = userId))
assertNull(settingsDiskSource.getDefaultUriMatchType(userId = userId))
assertNull(settingsDiskSource.getPullToRefreshEnabled(userId = userId))
assertNull(settingsDiskSource.getInlineAutofillEnabled(userId = userId))
assertNull(settingsDiskSource.getBlockedAutofillUris(userId = userId))
@ -379,6 +385,66 @@ class SettingsDiskSourceTest {
assertNull(fakeSharedPreferences.getString(vaultTimeoutActionKey, null))
}
@Test
fun `getDefaultUriMatchType when values are present should pull from SharedPreferences`() {
val defaultUriMatchTypeBaseKey = "bwPreferencesStorage:defaultUriMatch"
val mockUserId = "mockUserId"
val uriMatchType = UriMatchType.REGULAR_EXPRESSION
fakeSharedPreferences
.edit {
putInt(
"${defaultUriMatchTypeBaseKey}_$mockUserId",
3,
)
}
val actual = settingsDiskSource.getDefaultUriMatchType(userId = mockUserId)
assertEquals(
uriMatchType,
actual,
)
}
@Test
fun `getDefaultUriMatchType when values are absent should return null`() {
val mockUserId = "mockUserId"
assertNull(settingsDiskSource.getDefaultUriMatchType(userId = mockUserId))
}
@Test
fun `storeDefaultUriMatchType for non-null values should update SharedPreferences`() {
val defaultUriMatchTypeBaseKey = "bwPreferencesStorage:defaultUriMatch"
val mockUserId = "mockUserId"
val uriMatchType = UriMatchType.REGULAR_EXPRESSION
settingsDiskSource.storeDefaultUriMatchType(
userId = mockUserId,
uriMatchType = uriMatchType,
)
val actual = fakeSharedPreferences.getInt(
"${defaultUriMatchTypeBaseKey}_$mockUserId",
0,
)
assertEquals(
3,
actual,
)
}
@Test
fun `storeDefaultUriMatchType for null values should clear SharedPreferences`() {
val defaultUriMatchTypeBaseKey = "bwPreferencesStorage:defaultUriMatch"
val mockUserId = "mockUserId"
val defaultUriMatchTypeKey = "${defaultUriMatchTypeBaseKey}_$mockUserId"
fakeSharedPreferences.edit {
putInt(defaultUriMatchTypeKey, 3)
}
assertTrue(fakeSharedPreferences.contains(defaultUriMatchTypeKey))
settingsDiskSource.storeDefaultUriMatchType(
userId = mockUserId,
uriMatchType = null,
)
assertFalse(fakeSharedPreferences.contains(defaultUriMatchTypeKey))
}
@Test
fun `getPullToRefreshEnabled when values are present should pull from SharedPreferences`() {
val pullToRefreshBaseKey = "bwPreferencesStorage:syncOnRefresh"

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.platform.datasource.disk.util
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
@ -36,7 +37,7 @@ class FakeSettingsDiskSource : SettingsDiskSource {
private val storedLastSyncTime = mutableMapOf<String, Instant?>()
private val storedVaultTimeoutActions = mutableMapOf<String, VaultTimeoutAction?>()
private val storedVaultTimeoutInMinutes = mutableMapOf<String, Int?>()
private val storedUriMatchTypes = mutableMapOf<String, UriMatchType?>()
private val storedPullToRefreshEnabled = mutableMapOf<String, Boolean?>()
private val storedInlineAutofillEnabled = mutableMapOf<String, Boolean?>()
private val storedBlockedAutofillUris = mutableMapOf<String, List<String>?>()
@ -74,6 +75,7 @@ class FakeSettingsDiskSource : SettingsDiskSource {
override fun clearData(userId: String) {
storedVaultTimeoutActions.remove(userId)
storedVaultTimeoutInMinutes.remove(userId)
storedUriMatchTypes.remove(userId)
storedPullToRefreshEnabled.remove(userId)
storedInlineAutofillEnabled.remove(userId)
storedBlockedAutofillUris.remove(userId)
@ -124,6 +126,16 @@ class FakeSettingsDiskSource : SettingsDiskSource {
getMutableVaultTimeoutActionsFlow(userId = userId).tryEmit(vaultTimeoutAction)
}
override fun getDefaultUriMatchType(userId: String): UriMatchType? =
storedUriMatchTypes[userId]
override fun storeDefaultUriMatchType(
userId: String,
uriMatchType: UriMatchType?,
) {
storedUriMatchTypes[userId] = uriMatchType
}
override fun getPullToRefreshEnabled(userId: String): Boolean? =
storedPullToRefreshEnabled[userId]

View file

@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
import com.x8bit.bitwarden.data.platform.util.asSuccess
@ -376,6 +377,39 @@ class SettingsRepositoryTest {
}
}
@Test
fun `defaultUriMatchType should pull from and update SettingsDiskSource`() {
fakeAuthDiskSource.userState = null
assertEquals(
UriMatchType.DOMAIN,
settingsRepository.defaultUriMatchType,
)
val userId = "userId"
fakeAuthDiskSource.userState = MOCK_USER_STATE
// Updates to the disk source change the repository value
UriMatchType.entries.forEach { uriMatchType ->
fakeSettingsDiskSource.storeDefaultUriMatchType(
userId = userId,
uriMatchType = uriMatchType,
)
assertEquals(
uriMatchType,
settingsRepository.defaultUriMatchType,
)
}
// Updates to the repository value change the disk source
UriMatchType.entries.forEach { uriMatchType ->
settingsRepository.defaultUriMatchType = uriMatchType
assertEquals(
uriMatchType,
fakeSettingsDiskSource.getDefaultUriMatchType(userId = userId),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `isUnlockWithPinEnabled should return a value that tracks the existence of an encrypted PIN for the current user`() {

View file

@ -14,6 +14,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
@ -246,16 +247,16 @@ class AutoFillScreenTest : BaseComposeTest() {
@Test
fun `default URI match detection add login should be updated on or off according to state`() {
composeTestRule
.onNodeWithText("Default")
.onNodeWithText("Base domain")
.assertExists()
composeTestRule
.onNodeWithText("Starts with")
.assertDoesNotExist()
mutableStateFlow.update {
it.copy(uriDetectionMethod = AutoFillState.UriDetectionMethod.STARTS_WITH)
it.copy(uriDetectionMethod = UriMatchType.STARTS_WITH)
}
composeTestRule
.onNodeWithText("Default")
.onNodeWithText("Base domain")
.assertDoesNotExist()
composeTestRule
.onNodeWithText("Starts with")
@ -295,5 +296,5 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
isAutoFillServicesEnabled = false,
isCopyTotpAutomaticallyEnabled = false,
isUseInlineAutoFillEnabled = false,
uriDetectionMethod = AutoFillState.UriDetectionMethod.DEFAULT,
uriDetectionMethod = UriMatchType.DOMAIN,
)

View file

@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import io.mockk.every
@ -21,6 +22,8 @@ class AutoFillViewModelTest : BaseViewModelTest() {
private val settingsRepository: SettingsRepository = mockk() {
every { isInlineAutofillEnabled } returns true
every { isInlineAutofillEnabled = any() } just runs
every { defaultUriMatchType } returns UriMatchType.DOMAIN
every { defaultUriMatchType = any() } just runs
every { isAutofillEnabledStateFlow } returns mutableIsAutofillEnabledStateFlow
every { disableAutofill() } just runs
}
@ -36,7 +39,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
mutableIsAutofillEnabledStateFlow.value = true
val state = DEFAULT_STATE.copy(
isAutoFillServicesEnabled = true,
uriDetectionMethod = AutoFillState.UriDetectionMethod.REGULAR_EXPRESSION,
uriDetectionMethod = UriMatchType.REGULAR_EXPRESSION,
)
val viewModel = createViewModel(state = state)
assertEquals(state, viewModel.stateFlow.value)
@ -140,17 +143,15 @@ class AutoFillViewModelTest : BaseViewModelTest() {
}
@Test
fun `on UriDetectionMethodSelect should emit ShowToast`() = runTest {
fun `on UriDetectionMethodSelect should update the state and save the new value to settings`() {
val viewModel = createViewModel()
val method = AutoFillState.UriDetectionMethod.EXACT
viewModel.eventFlow.test {
viewModel.trySendAction(AutoFillAction.UriDetectionMethodSelect(method))
assertEquals(AutoFillEvent.ShowToast("Not yet implemented.".asText()), awaitItem())
}
val method = UriMatchType.EXACT
viewModel.trySendAction(AutoFillAction.UriDetectionMethodSelect(method))
assertEquals(
DEFAULT_STATE.copy(uriDetectionMethod = method),
viewModel.stateFlow.value,
)
verify { settingsRepository.defaultUriMatchType = method }
}
@Test
@ -175,5 +176,5 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
isAutoFillServicesEnabled = false,
isCopyTotpAutomaticallyEnabled = false,
isUseInlineAutoFillEnabled = true,
uriDetectionMethod = AutoFillState.UriDetectionMethod.DEFAULT,
uriDetectionMethod = UriMatchType.DOMAIN,
)

View file

@ -0,0 +1,27 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.util.asText
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class UriMatchTypeExtensionsTest {
@Test
fun `displayLabel should return the correct value for each type`() {
mapOf(
UriMatchType.DOMAIN to R.string.base_domain.asText(),
UriMatchType.HOST to R.string.host.asText(),
UriMatchType.STARTS_WITH to R.string.starts_with.asText(),
UriMatchType.REGULAR_EXPRESSION to R.string.reg_ex.asText(),
UriMatchType.EXACT to R.string.exact.asText(),
UriMatchType.NEVER to R.string.never.asText(),
)
.forEach { (type, label) ->
assertEquals(
label,
type.displayLabel,
)
}
}
}