parent
4fea8b31ed
commit
51937e73fc
@ -0,0 +1,105 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.settings.logins
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import mozilla.components.concept.sync.AccountObserver
|
||||||
|
import mozilla.components.concept.sync.AuthType
|
||||||
|
import mozilla.components.concept.sync.OAuthAccount
|
||||||
|
import mozilla.components.service.fxa.SyncEngine
|
||||||
|
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||||
|
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to manage the [R.string.pref_key_password_sync_logins] preference.
|
||||||
|
*/
|
||||||
|
class SyncLoginsPreferenceView(
|
||||||
|
private val syncLoginsPreference: Preference,
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
accountManager: FxaAccountManager,
|
||||||
|
private val navController: NavController
|
||||||
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
accountManager.register(object : AccountObserver {
|
||||||
|
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) =
|
||||||
|
updateSyncPreferenceStatus()
|
||||||
|
override fun onLoggedOut() = updateSyncPreferenceNeedsLogin()
|
||||||
|
override fun onAuthenticationProblems() = updateSyncPreferenceNeedsReauth()
|
||||||
|
}, owner = lifecycleOwner)
|
||||||
|
|
||||||
|
val accountExists = accountManager.authenticatedAccount() != null
|
||||||
|
val needsReauth = accountManager.accountNeedsReauth()
|
||||||
|
when {
|
||||||
|
needsReauth -> updateSyncPreferenceNeedsReauth()
|
||||||
|
accountExists -> updateSyncPreferenceStatus()
|
||||||
|
!accountExists -> updateSyncPreferenceNeedsLogin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the current status of the sync preference (on/off) for the logged in user.
|
||||||
|
*/
|
||||||
|
private fun updateSyncPreferenceStatus() {
|
||||||
|
syncLoginsPreference.apply {
|
||||||
|
val syncEnginesStatus = SyncEnginesStorage(context).getStatus()
|
||||||
|
val loginsSyncStatus = syncEnginesStatus.getOrElse(SyncEngine.Passwords) { false }
|
||||||
|
summary = context.getString(
|
||||||
|
if (loginsSyncStatus) R.string.preferences_passwords_sync_logins_on
|
||||||
|
else R.string.preferences_passwords_sync_logins_off
|
||||||
|
)
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
navigateToAccountSettingsFragment()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the user can sign in to turn on sync.
|
||||||
|
*/
|
||||||
|
private fun updateSyncPreferenceNeedsLogin() {
|
||||||
|
syncLoginsPreference.apply {
|
||||||
|
summary = context.getString(R.string.preferences_passwords_sync_logins_sign_in)
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
navigateToTurnOnSyncFragment()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the user can fix their account problems to turn on sync.
|
||||||
|
*/
|
||||||
|
private fun updateSyncPreferenceNeedsReauth() {
|
||||||
|
syncLoginsPreference.apply {
|
||||||
|
summary = context.getString(R.string.preferences_passwords_sync_logins_reconnect)
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
navigateToAccountProblemFragment()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToAccountSettingsFragment() {
|
||||||
|
val directions =
|
||||||
|
SavedLoginsAuthFragmentDirections.actionGlobalAccountSettingsFragment()
|
||||||
|
navController.navigate(directions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToAccountProblemFragment() {
|
||||||
|
val directions = SavedLoginsAuthFragmentDirections.actionGlobalAccountProblemFragment()
|
||||||
|
navController.navigate(directions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToTurnOnSyncFragment() {
|
||||||
|
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
||||||
|
navController.navigate(directions)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package org.mozilla.fenix.settings.logins
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import io.mockk.CapturingSlot
|
||||||
|
import io.mockk.MockKAnnotations
|
||||||
|
import io.mockk.Runs
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.impl.annotations.MockK
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkConstructor
|
||||||
|
import io.mockk.slot
|
||||||
|
import io.mockk.unmockkConstructor
|
||||||
|
import io.mockk.verify
|
||||||
|
import mozilla.components.concept.sync.AccountObserver
|
||||||
|
import mozilla.components.service.fxa.SyncEngine
|
||||||
|
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||||
|
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections
|
||||||
|
|
||||||
|
class SyncLoginsPreferenceViewTest {
|
||||||
|
|
||||||
|
@MockK private lateinit var syncLoginsPreference: Preference
|
||||||
|
@MockK private lateinit var lifecycleOwner: LifecycleOwner
|
||||||
|
@MockK private lateinit var accountManager: FxaAccountManager
|
||||||
|
@MockK(relaxed = true) private lateinit var navController: NavController
|
||||||
|
private lateinit var accountObserver: CapturingSlot<AccountObserver>
|
||||||
|
private lateinit var clickListener: CapturingSlot<Preference.OnPreferenceClickListener>
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
MockKAnnotations.init(this)
|
||||||
|
mockkConstructor(SyncEnginesStorage::class)
|
||||||
|
|
||||||
|
accountObserver = slot()
|
||||||
|
clickListener = slot()
|
||||||
|
val context = mockk<Context> {
|
||||||
|
every { getString(R.string.preferences_passwords_sync_logins_reconnect) } returns "Reconnect"
|
||||||
|
every { getString(R.string.preferences_passwords_sync_logins_sign_in) } returns "Sign in to Sync"
|
||||||
|
every { getString(R.string.preferences_passwords_sync_logins_on) } returns "On"
|
||||||
|
every { getString(R.string.preferences_passwords_sync_logins_off) } returns "Off"
|
||||||
|
}
|
||||||
|
|
||||||
|
every { syncLoginsPreference.summary = any() } just Runs
|
||||||
|
every { syncLoginsPreference.onPreferenceClickListener = capture(clickListener) } just Runs
|
||||||
|
every { syncLoginsPreference.context } returns context
|
||||||
|
every { accountManager.register(capture(accountObserver), owner = lifecycleOwner) } just Runs
|
||||||
|
every { anyConstructed<SyncEnginesStorage>().getStatus() } returns emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun teardown() {
|
||||||
|
unmockkConstructor(SyncEnginesStorage::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `needs reauth ui on init`() {
|
||||||
|
every { accountManager.authenticatedAccount() } returns mockk()
|
||||||
|
every { accountManager.accountNeedsReauth() } returns true
|
||||||
|
createView()
|
||||||
|
|
||||||
|
verify { syncLoginsPreference.summary = "Reconnect" }
|
||||||
|
assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference))
|
||||||
|
|
||||||
|
verify {
|
||||||
|
navController.navigate(
|
||||||
|
SavedLoginsAuthFragmentDirections.actionGlobalAccountProblemFragment()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `needs reauth ui on init even if null account`() {
|
||||||
|
every { accountManager.authenticatedAccount() } returns null
|
||||||
|
every { accountManager.accountNeedsReauth() } returns true
|
||||||
|
createView()
|
||||||
|
|
||||||
|
verify { syncLoginsPreference.summary = "Reconnect" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `needs login if account does not exist`() {
|
||||||
|
every { accountManager.authenticatedAccount() } returns null
|
||||||
|
every { accountManager.accountNeedsReauth() } returns false
|
||||||
|
createView()
|
||||||
|
|
||||||
|
verify { syncLoginsPreference.summary = "Sign in to Sync" }
|
||||||
|
assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference))
|
||||||
|
|
||||||
|
verify {
|
||||||
|
navController.navigate(
|
||||||
|
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show status for existing account`() {
|
||||||
|
every { accountManager.authenticatedAccount() } returns mockk()
|
||||||
|
every { accountManager.accountNeedsReauth() } returns false
|
||||||
|
createView()
|
||||||
|
|
||||||
|
verify { syncLoginsPreference.summary = "Off" }
|
||||||
|
assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference))
|
||||||
|
|
||||||
|
verify {
|
||||||
|
navController.navigate(
|
||||||
|
SavedLoginsAuthFragmentDirections.actionGlobalAccountSettingsFragment()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `show status for existing account with passwords`() {
|
||||||
|
every { anyConstructed<SyncEnginesStorage>().getStatus() } returns mapOf(
|
||||||
|
SyncEngine.Passwords to true
|
||||||
|
)
|
||||||
|
every { accountManager.authenticatedAccount() } returns mockk()
|
||||||
|
every { accountManager.accountNeedsReauth() } returns false
|
||||||
|
createView()
|
||||||
|
|
||||||
|
verify { syncLoginsPreference.summary = "On" }
|
||||||
|
assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference))
|
||||||
|
|
||||||
|
verify {
|
||||||
|
navController.navigate(
|
||||||
|
SavedLoginsAuthFragmentDirections.actionGlobalAccountSettingsFragment()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createView() = SyncLoginsPreferenceView(
|
||||||
|
syncLoginsPreference,
|
||||||
|
lifecycleOwner,
|
||||||
|
accountManager,
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue