mirror of
https://github.com/bitwarden/android.git
synced 2026-10-23 11:33:22 -05:00
BIT-1488: Track changes in autofill status and send users to settings (#663)
This commit is contained in:
1 parent
3851f88828
commit
94108bcb5d
11 files changed
+324
-7
No files matched your search
+69
@@ -1,11 +1,14 @@
|
||||
package com.x8bit.bitwarden.data.platform.repository
|
||||
|
||||
import android.view.autofill.AutofillManager
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.DerivePinKeyResponse
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
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.VaultTimeout
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
@@ -14,7 +17,12 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLang
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
@@ -23,11 +31,25 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SettingsRepositoryTest {
|
||||
private val autofillManager: AutofillManager = mockk {
|
||||
every { hasEnabledAutofillServices() } answers { isAutofillEnabledAndSupported }
|
||||
every { isAutofillSupported } answers { isAutofillEnabledAndSupported }
|
||||
every { isEnabled } answers { isAutofillEnabledAndSupported }
|
||||
every { disableAutofillServices() } just runs
|
||||
}
|
||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
private val appForegroundManager: AppForegroundManager = mockk {
|
||||
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
||||
}
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
|
||||
private val vaultSdkSource: VaultSdkSource = mockk()
|
||||
|
||||
private var isAutofillEnabledAndSupported = false
|
||||
|
||||
private val settingsRepository = SettingsRepositoryImpl(
|
||||
autofillManager = autofillManager,
|
||||
appForegroundManager = appForegroundManager,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
settingsDiskSource = fakeSettingsDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
@@ -389,6 +411,53 @@ class SettingsRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `isAutofillEnabledStateFlow should emit updates if necessary when the app foreground state changes and disableAutofill is called`() =
|
||||
runTest {
|
||||
settingsRepository.isAutofillEnabledStateFlow.test {
|
||||
assertFalse(awaitItem())
|
||||
|
||||
// An update is received when both the autofill state and foreground state change
|
||||
isAutofillEnabledAndSupported = true
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.FOREGROUNDED
|
||||
assertTrue(awaitItem())
|
||||
|
||||
// An update is not received when only the foreground state changes
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.BACKGROUNDED
|
||||
expectNoEvents()
|
||||
|
||||
// An update is not received when only the autofill state changes
|
||||
isAutofillEnabledAndSupported = false
|
||||
expectNoEvents()
|
||||
|
||||
// An update is received after both states have changed
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.FOREGROUNDED
|
||||
assertFalse(awaitItem())
|
||||
|
||||
// Calling disableAutofill will result in an emission of false
|
||||
isAutofillEnabledAndSupported = true
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.BACKGROUNDED
|
||||
assertTrue(awaitItem())
|
||||
settingsRepository.disableAutofill()
|
||||
assertFalse(awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `disableAutofill should trigger an emission of false from isAutofillEnabledStateFlow and disable autofill with the OS`() {
|
||||
// Start in a state where autofill is enabled
|
||||
isAutofillEnabledAndSupported = true
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.FOREGROUNDED
|
||||
assertTrue(settingsRepository.isAutofillEnabledStateFlow.value)
|
||||
|
||||
settingsRepository.disableAutofill()
|
||||
|
||||
assertFalse(settingsRepository.isAutofillEnabledStateFlow.value)
|
||||
verify { autofillManager.disableAutofillServices() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPullToRefreshEnabledFlow should react to changes in SettingsDiskSource`() = runTest {
|
||||
val userId = "userId"
|
||||
|
||||
+60
@@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
@@ -13,6 +14,8 @@ import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
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
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
@@ -24,6 +27,7 @@ import org.junit.Test
|
||||
|
||||
class AutoFillScreenTest : BaseComposeTest() {
|
||||
|
||||
private var isSystemSettingsRequestSuccess = false
|
||||
private var onNavigateBackCalled = false
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<AutoFillEvent>()
|
||||
@@ -32,6 +36,9 @@ class AutoFillScreenTest : BaseComposeTest() {
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
private val intentManager: IntentManager = mockk {
|
||||
every { startSystemAutofillSettingsActivity() } answers { isSystemSettingsRequestSuccess }
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -39,10 +46,63 @@ class AutoFillScreenTest : BaseComposeTest() {
|
||||
AutoFillScreen(
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
viewModel = viewModel,
|
||||
intentManager = intentManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on NavigateToAutofillSettings should attempt to navigate to system settings and not show the fallback dialog when result is a success`() {
|
||||
isSystemSettingsRequestSuccess = true
|
||||
|
||||
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings)
|
||||
|
||||
verify {
|
||||
intentManager.startSystemAutofillSettingsActivity()
|
||||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on NavigateToAutofillSettings should attempt to navigate to system settings and show the fallback dialog when result is not a success`() {
|
||||
isSystemSettingsRequestSuccess = false
|
||||
|
||||
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings)
|
||||
|
||||
verify {
|
||||
intentManager.startSystemAutofillSettingsActivity()
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText(
|
||||
"We were unable to automatically open the Android autofill settings menu for " +
|
||||
"you. You can navigate to the autofill settings menu manually from Android " +
|
||||
"Settings>System>Languages and input>Advanced>Autofill service.",
|
||||
)
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Ok")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on autofill settings fallback dialog Ok click should dismiss the dialog`() {
|
||||
isSystemSettingsRequestSuccess = false
|
||||
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings)
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Ok")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on auto fill services toggle should send AutoFillServicesClick`() {
|
||||
composeTestRule
|
||||
|
||||
+44
-3
@@ -10,15 +10,19 @@ import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val mutableIsAutofillEnabledStateFlow = MutableStateFlow(false)
|
||||
private val settingsRepository: SettingsRepository = mockk() {
|
||||
every { isInlineAutofillEnabled } returns true
|
||||
every { isInlineAutofillEnabled = any() } just runs
|
||||
every { isAutofillEnabledStateFlow } returns mutableIsAutofillEnabledStateFlow
|
||||
every { disableAutofill() } just runs
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -29,6 +33,7 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when set`() {
|
||||
mutableIsAutofillEnabledStateFlow.value = true
|
||||
val state = DEFAULT_STATE.copy(
|
||||
isAutoFillServicesEnabled = true,
|
||||
uriDetectionMethod = AutoFillState.UriDetectionMethod.REGULAR_EXPRESSION,
|
||||
@@ -37,6 +42,26 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
assertEquals(state, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `changes in autofill enabled status should update the state`() {
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||
|
||||
mutableIsAutofillEnabledStateFlow.value = true
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(isAutoFillServicesEnabled = true),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
|
||||
mutableIsAutofillEnabledStateFlow.value = false
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(isAutoFillServicesEnabled = false),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AskToAddLoginClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
@@ -51,14 +76,30 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AutoFillServicesClick should emit ShowToast`() = runTest {
|
||||
fun `on AutoFillServicesClick with false should disable autofill`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(AutoFillAction.AutoFillServicesClick(false))
|
||||
verify {
|
||||
settingsRepository.disableAutofill()
|
||||
}
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AutoFillServicesClick with true should emit NavigateToAutofillSettings`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AutoFillAction.AutoFillServicesClick(true))
|
||||
assertEquals(AutoFillEvent.ShowToast("Not yet implemented.".asText()), awaitItem())
|
||||
assertEquals(
|
||||
AutoFillEvent.NavigateToAutofillSettings,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(isAutoFillServicesEnabled = true),
|
||||
DEFAULT_STATE,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user