From 652af79e9597d5990a79e4adcd873a7cf94532a9 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Thu, 23 Jul 2020 12:27:39 -0700 Subject: [PATCH] Migrate from Session.toTab to BrowserStore (#12221) --- .../collections/CollectionCreationFragment.kt | 41 ++++++--- .../org/mozilla/fenix/ext/BrowserState.kt | 16 ++++ .../java/org/mozilla/fenix/ext/Session.kt | 43 --------- .../org/mozilla/fenix/home/HomeFragment.kt | 22 ++--- .../fenix/tabtray/TabTrayViewHolder.kt | 33 ++++--- .../CollectionCreationFragmentTest.kt | 78 ++++++++++------ .../DefaultBrowserToolbarControllerTest.kt | 7 -- .../org/mozilla/fenix/ext/BrowserStateTest.kt | 37 ++++++++ .../java/org/mozilla/fenix/ext/SessionTest.kt | 45 --------- .../DefaultSessionControlControllerTest.kt | 2 + .../fenix/tabtray/TabTrayViewHolderTest.kt | 92 +++++++++++++++++-- 11 files changed, 241 insertions(+), 175 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/ext/Session.kt create mode 100644 app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt delete mode 100644 app/src/test/java/org/mozilla/fenix/ext/SessionTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt index 2149d6bc3..264426c27 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt @@ -17,14 +17,16 @@ import kotlinx.android.synthetic.main.fragment_create_collection.view.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.plus -import mozilla.components.browser.session.SessionManager -import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider +import org.mozilla.fenix.ext.getMediaStateForSession import org.mozilla.fenix.ext.requireComponents -import org.mozilla.fenix.ext.toTab +import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.Tab @ExperimentalCoroutinesApi @@ -47,12 +49,11 @@ class CollectionCreationFragment : DialogFragment() { val view = inflater.inflate(R.layout.fragment_create_collection, container, false) val args: CollectionCreationFragmentArgs by navArgs() - val sessionManager = requireComponents.core.sessionManager val store = requireComponents.core.store val publicSuffixList = requireComponents.publicSuffixList - val tabs = sessionManager.getTabs(args.tabIds, store, publicSuffixList) + val tabs = store.state.getTabs(args.tabIds, publicSuffixList) val selectedTabs = if (args.selectedTabIds != null) { - sessionManager.getTabs(args.selectedTabIds, store, publicSuffixList).toSet() + store.state.getTabs(args.selectedTabIds, publicSuffixList).toSet() } else { if (tabs.size == 1) setOf(tabs.first()) else emptySet() } @@ -112,14 +113,30 @@ class CollectionCreationFragment : DialogFragment() { } } -@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) -fun SessionManager.getTabs( +@VisibleForTesting +internal fun BrowserState.getTabs( tabIds: Array?, - store: BrowserStore, publicSuffixList: PublicSuffixList ): List { return tabIds - ?.mapNotNull { this.findSessionById(it) } - ?.map { it.toTab(store, publicSuffixList) } - ?: emptyList() + ?.mapNotNull { id -> findTab(id) } + ?.map { it.toTab(this, publicSuffixList) } + .orEmpty() +} + +private fun TabSessionState.toTab( + state: BrowserState, + publicSuffixList: PublicSuffixList, + selected: Boolean? = null +): Tab { + val url = readerState.activeUrl ?: content.url + return Tab( + sessionId = this.id, + url = url, + hostname = url.toShortUrl(publicSuffixList), + title = content.title, + selected = selected, + icon = content.icon, + mediaState = state.getMediaStateForSession(this.id) + ) } diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt new file mode 100644 index 000000000..3339b01b1 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -0,0 +1,16 @@ +/* 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.ext + +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.MediaState + +fun BrowserState.getMediaStateForSession(sessionId: String): MediaState.State { + return if (media.aggregate.activeTabId == sessionId) { + media.aggregate.state + } else { + MediaState.State.NONE + } +} diff --git a/app/src/main/java/org/mozilla/fenix/ext/Session.kt b/app/src/main/java/org/mozilla/fenix/ext/Session.kt deleted file mode 100644 index 2c830a214..000000000 --- a/app/src/main/java/org/mozilla/fenix/ext/Session.kt +++ /dev/null @@ -1,43 +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.ext - -import android.content.Context -import mozilla.components.browser.session.Session -import mozilla.components.browser.state.selector.findTab -import mozilla.components.browser.state.state.MediaState -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.lib.publicsuffixlist.PublicSuffixList -import org.mozilla.fenix.home.Tab - -fun Session.toTab(context: Context, selected: Boolean? = null): Tab = - this.toTab( - context.components.core.store, - context.components.publicSuffixList, - selected - ) - -fun Session.toTab(store: BrowserStore, publicSuffixList: PublicSuffixList, selected: Boolean? = null): Tab { - val url = store.state.findTab(this.id)?.readerState?.activeUrl ?: this.url - return Tab( - sessionId = this.id, - url = url, - hostname = url.toShortUrl(publicSuffixList), - title = this.title, - selected = selected, - icon = this.icon, - mediaState = getMediaStateForSession(store, this) - ) -} - -private fun getMediaStateForSession(store: BrowserStore, session: Session): MediaState.State { - // For now we are looking up the media state for this session in the BrowserStore. Eventually - // we will migrate away from Session(Manager) and can use BrowserStore and BrowserState directly. - return if (store.state.media.aggregate.activeTabId == session.id) { - store.state.media.aggregate.state - } else { - MediaState.State.NONE - } -} diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index f147c9371..9297026aa 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -60,9 +60,11 @@ import mozilla.components.browser.menu.item.BrowserMenuImageText import mozilla.components.browser.menu.view.MenuButton import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.privateTabs import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount @@ -90,9 +92,7 @@ import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.resetPoliciesAfter -import org.mozilla.fenix.ext.sessionsOfType import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.ext.toTab import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlView @@ -145,6 +145,8 @@ class HomeFragment : Fragment() { private val sessionManager: SessionManager get() = requireComponents.core.sessionManager + private val store: BrowserStore + get() = requireComponents.core.store private lateinit var homeAppBarOffSetListener: AppBarLayout.OnOffsetChangedListener private val onboarding by lazy { FenixOnboarding(requireContext()) } @@ -774,10 +776,6 @@ class HomeFragment : Fragment() { } } - private fun getNumberOfSessions(private: Boolean = browsingModeManager.mode.isPrivate): Int { - return sessionManager.sessionsOfType(private = private).count() - } - private fun registerCollectionStorageObserver() { requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this) } @@ -789,7 +787,9 @@ class HomeFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { val recyclerView = sessionControlView!!.view delay(ANIM_SCROLL_DELAY) - val tabsSize = getNumberOfSessions() + val tabsSize = store.state + .getNormalOrPrivateTabs(browsingModeManager.mode.isPrivate) + .size var indexOfCollection = tabsSize + NON_TAB_ITEM_NUM changedCollection?.let { changedCollection -> @@ -888,14 +888,6 @@ class HomeFragment : Fragment() { } } - private fun List.toTabs(): List { - val selected = sessionManager.selectedSession - - return map { - it.toTab(requireContext(), it == selected) - } - } - private fun calculateNewOffset() { homeAppBarOffset = ((homeAppBar.layoutParams as CoordinatorLayout.LayoutParams) .behavior as AppBarLayout.Behavior).topAndBottomOffset diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt index a94b08edd..d9fce4c4c 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt @@ -10,9 +10,11 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import androidx.annotation.VisibleForTesting +import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.AppCompatImageButton import androidx.core.content.ContextCompat import mozilla.components.browser.state.state.MediaState +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView import mozilla.components.browser.toolbar.MAX_URI_LENGTH @@ -26,12 +28,14 @@ import mozilla.components.support.images.loader.ImageLoader import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getMediaStateForSession import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.ext.removeAndDisable import org.mozilla.fenix.ext.removeTouchDelegate import org.mozilla.fenix.ext.showAndEnable -import org.mozilla.fenix.ext.toTab +import org.mozilla.fenix.utils.Do import kotlin.math.max /** @@ -40,8 +44,11 @@ import kotlin.math.max class TabTrayViewHolder( itemView: View, private val imageLoader: ImageLoader, - val getSelectedTabId: () -> String? = { itemView.context.components.core.store.state.selectedTabId } + private val store: BrowserStore = itemView.context.components.core.store, + private val metrics: MetricController = itemView.context.components.analytics.metrics, + val getSelectedTabId: () -> String? = { store.state.selectedTabId } ) : TabViewHolder(itemView) { + private val titleView: TextView = itemView.findViewById(R.id.mozac_browser_tabstray_title) private val closeView: AppCompatImageButton = itemView.findViewById(R.id.mozac_browser_tabstray_close) @@ -79,16 +86,15 @@ class TabTrayViewHolder( // Media state playPauseButtonView.increaseTapArea(PLAY_PAUSE_BUTTON_EXTRA_DPS) - val session = itemView.context?.components?.core?.sessionManager?.findSessionById(tab.id) with(playPauseButtonView) { invalidate() - when (session?.toTab(itemView.context)?.mediaState) { + Do exhaustive when (store.state.getMediaStateForSession(tab.id)) { MediaState.State.PAUSED -> { showAndEnable() contentDescription = context.getString(R.string.mozac_feature_media_notification_action_play) setImageDrawable( - androidx.appcompat.content.res.AppCompatResources.getDrawable( + AppCompatResources.getDrawable( context, R.drawable.tab_tray_play_with_background ) @@ -100,7 +106,7 @@ class TabTrayViewHolder( contentDescription = context.getString(R.string.mozac_feature_media_notification_action_pause) setImageDrawable( - androidx.appcompat.content.res.AppCompatResources.getDrawable( + AppCompatResources.getDrawable( context, R.drawable.tab_tray_pause_with_background ) @@ -115,16 +121,15 @@ class TabTrayViewHolder( } playPauseButtonView.setOnClickListener { - val mState = session?.toTab(itemView.context)?.mediaState - when (mState) { + Do exhaustive when (store.state.getMediaStateForSession(tab.id)) { MediaState.State.PLAYING -> { - itemView.context.components.analytics.metrics.track(Event.TabMediaPause) - itemView.context.components.core.store.state.media.pauseIfPlaying() + metrics.track(Event.TabMediaPause) + store.state.media.pauseIfPlaying() } MediaState.State.PAUSED -> { - itemView.context.components.analytics.metrics.track(Event.TabMediaPlay) - itemView.context.components.core.store.state.media.playIfPaused() + metrics.track(Event.TabMediaPlay) + store.state.media.playIfPaused() } MediaState.State.NONE -> throw AssertionError( @@ -189,7 +194,7 @@ class TabTrayViewHolder( } internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) { - item.setAccessibilityDelegate(object : View.AccessibilityDelegate() { + item.accessibilityDelegate = object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( host: View?, info: AccessibilityNodeInfo? @@ -208,7 +213,7 @@ class TabTrayViewHolder( } } } - }) + } } companion object { diff --git a/app/src/test/java/org/mozilla/fenix/collections/CollectionCreationFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/collections/CollectionCreationFragmentTest.kt index 5a5e062c9..913590f0d 100644 --- a/app/src/test/java/org/mozilla/fenix/collections/CollectionCreationFragmentTest.kt +++ b/app/src/test/java/org/mozilla/fenix/collections/CollectionCreationFragmentTest.kt @@ -7,17 +7,15 @@ package org.mozilla.fenix.collections import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.ObsoleteCoroutinesApi -import kotlinx.coroutines.async -import mozilla.components.browser.session.Session -import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.state.BrowserState -import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.browser.state.state.ReaderState +import mozilla.components.browser.state.state.createTab import mozilla.components.feature.tab.collections.Tab import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.support.test.robolectric.createAddedTestFragment +import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -25,6 +23,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.FenixRobolectricTestRunner private const val URL_MOZILLA = "www.mozilla.org" @@ -36,31 +35,29 @@ private const val SESSION_ID_BAD_1 = "not a real session id" private const val SESSION_ID_BAD_2 = "definitely not a real session id" @ExperimentalCoroutinesApi -@ObsoleteCoroutinesApi @RunWith(FenixRobolectricTestRunner::class) class CollectionCreationFragmentTest { - @MockK private lateinit var sessionManager: SessionManager - @MockK private lateinit var publicSuffixList: PublicSuffixList - @MockK private lateinit var store: BrowserStore + @MockK(relaxed = true) private lateinit var publicSuffixList: PublicSuffixList - private val sessionMozilla = Session(initialUrl = URL_MOZILLA, id = SESSION_ID_MOZILLA) - private val sessionBcc = Session(initialUrl = URL_BCC, id = SESSION_ID_BCC) + private val sessionMozilla = createTab(URL_MOZILLA, id = SESSION_ID_MOZILLA) + private val sessionBcc = createTab(URL_BCC, id = SESSION_ID_BCC) + private val state = BrowserState( + tabs = listOf(sessionMozilla, sessionBcc) + ) @Before fun before() { MockKAnnotations.init(this) - every { sessionManager.findSessionById(SESSION_ID_MOZILLA) } answers { sessionMozilla } - every { sessionManager.findSessionById(SESSION_ID_BCC) } answers { sessionBcc } - every { sessionManager.findSessionById(SESSION_ID_BAD_1) } answers { null } - every { sessionManager.findSessionById(SESSION_ID_BAD_2) } answers { null } - every { publicSuffixList.stripPublicSuffix(URL_MOZILLA) } answers { GlobalScope.async { URL_MOZILLA } } - every { publicSuffixList.stripPublicSuffix(URL_BCC) } answers { GlobalScope.async { URL_BCC } } - every { store.state } answers { BrowserState() } + every { publicSuffixList.stripPublicSuffix(URL_MOZILLA) } returns CompletableDeferred(URL_MOZILLA) + every { publicSuffixList.stripPublicSuffix(URL_BCC) } returns CompletableDeferred(URL_BCC) } @Test fun `creation dialog shows and can be dismissed`() { + val store = testContext.components.core.store + every { store.state } returns state + val fragment = createAddedTestFragment { CollectionCreationFragment().apply { arguments = CollectionCreationFragmentArgs( @@ -76,9 +73,8 @@ class CollectionCreationFragmentTest { } @Test - fun `GIVEN tabs are present in session manager WHEN getTabs is called THEN tabs will be returned`() { - val tabs = sessionManager - .getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), store, publicSuffixList) + fun `GIVEN tabs are present in state WHEN getTabs is called THEN tabs will be returned`() { + val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), publicSuffixList) val hosts = tabs.map { it.hostname } @@ -87,9 +83,8 @@ class CollectionCreationFragmentTest { } @Test - fun `GIVEN some tabs are present in session manager WHEN getTabs is called THEN only valid tabs will be returned`() { - val tabs = sessionManager - .getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), store, publicSuffixList) + fun `GIVEN some tabs are present in state WHEN getTabs is called THEN only valid tabs will be returned`() { + val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), publicSuffixList) val hosts = tabs.map { it.hostname } @@ -98,18 +93,41 @@ class CollectionCreationFragmentTest { } @Test - fun `GIVEN tabs are not present in session manager WHEN getTabs is called THEN an empty list will be returned`() { - val tabs = sessionManager - .getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), store, publicSuffixList) + fun `GIVEN tabs are not present in state WHEN getTabs is called THEN an empty list will be returned`() { + val tabs = state.getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), publicSuffixList) assertEquals(emptyList(), tabs) } @Test fun `WHEN getTabs is called will null tabIds THEN an empty list will be returned`() { - val tabs = sessionManager - .getTabs(null, store, publicSuffixList) + val tabs = state.getTabs(null, publicSuffixList) assertEquals(emptyList(), tabs) } + + @Test + fun `toTab uses active reader URL`() { + val tabWithoutReaderState = createTab(url = "https://example.com", id = "1") + + val tabWithInactiveReaderState = createTab(url = "https://blog.mozilla.org", id = "2", + readerState = ReaderState(active = false, activeUrl = null) + ) + + val tabWithActiveReaderState = createTab(url = "moz-extension://123", id = "3", + readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123") + ) + + val state = BrowserState( + tabs = listOf(tabWithoutReaderState, tabWithInactiveReaderState, tabWithActiveReaderState) + ) + val tabs = state.getTabs( + arrayOf(tabWithoutReaderState.id, tabWithInactiveReaderState.id, tabWithActiveReaderState.id), + publicSuffixList + ) + + assertEquals(tabWithoutReaderState.content.url, tabs[0].url) + assertEquals(tabWithInactiveReaderState.content.url, tabs[1].url) + assertEquals("https://blog.mozilla.org/123", tabs[2].url) + } } diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt index a9d530bbf..c203fb976 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt @@ -64,7 +64,6 @@ import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.directionsEq import org.mozilla.fenix.ext.nav -import org.mozilla.fenix.ext.toTab import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.home.Tab import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit @@ -110,11 +109,6 @@ class DefaultBrowserToolbarControllerTest { fun setUp() { MockKAnnotations.init(this) - mockkStatic( - "org.mozilla.fenix.ext.SessionKt" - ) - every { any().toTab(any()) } returns currentSessionAsTab - mockkStatic( "org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt" ) @@ -138,7 +132,6 @@ class DefaultBrowserToolbarControllerTest { @After fun tearDown() { - unmockkStatic("org.mozilla.fenix.ext.SessionKt") unmockkStatic("org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt") } diff --git a/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt new file mode 100644 index 000000000..7c2c0a1d1 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt @@ -0,0 +1,37 @@ +/* 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.ext + +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.MediaState +import mozilla.components.browser.state.state.createTab +import org.junit.Assert.assertEquals +import org.junit.Test + +private const val SESSION_ID_MOZILLA = "0" +private const val SESSION_ID_BCC = "1" +private const val SESSION_ID_BAD = "not a real session id" + +class BrowserStateTest { + + private val sessionMozilla = createTab(url = "www.mozilla.org", id = SESSION_ID_MOZILLA) + private val sessionBcc = createTab(url = "www.bcc.co.uk", id = SESSION_ID_BCC) + + @Test + fun `return media state if it matches tab id`() { + val state = BrowserState( + tabs = listOf(sessionBcc, sessionMozilla), + media = MediaState( + MediaState.Aggregate( + state = MediaState.State.PLAYING, + activeTabId = SESSION_ID_MOZILLA + )) + ) + + assertEquals(MediaState.State.PLAYING, state.getMediaStateForSession(SESSION_ID_MOZILLA)) + assertEquals(MediaState.State.NONE, state.getMediaStateForSession(SESSION_ID_BCC)) + assertEquals(MediaState.State.NONE, state.getMediaStateForSession(SESSION_ID_BAD)) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/ext/SessionTest.kt b/app/src/test/java/org/mozilla/fenix/ext/SessionTest.kt deleted file mode 100644 index d784e257c..000000000 --- a/app/src/test/java/org/mozilla/fenix/ext/SessionTest.kt +++ /dev/null @@ -1,45 +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.ext - -import io.mockk.mockk -import mozilla.components.browser.session.Session -import mozilla.components.browser.state.state.BrowserState -import mozilla.components.browser.state.state.ReaderState -import mozilla.components.browser.state.state.createTab -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.lib.publicsuffixlist.PublicSuffixList -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner - -@RunWith(FenixRobolectricTestRunner::class) -class SessionTest { - - @Test - fun `toTab uses active reader URL`() { - val sessionWithoutReaderState = Session(id = "1", initialUrl = "https://example.com") - val tabWithoutReaderState = createTab(url = sessionWithoutReaderState.url, id = sessionWithoutReaderState.id) - - val sessionWithInactiveReaderState = Session(id = "2", initialUrl = "https://blog.mozilla.org") - val tabWithInactiveReaderState = createTab(url = sessionWithInactiveReaderState.url, id = sessionWithInactiveReaderState.id, - readerState = ReaderState(active = false, activeUrl = null) - ) - - val sessionWithActiveReaderState = Session(id = "3", initialUrl = "moz-extension://123") - val tabWithActiveReaderState = createTab(url = sessionWithActiveReaderState.url, id = sessionWithActiveReaderState.id, - readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123") - ) - - val tabs = listOf(tabWithoutReaderState, tabWithInactiveReaderState, tabWithActiveReaderState) - val store = BrowserStore(BrowserState(tabs)) - - val suffixList = mockk(relaxed = true) - assertEquals(sessionWithoutReaderState.url, sessionWithoutReaderState.toTab(store, suffixList).url) - assertEquals(sessionWithInactiveReaderState.url, sessionWithInactiveReaderState.toTab(store, suffixList).url) - assertEquals("https://blog.mozilla.org/123", sessionWithActiveReaderState.toTab(store, suffixList).url) - } -} diff --git a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt index c0ea3ddbc..0f5a8ce91 100644 --- a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.TestCoroutineScope import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.Engine import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tabs.TabsUseCases @@ -43,6 +44,7 @@ class DefaultSessionControlControllerTest { private val navController: NavController = mockk(relaxed = true) private val metrics: MetricController = mockk(relaxed = true) private val sessionManager: SessionManager = mockk(relaxed = true) + private val store: BrowserStore = mockk(relaxed = true) private val engine: Engine = mockk(relaxed = true) private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true) private val topSiteStorage: TopSiteStorage = mockk(relaxed = true) diff --git a/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayViewHolderTest.kt index bb110c2eb..57aa76948 100644 --- a/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayViewHolderTest.kt @@ -5,33 +5,54 @@ package org.mozilla.fenix.tabtray import android.view.LayoutInflater -import androidx.test.core.app.ApplicationProvider +import android.view.View +import android.widget.ImageButton +import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every +import io.mockk.impl.annotations.MockK import io.mockk.just 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.MediaState +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.toolbar.MAX_URI_LENGTH import mozilla.components.concept.tabstray.Tab +import mozilla.components.support.images.ImageLoadRequest import mozilla.components.support.images.loader.ImageLoader +import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.helpers.FenixRobolectricTestRunner @RunWith(FenixRobolectricTestRunner::class) class TabTrayViewHolderTest { + private lateinit var view: View + @MockK private lateinit var imageLoader: ImageLoader + @MockK private lateinit var store: BrowserStore + @MockK private lateinit var metrics: MetricController + private var state = BrowserState() + + @Before + fun setup() { + MockKAnnotations.init(this) + view = LayoutInflater.from(testContext) + .inflate(R.layout.tab_tray_item, null, false) + state = BrowserState() + + every { imageLoader.loadIntoView(any(), any(), any(), any()) } just Runs + every { store.state } answers { state } + } + @Test fun `extremely long URLs are truncated to prevent slowing down the UI`() { - val view = LayoutInflater.from(ApplicationProvider.getApplicationContext()).inflate( - R.layout.tab_tray_item, null, false - ) - val imageLoader: ImageLoader = mockk() - every { imageLoader.loadIntoView(any(), any(), any(), any()) } just Runs - val tabViewHolder = spyk(TabTrayViewHolder(view, imageLoader) { null }) - every { tabViewHolder.updateBackgroundColor(false) } just Runs + val tabViewHolder = createViewHolder() val extremelyLongUrl = "m".repeat(MAX_URI_LENGTH + 1) val tab = Tab( @@ -41,5 +62,58 @@ class TabTrayViewHolderTest { tabViewHolder.bind(tab, false, mockk()) assertEquals("m".repeat(MAX_URI_LENGTH), tabViewHolder.urlView?.text) + verify { imageLoader.loadIntoView(any(), ImageLoadRequest("123", 92)) } } + + @Test + fun `show play button if media is paused in tab`() { + val playPauseButtonView: ImageButton = view.findViewById(R.id.play_pause_button) + val tabViewHolder = createViewHolder() + + val tab = Tab( + id = "123", + url = "https://example.com" + ) + state = state.copy( + media = MediaState( + aggregate = MediaState.Aggregate( + activeTabId = "123", + state = MediaState.State.PAUSED + ) + ) + ) + tabViewHolder.bind(tab, false, mockk()) + + assertEquals("Play", playPauseButtonView.contentDescription) + } + + @Test + fun `show pause button if media is playing in tab`() { + val playPauseButtonView: ImageButton = view.findViewById(R.id.play_pause_button) + val tabViewHolder = createViewHolder() + + val tab = Tab( + id = "123", + url = "https://example.com" + ) + state = state.copy( + media = MediaState( + aggregate = MediaState.Aggregate( + activeTabId = "123", + state = MediaState.State.PLAYING + ) + ) + ) + tabViewHolder.bind(tab, false, mockk()) + + assertEquals("Pause", playPauseButtonView.contentDescription) + } + + private fun createViewHolder(getSelectedTabId: () -> String? = { null }) = TabTrayViewHolder( + view, + imageLoader = imageLoader, + store = store, + metrics = metrics, + getSelectedTabId = getSelectedTabId + ) }