tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6cefde734b2ab549bdaee1f05d45ba5ef29f1d8c
parent 8328c2006d5e572d684f62fb447d09974227f486
Author: Julie De Lorenzo <jdelorenzo@mozilla.com>
Date:   Fri, 31 Oct 2025 22:26:09 +0000

Bug 1977667:  Remove `NavigationInteractor` and move logic into TabManagerController r=android-reviewers,007

Differential Revision: https://phabricator.services.mozilla.com/D270352

Diffstat:
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/NavigationInteractor.kt | 125-------------------------------------------------------------------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/TabManagerController.kt | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt | 28++++++++--------------------
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/DefaultTabManagerControllerTest.kt | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Dmobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/NavigationInteractorTest.kt | 157-------------------------------------------------------------------------------
5 files changed, 266 insertions(+), 317 deletions(-)

diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/NavigationInteractor.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/NavigationInteractor.kt @@ -1,125 +0,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/. */ - -package org.mozilla.fenix.tabstray.controller - -import androidx.navigation.NavController -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.service.fxa.manager.FxaAccountManager -import mozilla.telemetry.glean.private.NoExtras -import org.mozilla.fenix.GleanMetrics.Events -import org.mozilla.fenix.GleanMetrics.TabsTray -import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint -import org.mozilla.fenix.home.HomeScreenViewModel.Companion.ALL_NORMAL_TABS -import org.mozilla.fenix.home.HomeScreenViewModel.Companion.ALL_PRIVATE_TABS -import org.mozilla.fenix.tabstray.ext.isActiveDownload -import org.mozilla.fenix.tabstray.ui.TabManagementFragmentDirections - -/** - * An interactor that helps with navigating to different parts of the app from the tab manager. - */ -interface NavigationInteractor { - - /** - * Called when tab manager should be dismissed. - */ - fun onTabManagerDismissed() - - /** - * Called when clicking the account settings button. - */ - fun onAccountSettingsClicked() - - /** - * Called when clicking the tab settings button. - */ - fun onTabSettingsClicked() - - /** - * Called when clicking the close all tabs button. - */ - fun onCloseAllTabsClicked(private: Boolean) - - /** - * Called when cancelling private downloads confirmed. - */ - fun onCloseAllPrivateTabsWarningConfirmed(private: Boolean) - - /** - * Called when opening the recently closed tabs menu button. - */ - fun onOpenRecentlyClosedClicked() -} - -/** - * A default implementation of [NavigationInteractor]. - * - * This is slated to get refactored in: - * https://bugzilla.mozilla.org/show_bug.cgi?id=1977667 - */ -class DefaultNavigationInteractor( - private val browserStore: BrowserStore, - private val navController: NavController, - private val dismissTabManagerAndNavigateHome: (sessionId: String) -> Unit, - private val showCancelledDownloadWarning: (downloadCount: Int, tabId: String?, source: String?) -> Unit, - private val accountManager: FxaAccountManager, -) : NavigationInteractor { - - override fun onTabManagerDismissed() { - TabsTray.closed.record(NoExtras()) - } - - override fun onAccountSettingsClicked() { - val isSignedIn = accountManager.authenticatedAccount() != null - - val direction = if (isSignedIn) { - TabManagementFragmentDirections.actionGlobalAccountSettingsFragment() - } else { - TabManagementFragmentDirections.actionGlobalTurnOnSync( - entrypoint = FenixFxAEntryPoint.NavigationInteraction, - ) - } - navController.navigate(direction) - } - - override fun onTabSettingsClicked() { - navController.navigate( - TabManagementFragmentDirections.actionGlobalTabSettingsFragment(), - ) - } - - override fun onOpenRecentlyClosedClicked() { - navController.navigate( - TabManagementFragmentDirections.actionGlobalRecentlyClosed(), - ) - Events.recentlyClosedTabsOpened.record(NoExtras()) - } - - override fun onCloseAllTabsClicked(private: Boolean) { - closeAllTabs(private, isConfirmed = false) - } - - override fun onCloseAllPrivateTabsWarningConfirmed(private: Boolean) { - closeAllTabs(private, isConfirmed = true) - } - - private fun closeAllTabs(private: Boolean, isConfirmed: Boolean) { - val sessionsToClose = if (private) { - ALL_PRIVATE_TABS - } else { - ALL_NORMAL_TABS - } - - if (private && !isConfirmed) { - val privateDownloads = browserStore.state.downloads.filter { - it.value.private && it.value.isActiveDownload() - } - if (privateDownloads.isNotEmpty()) { - showCancelledDownloadWarning(privateDownloads.size, null, null) - return - } - } - dismissTabManagerAndNavigateHome(sessionsToClose) - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/TabManagerController.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/controller/TabManagerController.kt @@ -28,6 +28,7 @@ import mozilla.components.feature.accounts.push.CloseTabsUseCases import mozilla.components.feature.downloads.ui.DownloadCancelDialogFragment import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.lib.state.DelicateAction +import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.GleanMetrics.Collections @@ -179,11 +180,37 @@ interface TabManagerController : SyncedTabsController, InactiveTabsController, T * Navigates to the sign into Sync flow */ fun handleSignInClicked() + + /** + * Called when clicking the account settings button. + */ + fun onAccountSettingsClicked() + + /** + * Called when clicking the tab settings button. + */ + fun onTabSettingsClicked() + + /** + * Called when clicking the close all tabs button. + */ + fun onCloseAllTabsClicked(private: Boolean) + + /** + * Called when cancelling private downloads confirmed. + */ + fun onCloseAllPrivateTabsWarningConfirmed(private: Boolean) + + /** + * Called when opening the recently closed tabs menu button. + */ + fun onOpenRecentlyClosedClicked() } /** * Default implementation of [TabManagerController]. * + * @param accountManager [FxaAccountManager] used to determine signed in status. * @param activity [HomeActivity] used to perform top-level app actions. * @param appStore [AppStore] used to dispatch any [AppAction]. * @param tabsTrayStore [TabsTrayStore] used to read/update the [TabsTrayState]. @@ -193,7 +220,6 @@ interface TabManagerController : SyncedTabsController, InactiveTabsController, T * @param navController [NavController] used to navigate away from the tab manager. * @param navigateToHomeAndDeleteSession Lambda used to return to the Homescreen and delete the current session. * @param profiler [Profiler] used to add profiler markers. - * @param navigationInteractor [NavigationInteractor] used to perform navigation actions with side effects. * @param tabsUseCases Use case wrapper for interacting with tabs. * @param fenixBrowserUseCases [FenixBrowserUseCases] used for adding new homepage tabs. * @param bookmarksStorage Storage layer for retrieving and saving bookmarks. @@ -210,6 +236,7 @@ interface TabManagerController : SyncedTabsController, InactiveTabsController, T */ @Suppress("TooManyFunctions", "LongParameterList") class DefaultTabManagerController( + private val accountManager: FxaAccountManager, private val activity: HomeActivity, private val appStore: AppStore, private val tabsTrayStore: TabsTrayStore, @@ -219,7 +246,6 @@ class DefaultTabManagerController( private val navController: NavController, private val navigateToHomeAndDeleteSession: (String) -> Unit, private val profiler: Profiler?, - private val navigationInteractor: NavigationInteractor, private val tabsUseCases: TabsUseCases, private val fenixBrowserUseCases: FenixBrowserUseCases, private val bookmarksStorage: BookmarksStorage, @@ -271,7 +297,7 @@ class DefaultTabManagerController( ) } - navigationInteractor.onTabManagerDismissed() + TabsTray.closed.record(NoExtras()) profiler?.addMarker( "DefaultTabManagerController.onNewTabTapped", startTime, @@ -627,10 +653,69 @@ class DefaultTabManagerController( ) } + override fun onAccountSettingsClicked() { + val isSignedIn = accountManager.authenticatedAccount() != null + + val direction = if (isSignedIn) { + TabManagementFragmentDirections.actionGlobalAccountSettingsFragment() + } else { + TabManagementFragmentDirections.actionGlobalTurnOnSync( + entrypoint = FenixFxAEntryPoint.NavigationInteraction, + ) + } + navController.navigate(direction) + } + + override fun onTabSettingsClicked() { + navController.navigate( + TabManagementFragmentDirections.actionGlobalTabSettingsFragment(), + ) + } + + override fun onCloseAllTabsClicked(private: Boolean) { + closeAllTabs(private = private, isConfirmed = false) + } + + override fun onCloseAllPrivateTabsWarningConfirmed(private: Boolean) { + closeAllTabs(private = private, isConfirmed = true) + } + + override fun onOpenRecentlyClosedClicked() { + navController.navigate( + TabManagementFragmentDirections.actionGlobalRecentlyClosed(), + ) + Events.recentlyClosedTabsOpened.record(NoExtras()) + } + /** * Marks the inactive tabs auto close dialog as shown and to not be displayed again. */ private fun markDialogAsShown() { settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true } + + /** + * Close all tabs. + * + * @param private Whether to close all of the Private tabs or all of the Normal tabs. + * @param isConfirmed: whether the user has confirmed the warning message + */ + private fun closeAllTabs(private: Boolean, isConfirmed: Boolean) { + val sessionsToClose = if (private) { + ALL_PRIVATE_TABS + } else { + ALL_NORMAL_TABS + } + + if (private && !isConfirmed) { + val privateDownloads = browserStore.state.downloads.filter { + it.value.private && it.value.isActiveDownload() + } + if (privateDownloads.isNotEmpty()) { + showCancelledDownloadWarning(privateDownloads.size, null, null) + return + } + } + dismissTabManagerAndNavigateHome(sessionsToClose) + } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt @@ -82,10 +82,8 @@ import org.mozilla.fenix.tabstray.TabsTrayStore import org.mozilla.fenix.tabstray.TabsTrayTelemetryMiddleware import org.mozilla.fenix.tabstray.binding.SecureTabManagerBinding import org.mozilla.fenix.tabstray.browser.TabSorter -import org.mozilla.fenix.tabstray.controller.DefaultNavigationInteractor import org.mozilla.fenix.tabstray.controller.DefaultTabManagerController import org.mozilla.fenix.tabstray.controller.DefaultTabManagerInteractor -import org.mozilla.fenix.tabstray.controller.NavigationInteractor import org.mozilla.fenix.tabstray.controller.TabManagerController import org.mozilla.fenix.tabstray.controller.TabManagerInteractor import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsIntegration @@ -105,7 +103,6 @@ class TabManagementFragment : DialogFragment() { private lateinit var tabManagerInteractor: TabManagerInteractor private lateinit var tabManagerController: TabManagerController - private lateinit var navigationInteractor: NavigationInteractor private lateinit var enablePbmPinLauncher: ActivityResultLauncher<Intent> @VisibleForTesting @@ -168,16 +165,8 @@ class TabManagementFragment : DialogFragment() { ) } - navigationInteractor = - DefaultNavigationInteractor( - browserStore = requireComponents.core.store, - navController = findNavController(), - dismissTabManagerAndNavigateHome = ::navigateToHomeAndDeleteSession, - showCancelledDownloadWarning = ::showCancelledDownloadWarning, - accountManager = requireComponents.backgroundServices.accountManager, - ) - tabManagerController = DefaultTabManagerController( + accountManager = requireComponents.backgroundServices.accountManager, activity = activity, appStore = requireComponents.appStore, tabsTrayStore = tabsTrayStore, @@ -186,7 +175,6 @@ class TabManagementFragment : DialogFragment() { browsingModeManager = activity.browsingModeManager, navController = findNavController(), navigateToHomeAndDeleteSession = ::navigateToHomeAndDeleteSession, - navigationInteractor = navigationInteractor, profiler = requireComponents.core.engine.profiler, tabsUseCases = requireComponents.useCases.tabsUseCases, fenixBrowserUseCases = requireComponents.useCases.fenixBrowserUseCases, @@ -337,9 +325,9 @@ class TabManagementFragment : DialogFragment() { onSaveToCollectionClick = tabManagerInteractor::onAddSelectedTabsToCollectionClicked, onShareSelectedTabsClick = tabManagerInteractor::onShareSelectedTabs, - onTabSettingsClick = navigationInteractor::onTabSettingsClicked, - onRecentlyClosedClick = navigationInteractor::onOpenRecentlyClosedClicked, - onAccountSettingsClick = navigationInteractor::onAccountSettingsClicked, + onTabSettingsClick = tabManagerController::onTabSettingsClicked, + onRecentlyClosedClick = tabManagerController::onOpenRecentlyClosedClicked, + onAccountSettingsClick = tabManagerController::onAccountSettingsClicked, onDeleteAllTabsClick = { if (tabsTrayStore.state.selectedPage == Page.NormalTabs) { tabsTrayStore.dispatch(TabsTrayAction.CloseAllNormalTabs) @@ -347,7 +335,7 @@ class TabManagementFragment : DialogFragment() { tabsTrayStore.dispatch(TabsTrayAction.CloseAllPrivateTabs) } - navigationInteractor.onCloseAllTabsClicked( + tabManagerController.onCloseAllTabsClicked( private = tabsTrayStore.state.selectedPage == Page.PrivateTabs, ) }, @@ -361,7 +349,7 @@ class TabManagementFragment : DialogFragment() { PrivateBrowsingLocked.bannerNegativeClicked.record() }, onTabAutoCloseBannerViewOptionsClick = { - navigationInteractor.onTabSettingsClicked() + tabManagerController.onTabSettingsClicked() requireContext().settings().shouldShowAutoCloseTabsBanner = false requireContext().settings().lastCfrShownTimeInMillis = @@ -387,7 +375,7 @@ class TabManagementFragment : DialogFragment() { false requireContext().settings().lastCfrShownTimeInMillis = System.currentTimeMillis() - navigationInteractor.onTabSettingsClicked() + tabManagerController.onTabSettingsClicked() TabsTray.inactiveTabsCfrSettings.record(NoExtras()) }, onInactiveTabsCFRDismiss = { @@ -516,7 +504,7 @@ class TabManagementFragment : DialogFragment() { if (tabId != null) { tabManagerInteractor.onDeletePrivateTabWarningAccepted(tabId, source) } else { - navigationInteractor.onCloseAllPrivateTabsWarningConfirmed(private = true) + tabManagerController.onCloseAllPrivateTabsWarningConfirmed(private = true) } } diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/DefaultTabManagerControllerTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/DefaultTabManagerControllerTest.kt @@ -35,12 +35,14 @@ import mozilla.components.concept.storage.BookmarkNodeType import mozilla.components.concept.storage.BookmarksStorage import mozilla.components.feature.accounts.push.CloseTabsUseCases import mozilla.components.feature.tabs.TabsUseCases +import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.support.test.ext.joinBlocking import mozilla.components.support.test.libstate.ext.waitUntilIdle import mozilla.components.support.test.middleware.CaptureActionsMiddleware import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.rule.runTestOnMain +import mozilla.telemetry.glean.private.NoExtras import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals @@ -96,9 +98,6 @@ class DefaultTabManagerControllerTest { private lateinit var profiler: Profiler @MockK(relaxed = true) - private lateinit var navigationInteractor: NavigationInteractor - - @MockK(relaxed = true) private lateinit var tabsUseCases: TabsUseCases @MockK(relaxed = true) @@ -107,6 +106,9 @@ class DefaultTabManagerControllerTest { @MockK(relaxed = true) private lateinit var activity: HomeActivity + @MockK(relaxed = true) + private lateinit var accountManager: FxaAccountManager + private val appStore: AppStore = mockk(relaxed = true) private val settings: Settings = mockk(relaxed = true) @@ -143,7 +145,8 @@ class DefaultTabManagerControllerTest { assertNull(TabsTray.newPrivateTabTapped.testGetValue()) - createController().handlePrivateTabsFabClick() + val target = createController() + target.handlePrivateTabsFabClick() assertNotNull(TabsTray.newPrivateTabTapped.testGetValue()) @@ -153,7 +156,7 @@ class DefaultTabManagerControllerTest { navController.navigate( TabManagementFragmentDirections.actionGlobalHome(focusOnAddressBar = true), ) - navigationInteractor.onTabManagerDismissed() + TabsTray.closed.record(NoExtras()) profiler.addMarker( "DefaultTabManagerController.onNewTabTapped", Double.MAX_VALUE, @@ -171,7 +174,8 @@ class DefaultTabManagerControllerTest { assertNull(TabsTray.newPrivateTabTapped.testGetValue()) - createController().handlePrivateTabsFabClick() + val target = createController() + target.handlePrivateTabsFabClick() assertNotNull(TabsTray.newPrivateTabTapped.testGetValue()) @@ -180,7 +184,7 @@ class DefaultTabManagerControllerTest { fenixBrowserUseCases.addNewHomepageTab( private = true, ) - navigationInteractor.onTabManagerDismissed() + TabsTray.closed.record(NoExtras()) profiler.addMarker( "DefaultTabManagerController.onNewTabTapped", Double.MAX_VALUE, @@ -194,7 +198,8 @@ class DefaultTabManagerControllerTest { every { getProfilerTime() } returns Double.MAX_VALUE } - createController().handleNormalTabsFabClick() + val target = createController() + target.handleNormalTabsFabClick() verifyOrder { profiler.getProfilerTime() @@ -202,7 +207,7 @@ class DefaultTabManagerControllerTest { navController.navigate( TabManagementFragmentDirections.actionGlobalHome(focusOnAddressBar = true), ) - navigationInteractor.onTabManagerDismissed() + TabsTray.closed.record(NoExtras()) profiler.addMarker( "DefaultTabManagerController.onNewTabTapped", Double.MAX_VALUE, @@ -218,14 +223,15 @@ class DefaultTabManagerControllerTest { every { getProfilerTime() } returns Double.MAX_VALUE } - createController().handleNormalTabsFabClick() + val target = createController() + target.handleNormalTabsFabClick() verifyOrder { profiler.getProfilerTime() fenixBrowserUseCases.addNewHomepageTab( private = false, ) - navigationInteractor.onTabManagerDismissed() + TabsTray.closed.record(NoExtras()) profiler.addMarker( "DefaultTabManagerController.onNewTabTapped", Double.MAX_VALUE, @@ -1354,6 +1360,158 @@ class DefaultTabManagerControllerTest { } } + @Test + fun `GIVEN logged in state WHEN account settings is clicked THEN navigate to account settings`() { + every { accountManager.authenticatedAccount() }.answers { mockk(relaxed = true) } + + createController().onAccountSettingsClicked() + + verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalAccountSettingsFragment()) } + } + + @Test + fun `GIVEN logged out state WHEN account settings is clicked THEN navigate to turn on sync`() { + every { accountManager.authenticatedAccount() }.answers { null } + + createController().onAccountSettingsClicked() + + verify(exactly = 1) { + navController.navigate( + TabManagementFragmentDirections.actionGlobalTurnOnSync( + entrypoint = FenixFxAEntryPoint.NavigationInteraction, + ), + ) + } + } + + @Test + fun `WHEN tab settings is clicked THEN navigate to global tab settings`() { + createController().onTabSettingsClicked() + verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalTabSettingsFragment()) } + } + + @Test + fun `GIVEN no open recently closed tabs WHEN open recently closed tabs clicked THEN navigate to recently closed tabs`() { + assertNull(Events.recentlyClosedTabsOpened.testGetValue()) + + createController().onOpenRecentlyClosedClicked() + + verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalRecentlyClosed()) } + assertNotNull(Events.recentlyClosedTabsOpened.testGetValue()) + } + + @Test + fun `GIVEN public tabs and one download in progress WHEN close all tabs clicked THEN dismiss tab manager and navigate to home`() { + val tab: TabSessionState = mockk { every { content.private } returns false } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns mapOf( + "1" to DownloadState( + "https://mozilla.org/download", + private = false, + destinationDirectory = "Download", + status = DownloadState.Status.DOWNLOADING, + ), + ) + + val controller = spyk(createController()) + controller.onCloseAllTabsClicked(private = false) + + verify { controller.dismissTabManagerAndNavigateHome(any()) } + } + + @Test + fun `GIVEN private tabs and 1 download in progress WHEN close all tabs clicked THEN dismiss tab manager and navigate to home`() { + val tab: TabSessionState = mockk { every { content.private } returns true } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns mapOf( + "1" to DownloadState( + "https://mozilla.org/download", + private = true, + destinationDirectory = "Download", + status = DownloadState.Status.DOWNLOADING, + ), + ) + + val controller = spyk(createController()) + controller.onCloseAllTabsClicked(private = false) + + verify { controller.dismissTabManagerAndNavigateHome(any()) } + } + + @Test + fun `GIVEN active private download WHEN onCloseAllTabsClicked is called for private tabs THEN showCancelledDownloadWarning is called`() { + var showCancelledDownloadWarningInvoked = false + val tab: TabSessionState = mockk { every { content.private } returns true } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns mapOf( + "1" to DownloadState( + "https://mozilla.org/download", + private = true, + destinationDirectory = "Download", + status = DownloadState.Status.DOWNLOADING, + ), + ) + + createController(showCancelledDownloadWarning = { _, _, _ -> showCancelledDownloadWarningInvoked = true }).onCloseAllTabsClicked(true) + + assertTrue(showCancelledDownloadWarningInvoked) + } + + @Test + fun `GIVEN no active private download WHEN onCloseAllTabsClicked is called for private tabs THEN showCancelledDownloadWarning is not called`() { + var showCancelledDownloadWarningInvoked = false + val tab: TabSessionState = mockk { every { content.private } returns true } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns emptyMap() + + createController(showCancelledDownloadWarning = { _, _, _ -> showCancelledDownloadWarningInvoked = true }).onCloseAllTabsClicked(true) + + assertFalse(showCancelledDownloadWarningInvoked) + } + + @Test + fun `GIVEN no active download WHEN onCloseAllTabsClicked is called for public tabs THEN showCancelledDownloadWarning is not called`() { + var showCancelledDownloadWarningInvoked = false + val tab: TabSessionState = mockk { every { content.private } returns false } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns mapOf( + "1" to DownloadState( + "https://mozilla.org/download", + private = false, + destinationDirectory = "Download", + status = DownloadState.Status.DOWNLOADING, + ), + ) + + createController(showCancelledDownloadWarning = { _, _, _ -> showCancelledDownloadWarningInvoked = true }).onCloseAllTabsClicked(true) + + assertFalse(showCancelledDownloadWarningInvoked) + } + + @Test + fun `GIVEN active download WHEN onCloseAllTabsClicked is called for public tabs THEN showCancelledDownloadWarning is not called`() { + var showCancelledDownloadWarningInvoked = false + val tab: TabSessionState = mockk { every { content.private } returns false } + every { browserStore.state } returns mockk { + every { tabs } returns listOf(tab) + } + every { browserStore.state.downloads } returns emptyMap() + + createController(showCancelledDownloadWarning = { _, _, _ -> showCancelledDownloadWarningInvoked = true }).onCloseAllTabsClicked(true) + + assertFalse(showCancelledDownloadWarningInvoked) + } + private fun makeBookmarkFolder(guid: String) = BookmarkNode( type = BookmarkNodeType.FOLDER, parentGuid = BookmarkRoot.Mobile.id, @@ -1376,6 +1534,7 @@ class DefaultTabManagerControllerTest { showBookmarkSnackbar: (Int, String?) -> Unit = { _, _ -> }, ): DefaultTabManagerController { return DefaultTabManagerController( + accountManager = accountManager, activity = activity, appStore = appStore, tabsTrayStore = trayStore, @@ -1385,7 +1544,6 @@ class DefaultTabManagerControllerTest { navController = navController, navigateToHomeAndDeleteSession = navigateToHomeAndDeleteSession, profiler = profiler, - navigationInteractor = navigationInteractor, tabsUseCases = tabsUseCases, fenixBrowserUseCases = fenixBrowserUseCases, bookmarksStorage = bookmarksStorage, diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/NavigationInteractorTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/controller/NavigationInteractorTest.kt @@ -1,157 +0,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/. */ - -package org.mozilla.fenix.tabstray.controller - -import androidx.navigation.NavController -import io.mockk.every -import io.mockk.mockk -import io.mockk.spyk -import io.mockk.verify -import mozilla.components.browser.state.state.BrowserState -import mozilla.components.browser.state.state.TabSessionState -import mozilla.components.browser.state.state.content.DownloadState -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.service.fxa.manager.FxaAccountManager -import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.test.rule.MainCoroutineRule -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.runner.RunWith -import org.mozilla.fenix.GleanMetrics.Events -import org.mozilla.fenix.GleanMetrics.TabsTray -import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint -import org.mozilla.fenix.helpers.FenixGleanTestRule -import org.mozilla.fenix.tabstray.ui.TabManagementFragmentDirections -import org.robolectric.RobolectricTestRunner -import mozilla.components.browser.state.state.createTab as createStateTab - -@RunWith(RobolectricTestRunner::class) // for gleanTestRule -class NavigationInteractorTest { - private lateinit var store: BrowserStore - private val testTab: TabSessionState = createStateTab(url = "https://mozilla.org") - private val navController: NavController = mockk(relaxed = true) - private val accountManager: FxaAccountManager = mockk(relaxed = true) - - val coroutinesTestRule: MainCoroutineRule = MainCoroutineRule() - val gleanTestRule = FenixGleanTestRule(testContext) - - @get:Rule - val chain: RuleChain = RuleChain.outerRule(gleanTestRule).around(coroutinesTestRule) - - @Before - fun setup() { - store = BrowserStore(initialState = BrowserState(tabs = listOf(testTab))) - } - - @Test - fun `WHEN the tab manager is dismissed THEN the metric is reported`() { - assertNull(TabsTray.closed.testGetValue()) - - createInteractor().onTabManagerDismissed() - - assertNotNull(TabsTray.closed.testGetValue()) - } - - @Test - fun `onAccountSettingsClicked calls navigation on DefaultNavigationInteractor`() { - every { accountManager.authenticatedAccount() }.answers { mockk(relaxed = true) } - - createInteractor().onAccountSettingsClicked() - - verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalAccountSettingsFragment()) } - } - - @Test - fun `onAccountSettingsClicked when not logged in calls navigation to turn on sync`() { - every { accountManager.authenticatedAccount() }.answers { null } - - createInteractor().onAccountSettingsClicked() - - verify(exactly = 1) { - navController.navigate( - TabManagementFragmentDirections.actionGlobalTurnOnSync( - entrypoint = FenixFxAEntryPoint.NavigationInteraction, - ), - ) - } - } - - @Test - fun `onTabSettingsClicked calls navigation on DefaultNavigationInteractor`() { - createInteractor().onTabSettingsClicked() - verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalTabSettingsFragment()) } - } - - @Test - fun `onOpenRecentlyClosedClicked calls navigation on DefaultNavigationInteractor`() { - assertNull(Events.recentlyClosedTabsOpened.testGetValue()) - - createInteractor().onOpenRecentlyClosedClicked() - - verify(exactly = 1) { navController.navigate(TabManagementFragmentDirections.actionGlobalRecentlyClosed()) } - assertNotNull(Events.recentlyClosedTabsOpened.testGetValue()) - } - - @Test - fun `onCloseAllTabsClicked calls navigation on DefaultNavigationInteractor`() { - var dismissTabManagerAndNavigateHomeInvoked = false - createInteractor( - dismissTabManagerAndNavigateHome = { - dismissTabManagerAndNavigateHomeInvoked = true - }, - ).onCloseAllTabsClicked(false) - - assertTrue(dismissTabManagerAndNavigateHomeInvoked) - } - - @Test - fun `GIVEN active private download WHEN onCloseAllTabsClicked is called for private tabs THEN showCancelledDownloadWarning is called`() { - var showCancelledDownloadWarningInvoked = false - val mockedStore: BrowserStore = mockk() - val controller = spyk( - createInteractor( - browserStore = mockedStore, - showCancelledDownloadWarning = { _, _, _ -> - showCancelledDownloadWarningInvoked = true - }, - ), - ) - val tab: TabSessionState = mockk { every { content.private } returns true } - every { mockedStore.state } returns mockk { - every { tabs } returns listOf(tab) - } - every { mockedStore.state.downloads } returns mapOf( - "1" to DownloadState( - "https://mozilla.org/download", - private = true, - destinationDirectory = "Download", - status = DownloadState.Status.DOWNLOADING, - ), - ) - - controller.onCloseAllTabsClicked(true) - - assertTrue(showCancelledDownloadWarningInvoked) - } - - private fun createInteractor( - browserStore: BrowserStore = store, - dismissTabManagerAndNavigateHome: (String) -> Unit = { _ -> }, - showCancelledDownloadWarning: (Int, String?, String?) -> Unit = { _, _, _ -> }, - ): NavigationInteractor { - return DefaultNavigationInteractor( - browserStore = browserStore, - navController = navController, - dismissTabManagerAndNavigateHome = dismissTabManagerAndNavigateHome, - showCancelledDownloadWarning = showCancelledDownloadWarning, - accountManager = accountManager, - ) - } -}