Unverified Commit 314ba546 authored by Jonathan Almeida's avatar Jonathan Almeida
Browse files

Close #6963: Sync account when SyncedTabsFeature is started

parent 0f6e197c
......@@ -33,6 +33,7 @@ import kotlin.coroutines.CoroutineContext
* @param presenter See [SyncedTabsPresenter].
* @param interactor See [SyncedTabsInteractor].
*/
@Suppress("LongParameterList")
class SyncedTabsFeature(
storage: SyncedTabsStorage,
accountManager: FxaAccountManager,
......@@ -40,7 +41,7 @@ class SyncedTabsFeature(
lifecycleOwner: LifecycleOwner,
coroutineContext: CoroutineContext = Dispatchers.IO,
onTabClicked: (Tab) -> Unit,
controller: SyncedTabsController = DefaultController(
private val controller: SyncedTabsController = DefaultController(
storage,
accountManager,
view,
......@@ -53,9 +54,8 @@ class SyncedTabsFeature(
lifecycleOwner
),
private val interactor: SyncedTabsInteractor = DefaultInteractor(
accountManager,
controller,
view,
coroutineContext,
onTabClicked
)
) : LifecycleAwareFeature {
......
......@@ -12,19 +12,23 @@ import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.ext.withConstellation
import mozilla.components.service.fxa.sync.SyncReason
import kotlin.coroutines.CoroutineContext
internal class DefaultController(
override val provider: SyncedTabsProvider,
override val accountManager: FxaAccountManager,
override val view: SyncedTabsView,
private val coroutineContext: CoroutineContext
coroutineContext: CoroutineContext
) : SyncedTabsController {
override fun syncTabs() {
view.startLoading()
private val scope = CoroutineScope(coroutineContext)
val scope = CoroutineScope(coroutineContext)
/**
* See [SyncedTabsController.refreshSyncedTabs]
*/
override fun refreshSyncedTabs() {
view.startLoading()
scope.launch {
accountManager.withConstellation {
......@@ -45,4 +49,16 @@ internal class DefaultController(
}
}
}
/**
* See [SyncedTabsController.syncAccount]
*/
override fun syncAccount() {
scope.launch {
accountManager.withConstellation {
refreshDevicesAsync().await()
}
accountManager.syncNowAsync(SyncReason.User)
}
}
}
......@@ -21,5 +21,10 @@ interface SyncedTabsController {
* Requests for remote tabs and notifies the [SyncedTabsView] when available with [SyncedTabsView.displaySyncedTabs]
* otherwise notifies the appropriate error to [SyncedTabsView.onError].
*/
fun syncTabs()
fun refreshSyncedTabs()
/**
* Requests for the account on the [FxaAccountManager] to perform a sync.
*/
fun syncAccount()
}
......@@ -4,19 +4,13 @@
package mozilla.components.feature.syncedtabs.interactor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.controller.SyncedTabsController
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.ext.withConstellation
import mozilla.components.service.fxa.sync.SyncReason
import kotlin.coroutines.CoroutineContext
internal class DefaultInteractor(
override val accountManager: FxaAccountManager,
override val controller: SyncedTabsController,
override val view: SyncedTabsView,
private val coroutineContext: CoroutineContext,
override val tabClicked: (Tab) -> Unit
) : SyncedTabsInteractor {
......@@ -33,11 +27,6 @@ internal class DefaultInteractor(
}
override fun onRefresh() {
CoroutineScope(coroutineContext).launch {
accountManager.withConstellation {
refreshDevicesAsync()
}
accountManager.syncNowAsync(SyncReason.User, true)
}
controller.syncAccount()
}
}
......@@ -5,15 +5,15 @@
package mozilla.components.feature.syncedtabs.interactor
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.controller.SyncedTabsController
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.support.base.feature.LifecycleAwareFeature
/**
* An interactor that handles events from [SyncedTabsView.Listener].
*/
interface SyncedTabsInteractor : SyncedTabsView.Listener, LifecycleAwareFeature {
val accountManager: FxaAccountManager
val controller: SyncedTabsController
val view: SyncedTabsView
val tabClicked: (Tab) -> Unit
}
......@@ -72,7 +72,7 @@ internal class DefaultPresenter(
return
}
controller.syncTabs()
controller.syncAccount()
}
override fun stop() {
......@@ -89,7 +89,7 @@ internal class DefaultPresenter(
}
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
controller.syncTabs()
controller.refreshSyncedTabs()
}
override fun onAuthenticationProblems() {
......@@ -103,7 +103,7 @@ internal class DefaultPresenter(
) : SyncStatusObserver {
override fun onIdle() {
controller.syncTabs()
controller.refreshSyncedTabs()
}
override fun onError(error: Exception?) {
......
......@@ -60,7 +60,7 @@ class SyncedTabsStorage(
}
/**
* Get the list of synced tabs.
* See [SyncedTabsProvider.getSyncedTabs].
*/
override suspend fun getSyncedTabs(): List<SyncedDeviceTabs> {
val otherDevices = syncClients() ?: return emptyList()
......
/*
* 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/.
*/
/* 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/. */
......@@ -11,6 +5,7 @@
package mozilla.components.feature.syncedtabs.controller
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
......@@ -23,6 +18,7 @@ import mozilla.components.feature.syncedtabs.storage.SyncedTabsStorage
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.sync.SyncReason
import mozilla.components.support.test.mock
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.After
......@@ -64,7 +60,7 @@ class DefaultControllerTest {
coroutineContext
)
controller.syncTabs()
controller.refreshSyncedTabs()
verify(view).startLoading()
verify(view).stopLoading()
......@@ -91,8 +87,51 @@ class DefaultControllerTest {
`when`(storage.getSyncedTabs()).thenReturn(emptyList())
controller.syncTabs()
controller.refreshSyncedTabs()
verify(view).onError(ErrorType.MULTIPLE_DEVICES_UNAVAILABLE)
}
@Test
fun `display synced tabs`() = runBlockingTest {
val controller = DefaultController(
storage,
accountManager,
view,
coroutineContext
)
val account: OAuthAccount = mock()
val constellation: DeviceConstellation = mock()
val state: ConstellationState = mock()
`when`(accountManager.authenticatedAccount()).thenReturn(account)
`when`(account.deviceConstellation()).thenReturn(constellation)
`when`(constellation.state()).thenReturn(state)
`when`(state.otherDevices).thenReturn(listOf(mock()))
`when`(storage.getSyncedTabs()).thenReturn(emptyList())
controller.refreshSyncedTabs()
}
@Test
fun `syncAccount refreshes devices and syncs`() = runBlockingTest {
val controller = DefaultController(
storage,
accountManager,
view,
coroutineContext
)
val account: OAuthAccount = mock()
val constellation: DeviceConstellation = mock()
`when`(accountManager.authenticatedAccount()).thenReturn(account)
`when`(account.deviceConstellation()).thenReturn(constellation)
`when`(constellation.refreshDevicesAsync()).thenReturn(CompletableDeferred(true))
controller.syncAccount()
verify(constellation).refreshDevicesAsync()
verify(accountManager).syncNowAsync(SyncReason.User, false)
}
}
/*
* 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/.
*/
/* 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/. */
......@@ -12,32 +6,27 @@ package mozilla.components.feature.syncedtabs.interactor
import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
import mozilla.components.concept.sync.DeviceConstellation
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.feature.syncedtabs.controller.SyncedTabsController
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.sync.SyncReason
import mozilla.components.support.test.mock
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
class DefaultInteractorTest {
private val accountManager: FxaAccountManager = mock()
private val view: SyncedTabsView = mock()
private val controller: SyncedTabsController = mock()
@Test
fun start() = runBlockingTest {
val view =
TestSyncedTabsView()
val feature = DefaultInteractor(
accountManager,
view,
coroutineContext
controller,
view
) {}
assertNull(view.listener)
......@@ -52,9 +41,8 @@ class DefaultInteractorTest {
val view =
TestSyncedTabsView()
val feature = DefaultInteractor(
accountManager,
view,
coroutineContext
controller,
view
) {}
assertNull(view.listener)
......@@ -72,9 +60,8 @@ class DefaultInteractorTest {
fun `onTabClicked invokes callback`() = runBlockingTest {
var invoked = false
val feature = DefaultInteractor(
accountManager,
view,
coroutineContext
controller,
view
) {
invoked = true
}
......@@ -87,33 +74,25 @@ class DefaultInteractorTest {
@Test
fun `onRefresh does not update devices when there is no constellation`() = runBlockingTest {
val feature = DefaultInteractor(
accountManager,
view,
coroutineContext
controller,
view
) {}
feature.onRefresh()
verify(accountManager).syncNowAsync(SyncReason.User, true)
verify(controller).syncAccount()
}
@Test
fun `onRefresh updates devices when there is a constellation`() = runBlockingTest {
val feature = DefaultInteractor(
accountManager,
view,
coroutineContext
controller,
view
) {}
val account: OAuthAccount = mock()
val constellation: DeviceConstellation = mock()
`when`(accountManager.authenticatedAccount()).thenReturn(account)
`when`(account.deviceConstellation()).thenReturn(constellation)
feature.onRefresh()
verify(constellation).refreshDevicesAsync()
verify(accountManager).syncNowAsync(SyncReason.User, true)
verify(controller).syncAccount()
}
private class TestSyncedTabsView : SyncedTabsView {
......
......@@ -102,7 +102,7 @@ class DefaultPresenterTest {
presenter.start()
verify(controller).syncTabs()
verify(controller).syncAccount()
}
@Test
......@@ -130,7 +130,7 @@ class DefaultPresenterTest {
presenter.accountObserver.onAuthenticated(mock(), mock())
verify(controller).syncTabs()
verify(controller).refreshSyncedTabs()
}
@Test
......@@ -158,7 +158,7 @@ class DefaultPresenterTest {
presenter.eventObserver.onIdle()
verify(controller).syncTabs()
verify(controller).refreshSyncedTabs()
}
@Test
......
......@@ -44,6 +44,9 @@ permalink: /changelog/
menu also contains an access entry for Add-ons Manager, for which `onAddonsManagerTapped` needs to be passed in the
constructor.
* **feature-syncedtabs**
* When the SyncedTabsFeature is started it syncs the devices and account first.
# 42.0.0
* [Commits](https://github.com/mozilla-mobile/android-components/compare/v41.0.0...42.0.0)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment