/* 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.home import android.view.View import androidx.navigation.NavController import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.MainScope import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain import mozilla.components.browser.session.SessionManager import mozilla.components.concept.engine.Engine import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tabs.TabsUseCases import org.junit.After import org.junit.Before import org.junit.Test import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager import org.mozilla.fenix.components.TabCollectionStorage import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.ext.components import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController import org.mozilla.fenix.settings.SupportUtils import mozilla.components.feature.tab.collections.Tab as ComponentTab @ExperimentalCoroutinesApi class DefaultSessionControlControllerTest { private val mainThreadSurrogate = newSingleThreadContext("UI thread") private val activity: HomeActivity = mockk(relaxed = true) private val store: HomeFragmentStore = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true) private val browsingModeManager: BrowsingModeManager = mockk(relaxed = true) private val closeTab: (sessionId: String) -> Unit = mockk(relaxed = true) private val closeAllTabs: (isPrivateMode: Boolean) -> Unit = mockk(relaxed = true) private val getListOfTabs: () -> List = { emptyList() } private val hideOnboarding: () -> Unit = mockk(relaxed = true) private val openSettingsScreen: () -> Unit = mockk(relaxed = true) private val openSearchScreen: () -> Unit = mockk(relaxed = true) private val invokePendingDeleteJobs: () -> Unit = mockk(relaxed = true) private val registerCollectionStorageObserver: () -> Unit = mockk(relaxed = true) private val scrollToTheTop: () -> Unit = mockk(relaxed = true) private val showDeleteCollectionPrompt: (tabCollection: TabCollection) -> Unit = mockk(relaxed = true) private val metrics: MetricController = mockk(relaxed = true) private val state: HomeFragmentState = mockk(relaxed = true) private val sessionManager: SessionManager = mockk(relaxed = true) private val engine: Engine = mockk(relaxed = true) private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true) private val tabsUseCases: TabsUseCases = mockk(relaxed = true) private lateinit var controller: DefaultSessionControlController @Before fun setup() { Dispatchers.setMain(mainThreadSurrogate) mockkStatic("org.mozilla.fenix.ext.ContextKt") every { activity.components.core.engine } returns engine every { activity.components.core.sessionManager } returns sessionManager every { activity.components.core.tabCollectionStorage } returns tabCollectionStorage every { activity.components.useCases.tabsUseCases } returns tabsUseCases every { store.state } returns state every { state.collections } returns emptyList() every { state.expandedCollections } returns emptySet() every { state.mode } returns Mode.Normal every { state.tabs } returns emptyList() every { activity.components.analytics.metrics } returns metrics controller = DefaultSessionControlController( activity = activity, store = store, navController = navController, browsingModeManager = browsingModeManager, lifecycleScope = MainScope(), closeTab = closeTab, closeAllTabs = closeAllTabs, getListOfTabs = getListOfTabs, hideOnboarding = hideOnboarding, invokePendingDeleteJobs = invokePendingDeleteJobs, registerCollectionStorageObserver = registerCollectionStorageObserver, scrollToTheTop = scrollToTheTop, showDeleteCollectionPrompt = showDeleteCollectionPrompt, openSettingsScreen = openSettingsScreen, openSearchScreen = openSearchScreen ) } @After fun tearDown() { Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher mainThreadSurrogate.close() } @Test fun handleCloseTab() { val sessionId = "hello" controller.handleCloseTab(sessionId) verify { closeTab(sessionId) } } @Test fun handleCloseAllTabs() { val isPrivateMode = true controller.handleCloseAllTabs(isPrivateMode) verify { closeAllTabs(isPrivateMode) } } @Test fun handleCollectionAddTabTapped() { val collection: TabCollection = mockk(relaxed = true) controller.handleCollectionAddTabTapped(collection) verify { metrics.track(Event.CollectionAddTabPressed) } } @Test fun handleCollectionOpenTabClicked() { val tab: ComponentTab = mockk(relaxed = true) controller.handleCollectionOpenTabClicked(tab) verify { invokePendingDeleteJobs() } verify { metrics.track(Event.CollectionTabRestored) } } @Test fun handleCollectionOpenTabsTapped() { val collection: TabCollection = mockk(relaxed = true) controller.handleCollectionOpenTabsTapped(collection) verify { invokePendingDeleteJobs() } verify { scrollToTheTop() } verify { metrics.track(Event.CollectionAllTabsRestored) } } @Test fun handleCollectionRemoveTab() { val collection: TabCollection = mockk(relaxed = true) val tab: ComponentTab = mockk(relaxed = true) controller.handleCollectionRemoveTab(collection, tab) verify { metrics.track(Event.CollectionTabRemoved) } } @Test fun handleCollectionShareTabsClicked() { val collection: TabCollection = mockk(relaxed = true) controller.handleCollectionShareTabsClicked(collection) verify { metrics.track(Event.CollectionShared) } } @Test fun handleDeleteCollectionTapped() { val collection: TabCollection = mockk(relaxed = true) controller.handleDeleteCollectionTapped(collection) verify { showDeleteCollectionPrompt(collection) } } @Test fun handlePrivateBrowsingLearnMoreClicked() { controller.handlePrivateBrowsingLearnMoreClicked() verify { activity.openToBrowserAndLoad( searchTermOrURL = SupportUtils.getGenericSumoURLForTopic (SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS), newTab = true, from = BrowserDirection.FromHome ) } } @Test fun handleRenameCollectionTapped() { val collection: TabCollection = mockk(relaxed = true) controller.handleRenameCollectionTapped(collection) verify { metrics.track(Event.CollectionRenamePressed) } } @Test fun handleSaveTabToCollection() { controller.handleSaveTabToCollection(selectedTabId = null) verify { invokePendingDeleteJobs() } } @Test fun handleSelectTab() { val tabView: View = mockk(relaxed = true) val sessionId = "hello" controller.handleSelectTab(tabView, sessionId) verify { invokePendingDeleteJobs() } verify { activity.openToBrowser(BrowserDirection.FromHome) } } @Test fun handleSelectTopSite() { val topSiteUrl = "mozilla.org" controller.handleSelectTopSite(topSiteUrl) verify { invokePendingDeleteJobs() } verify { metrics.track(Event.TopSiteOpenInNewTab) } verify { tabsUseCases.addTab.invoke( topSiteUrl, selectTab = true, startLoading = true ) } verify { activity.openToBrowser(BrowserDirection.FromHome) } } @Test fun handleShareTabs() { controller.handleShareTabs() verify { invokePendingDeleteJobs() } } @Test fun handleStartBrowsingClicked() { controller.handleStartBrowsingClicked() verify { hideOnboarding() } } @Test fun handleOpenSettingsClicked() { controller.handleOpenSettingsClicked() verify { openSettingsScreen() } } @Test fun handleToggleCollectionExpanded() { val collection: TabCollection = mockk(relaxed = true) controller.handleToggleCollectionExpanded(collection, true) verify { store.dispatch(HomeFragmentAction.CollectionExpanded(collection, true)) } } }