For #25491 - Refactor InactiveTabs interactors and controllers

pull/543/head
Noah Bond 2 years ago committed by mergify[bot]
parent 717e95a2d0
commit 5f7fd5313c

@ -21,9 +21,8 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.ext.DEFAULT_ACTIVE_DAYS
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.tabstray.ext.isActiveDownload
import java.util.concurrent.TimeUnit
@ -98,11 +97,6 @@ interface TabsTrayController {
tabs: Collection<TabSessionState>,
numOfDays: Long = DEFAULT_ACTIVE_DAYS + 1
)
/**
* Deletes all inactive tabs.
*/
fun handleDeleteAllInactiveTabs()
}
@Suppress("TooManyFunctions")
@ -274,12 +268,4 @@ class DefaultTabsTrayController(
dismissTray()
navigateToHomeAndDeleteSession(sessionId)
}
override fun handleDeleteAllInactiveTabs() {
TabsTray.closeAllInactiveTabs.record(NoExtras())
browserStore.state.potentialInactiveTabs.map { it.id }.let {
tabsUseCases.removeTabs(it)
}
showUndoSnackbarForTab(false)
}
}

@ -51,6 +51,9 @@ import org.mozilla.fenix.home.HomeScreenViewModel
import org.mozilla.fenix.share.ShareFragment
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
import org.mozilla.fenix.tabstray.browser.DefaultBrowserTrayInteractor
import org.mozilla.fenix.tabstray.browser.DefaultInactiveTabsController
import org.mozilla.fenix.tabstray.browser.DefaultInactiveTabsInteractor
import org.mozilla.fenix.tabstray.browser.InactiveTabsInteractor
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding.VisibilityModifier
import org.mozilla.fenix.tabstray.browser.SelectionHandleBinding
@ -80,6 +83,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
private lateinit var browserTrayInteractor: BrowserTrayInteractor
private lateinit var tabsTrayInteractor: TabsTrayInteractor
private lateinit var tabsTrayController: DefaultTabsTrayController
private lateinit var inactiveTabsInteractor: DefaultInactiveTabsInteractor
private lateinit var navigationInteractor: DefaultNavigationInteractor
@VisibleForTesting internal lateinit var trayBehaviorManager: TabSheetBehaviorManager
@ -227,6 +231,17 @@ class TabsTrayFragment : AppCompatDialogFragment() {
requireComponents.useCases.tabsUseCases.selectTab,
)
inactiveTabsInteractor = DefaultInactiveTabsInteractor(
controller = DefaultInactiveTabsController(
appStore = requireComponents.appStore,
settings = requireContext().settings(),
browserStore = requireComponents.core.store,
tabsUseCases = requireComponents.useCases.tabsUseCases,
showUndoSnackbar = ::showUndoSnackbarForTab,
),
browserInteractor = browserTrayInteractor,
)
setupMenu(navigationInteractor)
setupPager(
context = view.context,
@ -235,6 +250,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
trayInteractor = tabsTrayInteractor,
browserInteractor = browserTrayInteractor,
navigationInteractor = navigationInteractor,
inactiveTabsInteractor = inactiveTabsInteractor,
)
setupBackgroundDismissalListener {
@ -476,7 +492,8 @@ class TabsTrayFragment : AppCompatDialogFragment() {
store: TabsTrayStore,
trayInteractor: TabsTrayInteractor,
browserInteractor: BrowserTrayInteractor,
navigationInteractor: NavigationInteractor
navigationInteractor: NavigationInteractor,
inactiveTabsInteractor: InactiveTabsInteractor
) {
tabsTrayBinding.tabsTray.apply {
adapter = TrayPagerAdapter(
@ -488,6 +505,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
tabsTrayInteractor = trayInteractor,
browserStore = requireComponents.core.store,
appStore = requireComponents.appStore,
inactiveTabsInteractor = inactiveTabsInteractor,
)
isUserInputEnabled = false
}

@ -50,11 +50,6 @@ interface TabsTrayInteractor {
targetId: String?,
placeAfter: Boolean
)
/**
* Deletes all inactive tabs.
*/
fun onDeleteInactiveTabs()
}
/**
@ -96,8 +91,4 @@ class DefaultTabsTrayInteractor(
override fun onInactiveDebugClicked(tabs: Collection<TabSessionState>) {
controller.forceTabsAsInactive(tabs)
}
override fun onDeleteInactiveTabs() {
controller.handleDeleteAllInactiveTabs()
}
}

@ -14,13 +14,10 @@ import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.browser.state.store.BrowserStore
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.tabstray.browser.BrowserTabsAdapter
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
import org.mozilla.fenix.tabstray.browser.DefaultInactiveTabsInteractor
import org.mozilla.fenix.tabstray.browser.InactiveTabsAdapter
import org.mozilla.fenix.tabstray.browser.InactiveTabsController
import org.mozilla.fenix.tabstray.browser.InactiveTabsInteractor
import org.mozilla.fenix.tabstray.browser.TabGroupAdapter
import org.mozilla.fenix.tabstray.browser.TitleHeaderAdapter
import org.mozilla.fenix.tabstray.viewholders.AbstractPageViewHolder
@ -37,7 +34,8 @@ class TrayPagerAdapter(
@VisibleForTesting internal val navInteractor: NavigationInteractor,
@VisibleForTesting internal val tabsTrayInteractor: TabsTrayInteractor,
@VisibleForTesting internal val browserStore: BrowserStore,
@VisibleForTesting internal val appStore: AppStore
@VisibleForTesting internal val appStore: AppStore,
@VisibleForTesting internal val inactiveTabsInteractor: InactiveTabsInteractor,
) : RecyclerView.Adapter<AbstractPageViewHolder>() {
/**
@ -50,7 +48,6 @@ class TrayPagerAdapter(
InactiveTabsAdapter(
lifecycleOwner = lifecycleOwner,
tabsTrayStore = tabsTrayStore,
tabsTrayInteractor = tabsTrayInteractor,
inactiveTabsInteractor = inactiveTabsInteractor,
featureName = INACTIVE_TABS_FEATURE_NAME,
),
@ -60,16 +57,6 @@ class TrayPagerAdapter(
)
}
private val inactiveTabsInteractor by lazy {
DefaultInactiveTabsInteractor(
InactiveTabsController(
appStore = context.components.appStore,
settings = context.settings(),
browserInteractor = browserInteractor,
)
)
}
private val privateAdapter by lazy {
BrowserTabsAdapter(
context,

@ -41,7 +41,6 @@ class InactiveTabViewHolder(
composeView: ComposeView,
lifecycleOwner: LifecycleOwner,
private val tabsTrayStore: TabsTrayStore,
private val tabsTrayInteractor: TabsTrayInteractor,
private val inactiveTabsInteractor: InactiveTabsInteractor,
) : ComposeViewHolder(composeView, lifecycleOwner) {
@ -65,7 +64,7 @@ class InactiveTabViewHolder(
expanded = expanded,
showAutoCloseDialog = showAutoClosePrompt,
onHeaderClick = { inactiveTabsInteractor.onHeaderClicked(!expanded) },
onDeleteAllButtonClick = tabsTrayInteractor::onDeleteInactiveTabs,
onDeleteAllButtonClick = inactiveTabsInteractor::onDeleteAllInactiveTabsClicked,
onAutoCloseDismissClick = {
inactiveTabsInteractor.onCloseClicked()
showAutoClosePrompt = !showAutoClosePrompt

@ -8,7 +8,6 @@ import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.tabstray.TabsTrayInteractor
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
@ -17,7 +16,6 @@ import org.mozilla.fenix.tabstray.TabsTrayStore
*
* @param lifecycleOwner [LifecycleOwner] to which the Composable will be tied to.
* @param tabsTrayStore [TabsTrayStore] used to listen for changes to [TabsTrayState.inactiveTabs].
* @param tabsTrayInteractor [TabsTrayInteractor] used to handle deleting all inactive tabs.
* @param inactiveTabsInteractor [InactiveTabsInteractor] used to respond to interactions with the inactive tabs header
* and the auto close dialog.
* @param featureName [String] representing the name of the inactive tabs feature for telemetry reporting.
@ -26,7 +24,6 @@ import org.mozilla.fenix.tabstray.TabsTrayStore
class InactiveTabsAdapter(
private val lifecycleOwner: LifecycleOwner,
private val tabsTrayStore: TabsTrayStore,
private val tabsTrayInteractor: TabsTrayInteractor,
private val inactiveTabsInteractor: InactiveTabsInteractor,
override val featureName: String,
) : RecyclerView.Adapter<InactiveTabViewHolder>(), FeatureNameHolder {
@ -39,7 +36,6 @@ class InactiveTabsAdapter(
lifecycleOwner = lifecycleOwner,
tabsTrayStore = tabsTrayStore,
inactiveTabsInteractor = inactiveTabsInteractor,
tabsTrayInteractor = tabsTrayInteractor,
)
}

@ -5,48 +5,79 @@
package org.mozilla.fenix.tabstray.browser
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppAction.UpdateInactiveExpanded
import org.mozilla.fenix.tabstray.TrayPagerAdapter.Companion.INACTIVE_TABS_FEATURE_NAME
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.GleanMetrics.TabsTray as TabsTrayMetrics
/**
* Default behavior for handling all user interactions with the Inactive Tabs feature.
*
* @param appStore [AppStore] used to dispatch any [AppAction].
* @param settings [Settings] used to update any user preferences.
* @param browserInteractor [BrowserTrayInteractor] used to respond to interactions with specific inactive tabs.
* Contract for how all user interactions with the Inactive Tabs feature are to be handled.
*/
class InactiveTabsController(
private val appStore: AppStore,
private val settings: Settings,
private val browserInteractor: BrowserTrayInteractor,
) {
interface InactiveTabsController {
/**
* Opens the given inactive tab.
*/
fun openInactiveTab(tab: TabSessionState) {
TabsTrayMetrics.openInactiveTab.add()
browserInteractor.onTabSelected(tab, INACTIVE_TABS_FEATURE_NAME)
}
fun openInactiveTab(tab: TabSessionState)
/**
* Closes the given inactive tab.
*/
fun closeInactiveTab(tab: TabSessionState) {
TabsTrayMetrics.closeInactiveTab.add()
browserInteractor.onTabClosed(tab, INACTIVE_TABS_FEATURE_NAME)
}
fun closeInactiveTab(tab: TabSessionState)
/**
* Updates the inactive card to be expanded to display all the tabs, or collapsed with only
* the title showing.
*/
fun updateCardExpansion(isExpanded: Boolean) {
fun updateCardExpansion(isExpanded: Boolean)
/**
* Dismiss the auto-close dialog.
*/
fun dismissAutoCloseDialog()
/**
* Enable the auto-close feature with the "after a month" setting.
*/
fun enableInactiveTabsAutoClose()
/**
* Delete all inactive tabs.
*/
fun deleteAllInactiveTabs()
}
/**
* Default behavior for handling all user interactions with the Inactive Tabs feature.
*
* @param appStore [AppStore] used to dispatch any [AppAction].
* @param settings [Settings] used to update any user preferences.
* @param browserStore [BrowserStore] used to obtain all inactive tabs.
* @param tabsUseCases [TabsUseCases] used to perform the deletion of all inactive tabs.
* @param showUndoSnackbar Invoked when deleting all inactive tabs.
*/
class DefaultInactiveTabsController(
private val appStore: AppStore,
private val settings: Settings,
private val browserStore: BrowserStore,
private val tabsUseCases: TabsUseCases,
private val showUndoSnackbar: (Boolean) -> Unit,
) : InactiveTabsController {
override fun openInactiveTab(tab: TabSessionState) {
TabsTrayMetrics.openInactiveTab.add()
}
override fun closeInactiveTab(tab: TabSessionState) {
TabsTrayMetrics.closeInactiveTab.add()
}
override fun updateCardExpansion(isExpanded: Boolean) {
appStore.dispatch(UpdateInactiveExpanded(isExpanded))
when (isExpanded) {
@ -55,18 +86,12 @@ class InactiveTabsController(
}
}
/**
* Dismiss the auto-close dialog.
*/
fun close() {
override fun dismissAutoCloseDialog() {
markDialogAsShown()
TabsTrayMetrics.autoCloseDimissed.record(NoExtras())
}
/**
* Enable the auto-close feature with the after a month setting.
*/
fun enableAutoClosed() {
override fun enableInactiveTabsAutoClose() {
markDialogAsShown()
settings.closeTabsAfterOneMonth = true
settings.closeTabsAfterOneWeek = false
@ -75,6 +100,14 @@ class InactiveTabsController(
TabsTrayMetrics.autoCloseTurnOnClicked.record(NoExtras())
}
override fun deleteAllInactiveTabs() {
TabsTrayMetrics.closeAllInactiveTabs.record(NoExtras())
browserStore.state.potentialInactiveTabs.map { it.id }.let {
tabsUseCases.removeTabs(it)
}
showUndoSnackbar(false)
}
/**
* Marks the dialog as shown and to not be displayed again.
*/

@ -5,6 +5,7 @@
package org.mozilla.fenix.tabstray.browser
import mozilla.components.browser.state.state.TabSessionState
import org.mozilla.fenix.tabstray.TrayPagerAdapter
/**
* Interactor for all things related to inactive tabs in the tabs tray.
@ -30,6 +31,11 @@ interface InactiveTabsInteractor : InactiveTabsAutoCloseDialogInteractor {
* @param tab [TabSessionState] that was closed.
*/
fun onTabClosed(tab: TabSessionState)
/**
* Invoked when the user clicks on the delete all inactive tabs button.
*/
fun onDeleteAllInactiveTabsClicked()
}
/**
@ -48,8 +54,15 @@ interface InactiveTabsAutoCloseDialogInteractor {
fun onEnabledAutoCloseClicked()
}
/**
* Interactor to be called for any user interactions with the Inactive Tabs feature.
*
* @param controller [InactiveTabsController] todo.
* @param browserInteractor [BrowserTrayInteractor] used to respond to interactions with specific inactive tabs.
*/
class DefaultInactiveTabsInteractor(
private val controller: InactiveTabsController
private val controller: InactiveTabsController,
private val browserInteractor: BrowserTrayInteractor,
) : InactiveTabsInteractor {
/**
@ -63,14 +76,14 @@ class DefaultInactiveTabsInteractor(
* See [InactiveTabsAutoCloseDialogInteractor.onCloseClicked].
*/
override fun onCloseClicked() {
controller.close()
controller.dismissAutoCloseDialog()
}
/**
* See [InactiveTabsAutoCloseDialogInteractor.onEnabledAutoCloseClicked].
*/
override fun onEnabledAutoCloseClicked() {
controller.enableAutoClosed()
controller.enableInactiveTabsAutoClose()
}
/**
@ -78,6 +91,7 @@ class DefaultInactiveTabsInteractor(
*/
override fun onTabClicked(tab: TabSessionState) {
controller.openInactiveTab(tab)
browserInteractor.onTabSelected(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME)
}
/**
@ -85,5 +99,13 @@ class DefaultInactiveTabsInteractor(
*/
override fun onTabClosed(tab: TabSessionState) {
controller.closeInactiveTab(tab)
browserInteractor.onTabClosed(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME)
}
/**
* See [InactiveTabsInteractor.onDeleteAllInactiveTabsClicked].
*/
override fun onDeleteAllInactiveTabsClicked() {
controller.deleteAllInactiveTabs()
}
}

@ -38,10 +38,8 @@ import org.junit.runner.RunWith
import org.mozilla.fenix.GleanMetrics.TabsTray
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.ext.maxActiveTime
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.HomeFragment
@RunWith(FenixRobolectricTestRunner::class) // for gleanTestRule
class DefaultTabsTrayControllerTest {
@ -477,63 +475,6 @@ class DefaultTabsTrayControllerTest {
assertTrue(navigateToHomeAndDeleteSessionInvoked)
}
@Test
fun `WHEN deleteAllInactiveTabs is called THEN that it uses tabsUseCases#removeTabs and shows an undo snackbar`() {
var showUndoSnackbarForTabInvoked = false
val controller = spyk(
createController(
showUndoSnackbarForTab = {
showUndoSnackbarForTabInvoked = true
}
)
)
val inactiveTab: TabSessionState = mockk {
every { lastAccess } returns maxActiveTime
every { createdAt } returns 0
every { id } returns "24"
every { content } returns mockk {
every { private } returns false
}
}
every { browserStore.state } returns mockk()
try {
mockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
every { browserStore.state.potentialInactiveTabs } returns listOf(inactiveTab)
controller.handleDeleteAllInactiveTabs()
verify { tabsUseCases.removeTabs(listOf("24")) }
assertTrue(showUndoSnackbarForTabInvoked)
} finally {
unmockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
}
}
@Test
fun `WHEN handleDeleteAllInactiveTabs is called THEN Event#TabsTrayCloseAllInactiveTabs and Event#TabsTrayCloseInactiveTab are added to telemetry`() {
val inactiveTab: TabSessionState = mockk {
every { lastAccess } returns maxActiveTime
every { createdAt } returns 0
every { id } returns "24"
every { content } returns mockk {
every { private } returns false
}
}
every { browserStore.state } returns mockk()
assertNull(TabsTray.closeAllInactiveTabs.testGetValue())
try {
mockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
every { browserStore.state.potentialInactiveTabs } returns listOf(inactiveTab)
createController().handleDeleteAllInactiveTabs()
assertNotNull(TabsTray.closeAllInactiveTabs.testGetValue())
} finally {
unmockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
}
}
private fun createController(
navigateToHomeAndDeleteSession: (String) -> Unit = { },
selectTabPosition: (Int, Boolean) -> Unit = { _, _ -> },

@ -10,6 +10,7 @@ import mozilla.components.browser.state.state.TabSessionState
import org.junit.Test
class DefaultTabsTrayInteractorTest {
val controller: TabsTrayController = mockk(relaxed = true)
val trayInteractor = DefaultTabsTrayInteractor(controller)
@ -49,11 +50,4 @@ class DefaultTabsTrayInteractorTest {
verifySequence { controller.handleMultipleTabsDeletion(tabsToDelete) }
}
@Test
fun `GIVEN user selecting delete all inactive tabs WHEN onDeleteTabs is called THEN the Interactor delegates the controller`() {
trayInteractor.onDeleteInactiveTabs()
verifySequence { controller.handleDeleteAllInactiveTabs() }
}
}

@ -54,6 +54,7 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.helpers.MockkRetryTestRule
import org.mozilla.fenix.home.HomeScreenViewModel
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
import org.mozilla.fenix.tabstray.browser.InactiveTabsInteractor
import org.mozilla.fenix.tabstray.ext.showWithTheme
import org.mozilla.fenix.utils.allowUndo
@ -218,6 +219,7 @@ class TabsTrayFragmentTest {
val trayInteractor: TabsTrayInteractor = mockk()
val browserInteractor: BrowserTrayInteractor = mockk()
val navigationInteractor: NavigationInteractor = mockk()
val inactiveTabsInteractor: InactiveTabsInteractor = mockk()
val browserStore: BrowserStore = mockk()
every { context.components.core.store } returns browserStore
@ -228,6 +230,7 @@ class TabsTrayFragmentTest {
trayInteractor = trayInteractor,
browserInteractor = browserInteractor,
navigationInteractor = navigationInteractor,
inactiveTabsInteractor = inactiveTabsInteractor,
)
val adapter = (tabsTrayBinding.tabsTray.adapter as TrayPagerAdapter)

@ -4,55 +4,63 @@
package org.mozilla.fenix.tabstray.browser
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.unmockkStatic
import io.mockk.verify
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.components.service.glean.testing.GleanTestRule
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.ext.maxActiveTime
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.tabstray.TrayPagerAdapter
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.GleanMetrics.TabsTray as TabsTrayMetrics
@RunWith(FenixRobolectricTestRunner::class)
class InactiveTabsControllerTest {
class DefaultInactiveTabsControllerTest {
private val appStore: AppStore = mockk(relaxed = true)
private val settings: Settings = mockk(relaxed = true)
private val browserInteractor: BrowserTrayInteractor = mockk(relaxed = true)
private val appStore = AppStore()
private val browserStore: BrowserStore = mockk(relaxed = true)
private val tabsUseCases: TabsUseCases = mockk(relaxed = true)
@get:Rule
val gleanTestRule = GleanTestRule(testContext)
@Test
fun `WHEN the inactive tabs section is expanded THEN the expanded telemetry event should be report`() {
val controller = InactiveTabsController(appStore, settings, browserInteractor)
fun `WHEN the inactive tabs section is expanded THEN the expanded telemetry event should be reported`() {
val controller = createController()
assertNull(TabsTrayMetrics.inactiveTabsExpanded.testGetValue())
assertNull(TabsTrayMetrics.inactiveTabsCollapsed.testGetValue())
controller.updateCardExpansion(true)
controller.updateCardExpansion(isExpanded = true)
assertNotNull(TabsTrayMetrics.inactiveTabsExpanded.testGetValue())
assertNull(TabsTrayMetrics.inactiveTabsCollapsed.testGetValue())
}
@Test
fun `WHEN the inactive tabs section is collapsed THEN the collapsed telemetry event should be report`() {
val controller = InactiveTabsController(appStore, settings, browserInteractor)
fun `WHEN the inactive tabs section is collapsed THEN the collapsed telemetry event should be reported`() {
val controller = createController()
assertNull(TabsTrayMetrics.inactiveTabsExpanded.testGetValue())
assertNull(TabsTrayMetrics.inactiveTabsCollapsed.testGetValue())
controller.updateCardExpansion(false)
controller.updateCardExpansion(isExpanded = false)
assertNull(TabsTrayMetrics.inactiveTabsExpanded.testGetValue())
assertNotNull(TabsTrayMetrics.inactiveTabsCollapsed.testGetValue())
@ -60,11 +68,11 @@ class InactiveTabsControllerTest {
@Test
fun `WHEN the inactive tabs auto-close feature prompt is dismissed THEN update settings and report the telemetry event`() {
val controller = spyk(InactiveTabsController(appStore, settings, browserInteractor))
val controller = spyk(createController())
assertNull(TabsTrayMetrics.autoCloseDimissed.testGetValue())
controller.close()
controller.dismissAutoCloseDialog()
assertNotNull(TabsTrayMetrics.autoCloseDimissed.testGetValue())
verify { settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true }
@ -72,11 +80,11 @@ class InactiveTabsControllerTest {
@Test
fun `WHEN the inactive tabs auto-close feature prompt is accepted THEN update settings and report the telemetry event`() {
val controller = spyk(InactiveTabsController(appStore, settings, browserInteractor))
val controller = spyk(createController())
assertNull(TabsTrayMetrics.autoCloseTurnOnClicked.testGetValue())
controller.enableAutoClosed()
controller.enableInactiveTabsAutoClose()
assertNotNull(TabsTrayMetrics.autoCloseTurnOnClicked.testGetValue())
@ -88,8 +96,8 @@ class InactiveTabsControllerTest {
}
@Test
fun `WHEN an inactive tab is selected THEN the open the tab and report the telemetry event`() {
val controller = InactiveTabsController(appStore, settings, browserInteractor)
fun `WHEN an inactive tab is selected THEN report the telemetry event`() {
val controller = createController()
val tab = TabSessionState(
id = "tabId",
content = ContentState(
@ -101,14 +109,12 @@ class InactiveTabsControllerTest {
controller.openInactiveTab(tab)
verify { browserInteractor.onTabSelected(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME) }
assertNotNull(TabsTrayMetrics.openInactiveTab.testGetValue())
}
@Test
fun `WHEN an inactive tab is closed THEN the close the tab and report the telemetry event`() {
val controller = InactiveTabsController(appStore, settings, browserInteractor)
fun `WHEN an inactive tab is closed THEN report the telemetry event`() {
val controller = createController()
val tab = TabSessionState(
id = "tabId",
content = ContentState(
@ -116,12 +122,55 @@ class InactiveTabsControllerTest {
)
)
assertNull(TabsTrayMetrics.openInactiveTab.testGetValue())
assertNull(TabsTrayMetrics.closeInactiveTab.testGetValue())
controller.openInactiveTab(tab)
controller.closeInactiveTab(tab)
verify { browserInteractor.onTabSelected(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME) }
assertNotNull(TabsTrayMetrics.closeInactiveTab.testGetValue())
}
assertNotNull(TabsTrayMetrics.openInactiveTab.testGetValue())
@Test
fun `WHEN all inactive tabs are closed THEN perform the deletion and report the telemetry event and show a Snackbar`() {
var showSnackbarInvoked = false
val controller = createController(
showUndoSnackbar = {
showSnackbarInvoked = true
}
)
val inactiveTab: TabSessionState = mockk {
every { lastAccess } returns maxActiveTime
every { createdAt } returns 0
every { id } returns "24"
every { content } returns mockk {
every { private } returns false
}
}
try {
mockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
every { browserStore.state } returns mockk()
every { browserStore.state.potentialInactiveTabs } returns listOf(inactiveTab)
assertNull(TabsTrayMetrics.closeAllInactiveTabs.testGetValue())
controller.deleteAllInactiveTabs()
verify { tabsUseCases.removeTabs(listOf("24")) }
assertNotNull(TabsTrayMetrics.closeAllInactiveTabs.testGetValue())
assertTrue(showSnackbarInvoked)
} finally {
unmockkStatic("mozilla.components.browser.state.selector.SelectorsKt")
}
}
private fun createController(
showUndoSnackbar: (Boolean) -> Unit = { _ -> },
): DefaultInactiveTabsController {
return DefaultInactiveTabsController(
appStore = appStore,
settings = settings,
browserStore = browserStore,
tabsUseCases = tabsUseCases,
showUndoSnackbar = showUndoSnackbar,
)
}
}

@ -9,43 +9,36 @@ import io.mockk.verify
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import org.junit.Test
import org.mozilla.fenix.tabstray.TrayPagerAdapter
class DefaultInactiveTabsInteractorTest {
@Test
fun `WHEN onHeaderClicked THEN updateCardExpansion`() {
val controller: InactiveTabsController = mockk(relaxed = true)
val interactor = DefaultInactiveTabsInteractor(controller)
private val controller: InactiveTabsController = mockk(relaxed = true)
private val browserInteractor: BrowserTrayInteractor = mockk(relaxed = true)
interactor.onHeaderClicked(true)
@Test
fun `WHEN the inactive tabs header is clicked THEN update the expansion state of the inactive tabs card`() {
createInteractor().onHeaderClicked(true)
verify { controller.updateCardExpansion(true) }
}
@Test
fun `WHEN onCloseClicked THEN close`() {
val controller: InactiveTabsController = mockk(relaxed = true)
val interactor = DefaultInactiveTabsInteractor(controller)
interactor.onCloseClicked()
fun `WHEN the inactive tabs auto close dialog's close button is clicked THEN dismiss the dialog`() {
createInteractor().onCloseClicked()
verify { controller.close() }
verify { controller.dismissAutoCloseDialog() }
}
@Test
fun `WHEN onEnabledAutoCloseClicked THEN enableAutoClosed`() {
val controller: InactiveTabsController = mockk(relaxed = true)
val interactor = DefaultInactiveTabsInteractor(controller)
fun `WHEN the enable inactive tabs auto close button is clicked THEN turn on the auto close feature`() {
createInteractor().onEnabledAutoCloseClicked()
interactor.onEnabledAutoCloseClicked()
verify { controller.enableAutoClosed() }
verify { controller.enableInactiveTabsAutoClose() }
}
@Test
fun `WHEN an inactive tab is clicked THEN open the tab`() {
val controller: InactiveTabsController = mockk(relaxed = true)
val interactor = DefaultInactiveTabsInteractor(controller)
val tab = TabSessionState(
id = "tabId",
content = ContentState(
@ -53,15 +46,14 @@ class DefaultInactiveTabsInteractorTest {
)
)
interactor.onTabClicked(tab)
createInteractor().onTabClicked(tab)
verify { controller.openInactiveTab(tab) }
verify { browserInteractor.onTabSelected(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME) }
}
@Test
fun `WHEN an inactive tab is clicked to be closed THEN close the tab`() {
val controller: InactiveTabsController = mockk(relaxed = true)
val interactor = DefaultInactiveTabsInteractor(controller)
val tab = TabSessionState(
id = "tabId",
content = ContentState(
@ -69,8 +61,23 @@ class DefaultInactiveTabsInteractorTest {
)
)
interactor.onTabClosed(tab)
createInteractor().onTabClosed(tab)
verify { controller.closeInactiveTab(tab) }
verify { browserInteractor.onTabClosed(tab, TrayPagerAdapter.INACTIVE_TABS_FEATURE_NAME) }
}
@Test
fun `WHEN the close all inactive tabs button is clicked THEN delete all inactive tabs`() {
createInteractor().onDeleteAllInactiveTabsClicked()
verify { controller.deleteAllInactiveTabs() }
}
private fun createInteractor(): DefaultInactiveTabsInteractor {
return DefaultInactiveTabsInteractor(
controller = controller,
browserInteractor = browserInteractor,
)
}
}

Loading…
Cancel
Save