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.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.consumeFrom
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.ext.getMediaStateForSession
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.toTab import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.Tab import org.mozilla.fenix.home.Tab
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@ -47,12 +49,11 @@ class CollectionCreationFragment : DialogFragment() {
val view = inflater.inflate(R.layout.fragment_create_collection, container, false) val view = inflater.inflate(R.layout.fragment_create_collection, container, false)
val args: CollectionCreationFragmentArgs by navArgs() val args: CollectionCreationFragmentArgs by navArgs()
val sessionManager = requireComponents.core.sessionManager
val store = requireComponents.core.store val store = requireComponents.core.store
val publicSuffixList = requireComponents.publicSuffixList 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) { val selectedTabs = if (args.selectedTabIds != null) {
sessionManager.getTabs(args.selectedTabIds, store, publicSuffixList).toSet() store.state.getTabs(args.selectedTabIds, publicSuffixList).toSet()
} else { } else {
if (tabs.size == 1) setOf(tabs.first()) else emptySet() if (tabs.size == 1) setOf(tabs.first()) else emptySet()
} }
@ -112,14 +113,30 @@ class CollectionCreationFragment : DialogFragment() {
} }
} }
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting
fun SessionManager.getTabs( internal fun BrowserState.getTabs(
tabIds: Array<String>?, tabIds: Array<String>?,
store: BrowserStore,
publicSuffixList: PublicSuffixList publicSuffixList: PublicSuffixList
): List<Tab> { ): List<Tab> {
return tabIds return tabIds
?.mapNotNull { this.findSessionById(it) } ?.mapNotNull { id -> findTab(id) }
?.map { it.toTab(store, publicSuffixList) } ?.map { it.toTab(this, publicSuffixList) }
?: emptyList() .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.menu.view.MenuButton
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager 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.normalTabs
import mozilla.components.browser.state.selector.privateTabs import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.state.BrowserState 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.AccountObserver
import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount 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.nav
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.resetPoliciesAfter import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings 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.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.home.sessioncontrol.SessionControlView import org.mozilla.fenix.home.sessioncontrol.SessionControlView
@ -145,6 +145,8 @@ class HomeFragment : Fragment() {
private val sessionManager: SessionManager private val sessionManager: SessionManager
get() = requireComponents.core.sessionManager get() = requireComponents.core.sessionManager
private val store: BrowserStore
get() = requireComponents.core.store
private lateinit var homeAppBarOffSetListener: AppBarLayout.OnOffsetChangedListener private lateinit var homeAppBarOffSetListener: AppBarLayout.OnOffsetChangedListener
private val onboarding by lazy { FenixOnboarding(requireContext()) } 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() { private fun registerCollectionStorageObserver() {
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this) requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
} }
@ -789,7 +787,9 @@ class HomeFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
val recyclerView = sessionControlView!!.view val recyclerView = sessionControlView!!.view
delay(ANIM_SCROLL_DELAY) delay(ANIM_SCROLL_DELAY)
val tabsSize = getNumberOfSessions() val tabsSize = store.state
.getNormalOrPrivateTabs(browsingModeManager.mode.isPrivate)
.size
var indexOfCollection = tabsSize + NON_TAB_ITEM_NUM var indexOfCollection = tabsSize + NON_TAB_ITEM_NUM
changedCollection?.let { changedCollection -> 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() { private fun calculateNewOffset() {
homeAppBarOffset = ((homeAppBar.layoutParams as CoordinatorLayout.LayoutParams) homeAppBarOffset = ((homeAppBar.layoutParams as CoordinatorLayout.LayoutParams)
.behavior as AppBarLayout.Behavior).topAndBottomOffset .behavior as AppBarLayout.Behavior).topAndBottomOffset

View File

@ -10,9 +10,11 @@ import android.widget.ImageButton
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.AppCompatImageButton import androidx.appcompat.widget.AppCompatImageButton
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import mozilla.components.browser.state.state.MediaState 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.TabViewHolder
import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
import mozilla.components.browser.toolbar.MAX_URI_LENGTH 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 mozilla.components.support.ktx.kotlin.tryGetHostFromUrl
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event 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.components
import org.mozilla.fenix.ext.getMediaStateForSession
import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.removeAndDisable import org.mozilla.fenix.ext.removeAndDisable
import org.mozilla.fenix.ext.removeTouchDelegate import org.mozilla.fenix.ext.removeTouchDelegate
import org.mozilla.fenix.ext.showAndEnable import org.mozilla.fenix.ext.showAndEnable
import org.mozilla.fenix.ext.toTab import org.mozilla.fenix.utils.Do
import kotlin.math.max import kotlin.math.max
/** /**
@ -40,8 +44,11 @@ import kotlin.math.max
class TabTrayViewHolder( class TabTrayViewHolder(
itemView: View, itemView: View,
private val imageLoader: ImageLoader, 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) { ) : TabViewHolder(itemView) {
private val titleView: TextView = itemView.findViewById(R.id.mozac_browser_tabstray_title) private val titleView: TextView = itemView.findViewById(R.id.mozac_browser_tabstray_title)
private val closeView: AppCompatImageButton = private val closeView: AppCompatImageButton =
itemView.findViewById(R.id.mozac_browser_tabstray_close) itemView.findViewById(R.id.mozac_browser_tabstray_close)
@ -79,16 +86,15 @@ class TabTrayViewHolder(
// Media state // Media state
playPauseButtonView.increaseTapArea(PLAY_PAUSE_BUTTON_EXTRA_DPS) playPauseButtonView.increaseTapArea(PLAY_PAUSE_BUTTON_EXTRA_DPS)
val session = itemView.context?.components?.core?.sessionManager?.findSessionById(tab.id)
with(playPauseButtonView) { with(playPauseButtonView) {
invalidate() invalidate()
when (session?.toTab(itemView.context)?.mediaState) { Do exhaustive when (store.state.getMediaStateForSession(tab.id)) {
MediaState.State.PAUSED -> { MediaState.State.PAUSED -> {
showAndEnable() showAndEnable()
contentDescription = contentDescription =
context.getString(R.string.mozac_feature_media_notification_action_play) context.getString(R.string.mozac_feature_media_notification_action_play)
setImageDrawable( setImageDrawable(
androidx.appcompat.content.res.AppCompatResources.getDrawable( AppCompatResources.getDrawable(
context, context,
R.drawable.tab_tray_play_with_background R.drawable.tab_tray_play_with_background
) )
@ -100,7 +106,7 @@ class TabTrayViewHolder(
contentDescription = contentDescription =
context.getString(R.string.mozac_feature_media_notification_action_pause) context.getString(R.string.mozac_feature_media_notification_action_pause)
setImageDrawable( setImageDrawable(
androidx.appcompat.content.res.AppCompatResources.getDrawable( AppCompatResources.getDrawable(
context, context,
R.drawable.tab_tray_pause_with_background R.drawable.tab_tray_pause_with_background
) )
@ -115,16 +121,15 @@ class TabTrayViewHolder(
} }
playPauseButtonView.setOnClickListener { playPauseButtonView.setOnClickListener {
val mState = session?.toTab(itemView.context)?.mediaState Do exhaustive when (store.state.getMediaStateForSession(tab.id)) {
when (mState) {
MediaState.State.PLAYING -> { MediaState.State.PLAYING -> {
itemView.context.components.analytics.metrics.track(Event.TabMediaPause) metrics.track(Event.TabMediaPause)
itemView.context.components.core.store.state.media.pauseIfPlaying() store.state.media.pauseIfPlaying()
} }
MediaState.State.PAUSED -> { MediaState.State.PAUSED -> {
itemView.context.components.analytics.metrics.track(Event.TabMediaPlay) metrics.track(Event.TabMediaPlay)
itemView.context.components.core.store.state.media.playIfPaused() store.state.media.playIfPaused()
} }
MediaState.State.NONE -> throw AssertionError( MediaState.State.NONE -> throw AssertionError(
@ -189,7 +194,7 @@ class TabTrayViewHolder(
} }
internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) { internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) {
item.setAccessibilityDelegate(object : View.AccessibilityDelegate() { item.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo( override fun onInitializeAccessibilityNodeInfo(
host: View?, host: View?,
info: AccessibilityNodeInfo? info: AccessibilityNodeInfo?
@ -208,7 +213,7 @@ class TabTrayViewHolder(
} }
} }
} }
}) }
} }
companion object { companion object {

View File

@ -7,17 +7,15 @@ package org.mozilla.fenix.collections
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi 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.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.feature.tab.collections.Tab
import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.test.robolectric.createAddedTestFragment import mozilla.components.support.test.robolectric.createAddedTestFragment
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
@ -25,6 +23,7 @@ import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
private const val URL_MOZILLA = "www.mozilla.org" 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" private const val SESSION_ID_BAD_2 = "definitely not a real session id"
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class CollectionCreationFragmentTest { class CollectionCreationFragmentTest {
@MockK private lateinit var sessionManager: SessionManager @MockK(relaxed = true) private lateinit var publicSuffixList: PublicSuffixList
@MockK private lateinit var publicSuffixList: PublicSuffixList
@MockK private lateinit var store: BrowserStore
private val sessionMozilla = Session(initialUrl = URL_MOZILLA, id = SESSION_ID_MOZILLA) private val sessionMozilla = createTab(URL_MOZILLA, id = SESSION_ID_MOZILLA)
private val sessionBcc = Session(initialUrl = URL_BCC, id = SESSION_ID_BCC) private val sessionBcc = createTab(URL_BCC, id = SESSION_ID_BCC)
private val state = BrowserState(
tabs = listOf(sessionMozilla, sessionBcc)
)
@Before @Before
fun before() { fun before() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
every { sessionManager.findSessionById(SESSION_ID_MOZILLA) } answers { sessionMozilla } every { publicSuffixList.stripPublicSuffix(URL_MOZILLA) } returns CompletableDeferred(URL_MOZILLA)
every { sessionManager.findSessionById(SESSION_ID_BCC) } answers { sessionBcc } every { publicSuffixList.stripPublicSuffix(URL_BCC) } returns CompletableDeferred(URL_BCC)
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() }
} }
@Test @Test
fun `creation dialog shows and can be dismissed`() { fun `creation dialog shows and can be dismissed`() {
val store = testContext.components.core.store
every { store.state } returns state
val fragment = createAddedTestFragment { val fragment = createAddedTestFragment {
CollectionCreationFragment().apply { CollectionCreationFragment().apply {
arguments = CollectionCreationFragmentArgs( arguments = CollectionCreationFragmentArgs(
@ -76,9 +73,8 @@ class CollectionCreationFragmentTest {
} }
@Test @Test
fun `GIVEN tabs are present in session manager WHEN getTabs is called THEN tabs will be returned`() { fun `GIVEN tabs are present in state WHEN getTabs is called THEN tabs will be returned`() {
val tabs = sessionManager val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), publicSuffixList)
.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), store, publicSuffixList)
val hosts = tabs.map { it.hostname } val hosts = tabs.map { it.hostname }
@ -87,9 +83,8 @@ class CollectionCreationFragmentTest {
} }
@Test @Test
fun `GIVEN some tabs are present in session manager WHEN getTabs is called THEN only valid tabs will be returned`() { fun `GIVEN some tabs are present in state WHEN getTabs is called THEN only valid tabs will be returned`() {
val tabs = sessionManager val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), publicSuffixList)
.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), store, publicSuffixList)
val hosts = tabs.map { it.hostname } val hosts = tabs.map { it.hostname }
@ -98,18 +93,41 @@ class CollectionCreationFragmentTest {
} }
@Test @Test
fun `GIVEN tabs are not present in session manager WHEN getTabs is called THEN an empty list will be returned`() { fun `GIVEN tabs are not present in state WHEN getTabs is called THEN an empty list will be returned`() {
val tabs = sessionManager val tabs = state.getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), publicSuffixList)
.getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), store, publicSuffixList)
assertEquals(emptyList<Tab>(), tabs) assertEquals(emptyList<Tab>(), tabs)
} }
@Test @Test
fun `WHEN getTabs is called will null tabIds THEN an empty list will be returned`() { fun `WHEN getTabs is called will null tabIds THEN an empty list will be returned`() {
val tabs = sessionManager val tabs = state.getTabs(null, publicSuffixList)
.getTabs(null, store, publicSuffixList)
assertEquals(emptyList<Tab>(), tabs) 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.components
import org.mozilla.fenix.ext.directionsEq import org.mozilla.fenix.ext.directionsEq
import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.toTab
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.Tab import org.mozilla.fenix.home.Tab
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
@ -110,11 +109,6 @@ class DefaultBrowserToolbarControllerTest {
fun setUp() { fun setUp() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
mockkStatic(
"org.mozilla.fenix.ext.SessionKt"
)
every { any<Session>().toTab(any()) } returns currentSessionAsTab
mockkStatic( mockkStatic(
"org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt" "org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt"
) )
@ -138,7 +132,6 @@ class DefaultBrowserToolbarControllerTest {
@After @After
fun tearDown() { fun tearDown() {
unmockkStatic("org.mozilla.fenix.ext.SessionKt")
unmockkStatic("org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt") 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.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestCoroutineScope
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.Engine
import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.feature.tabs.TabsUseCases
@ -43,6 +44,7 @@ class DefaultSessionControlControllerTest {
private val navController: NavController = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true)
private val metrics: MetricController = mockk(relaxed = true) private val metrics: MetricController = mockk(relaxed = true)
private val sessionManager: SessionManager = 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 engine: Engine = mockk(relaxed = true)
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true) private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
private val topSiteStorage: TopSiteStorage = mockk(relaxed = true) private val topSiteStorage: TopSiteStorage = mockk(relaxed = true)

View File

@ -5,33 +5,54 @@
package org.mozilla.fenix.tabtray package org.mozilla.fenix.tabtray
import android.view.LayoutInflater 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.Runs
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.just import io.mockk.just
import io.mockk.mockk 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.browser.toolbar.MAX_URI_LENGTH
import mozilla.components.concept.tabstray.Tab import mozilla.components.concept.tabstray.Tab
import mozilla.components.support.images.ImageLoadRequest
import mozilla.components.support.images.loader.ImageLoader import mozilla.components.support.images.loader.ImageLoader
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class TabTrayViewHolderTest { 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 @Test
fun `extremely long URLs are truncated to prevent slowing down the UI`() { fun `extremely long URLs are truncated to prevent slowing down the UI`() {
val view = LayoutInflater.from(ApplicationProvider.getApplicationContext()).inflate( val tabViewHolder = createViewHolder()
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 extremelyLongUrl = "m".repeat(MAX_URI_LENGTH + 1) val extremelyLongUrl = "m".repeat(MAX_URI_LENGTH + 1)
val tab = Tab( val tab = Tab(
@ -41,5 +62,58 @@ class TabTrayViewHolderTest {
tabViewHolder.bind(tab, false, mockk()) tabViewHolder.bind(tab, false, mockk())
assertEquals("m".repeat(MAX_URI_LENGTH), tabViewHolder.urlView?.text) 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
)
} }