diff --git a/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt b/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt index 6d06e0483d6..2b072d1e045 100644 --- a/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt +++ b/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt @@ -10,19 +10,23 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.request.RequestInterceptor -import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.service.fxa.manager.FxaAccountManager import kotlin.coroutines.CoroutineContext /** * Ties together an account manager with a session manager/tabs implementation, facilitating an * authentication flow. + * @property accountManager . + * @property redirectUrl This is the url that will be reached at the final authentication state. + * @property coroutineContext The context where [onBeginAuthentication] will be executed, + * if you want an different context you are in charge of switching. The default context is [Dispatchers.IO]. + * @property onBeginAuthentication A lambda function that receives the authentication url. */ class FirefoxAccountsAuthFeature( private val accountManager: FxaAccountManager, - private val tabsUseCases: TabsUseCases, private val redirectUrl: String, - private val coroutineContext: CoroutineContext = Dispatchers.Main + private val coroutineContext: CoroutineContext = Dispatchers.IO, + private val onBeginAuthentication: (String) -> Unit = {} ) { fun beginAuthentication() { beginAuthenticationAsync { @@ -48,7 +52,8 @@ class FirefoxAccountsAuthFeature( // UI to the user. // It's possible that the underlying problem will go away by the time the tab actually // loads, resulting in a confusing experience. - tabsUseCases.addTab.invoke(authUrl) + + onBeginAuthentication(authUrl) } } diff --git a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt index 0f30e574749..00abb98d099 100644 --- a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt +++ b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.runBlocking import mozilla.components.concept.sync.DeviceType import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.Profile -import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.service.fxa.ServerConfig import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.support.test.any @@ -21,10 +20,10 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.`when` -import org.mockito.Mockito.times -import org.mockito.Mockito.verify import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.service.fxa.DeviceConfig +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue // Same as the actual account manager, except we get to control how FirefoxAccountShaped instances // are created. This is necessary because due to some build issues (native dependencies not available @@ -48,63 +47,99 @@ class FirefoxAccountsAuthFeatureTest { @Test fun `begin authentication`() { val manager = prepareAccountManagerForSuccessfulAuthentication() - val mockAddTab: TabsUseCases.AddNewTabUseCase = mock() - val mockTabs: TabsUseCases = mock() - `when`(mockTabs.addTab).thenReturn(mockAddTab) + var path = "" + var wasCalled = false + val onBeginAuthentication: (String) -> Unit = { + path = it + wasCalled = true + } runBlocking { - val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext) + val feature = FirefoxAccountsAuthFeature( + manager, + "somePath", + this.coroutineContext, + onBeginAuthentication + ) feature.beginAuthentication() } - verify(mockAddTab, times(1)).invoke("auth://url") + assertTrue(wasCalled) + assertEquals("auth://url", path) } @Test fun `begin pairing authentication`() { val manager = prepareAccountManagerForSuccessfulAuthentication() - val mockAddTab: TabsUseCases.AddNewTabUseCase = mock() - val mockTabs: TabsUseCases = mock() - `when`(mockTabs.addTab).thenReturn(mockAddTab) + var path = "" + var wasCalled = false + val onBeginAuthentication: (String) -> Unit = { + path = it + wasCalled = true + } runBlocking { - val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext) + val feature = FirefoxAccountsAuthFeature( + manager, + "somePath", + this.coroutineContext, + onBeginAuthentication + ) feature.beginPairingAuthentication("auth://pair") } - verify(mockAddTab, times(1)).invoke("auth://url") + assertTrue(wasCalled) + assertEquals("auth://url", path) } @Test fun `begin authentication with errors`() { val manager = prepareAccountManagerForFailedAuthentication() - val mockAddTab: TabsUseCases.AddNewTabUseCase = mock() - val mockTabs: TabsUseCases = mock() - `when`(mockTabs.addTab).thenReturn(mockAddTab) + var path = "" + var wasCalled = false + val onBeginAuthentication: (String) -> Unit = { + path = it + wasCalled = true + } runBlocking { - val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext) + val feature = FirefoxAccountsAuthFeature( + manager, + "somePath", + this.coroutineContext, + onBeginAuthentication + ) feature.beginAuthentication() } // Fallback url is invoked. - verify(mockAddTab, times(1)).invoke("https://accounts.firefox.com/signin") + assertTrue(wasCalled) + assertEquals("https://accounts.firefox.com/signin", path) } @Test fun `begin pairing authentication with errors`() { val manager = prepareAccountManagerForFailedAuthentication() - val mockAddTab: TabsUseCases.AddNewTabUseCase = mock() - val mockTabs: TabsUseCases = mock() - `when`(mockTabs.addTab).thenReturn(mockAddTab) + var path = "" + var wasCalled = false + val onBeginAuthentication: (String) -> Unit = { + path = it + wasCalled = true + } runBlocking { - val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext) + val feature = FirefoxAccountsAuthFeature( + manager, + "somePath", + this.coroutineContext, + onBeginAuthentication + ) feature.beginPairingAuthentication("auth://pair") } // Fallback url is invoked. - verify(mockAddTab, times(1)).invoke("https://accounts.firefox.com/signin") + assertTrue(wasCalled) + assertEquals("https://accounts.firefox.com/signin", path) } private fun prepareAccountManagerForSuccessfulAuthentication(): TestableFxaAccountManager { diff --git a/docs/changelog.md b/docs/changelog.md index a3638eeeafe..d594ecb8f61 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,20 @@ permalink: /changelog/ * [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt) * [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt) +* **feature-accounts** + * ⚠️ **This is a breaking change**: + * The `FirefoxAccountsAuthFeature` no longer needs an `TabsUseCases` instead it takes a lambda to + allow apps to decide which action should be taken fixing [#2438](https://github.com/mozilla-mobile/android-components/issues/2438) and [#3272](https://github.com/mozilla-mobile/android-components/issues/3272). + + ```kotlin + val feature = FirefoxAccountsAuthFeature( + accountManager, + redirectUrl + ){ + tabsUseCases.addTab(authUrl) + } + ``` + * **browser-engine-gecko-nightly** * Now supports window requests. A new tab will be opened for `target="_blank"` links and `window.open` calls.