1
0
Fork 0

Migrate from Session.toTab to BrowserStore (#12221)

master
Tiger Oakes 2020-07-23 12:27:39 -07:00 committed by GitHub
parent 9af167ba0a
commit 652af79e95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 241 additions and 175 deletions

View File

@ -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<String>?,
store: BrowserStore,
publicSuffixList: PublicSuffixList
): List<Tab> {
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)
)
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<Session>.toTabs(): List<Tab> {
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

View File

@ -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 {

View File

@ -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<Tab>(), 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<Tab>(), 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)
}
}

View File

@ -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<Session>().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")
}

View File

@ -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))
}
}

View File

@ -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<PublicSuffixList>(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)
}
}

View File

@ -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)

View File

@ -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
)
}