For #26227 - Remove search term tab groups from Jump Back In

This commit is contained in:
Noah Bond 2022-08-03 15:23:58 -07:00 committed by mergify[bot]
parent aa992263d6
commit 8656fd48eb
28 changed files with 23 additions and 956 deletions

View File

@ -93,7 +93,6 @@ import org.mozilla.fenix.perf.StrictModeManager
import org.mozilla.fenix.perf.lazyMonitored
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.advanced.getSelectedLocale
import org.mozilla.fenix.tabstray.SearchTermTabGroupMiddleware
import org.mozilla.fenix.telemetry.TelemetryMiddleware
import org.mozilla.fenix.utils.getUndoDelay
import org.mozilla.geckoview.GeckoRuntime
@ -247,7 +246,6 @@ class Core(
AdsTelemetryMiddleware(adsTelemetry),
LastMediaAccessMiddleware(),
HistoryMetadataMiddleware(historyMetadataService),
SearchTermTabGroupMiddleware()
) + if (tabsPrioritizationEnable) {
listOf(SessionPrioritizationMiddleware())
} else {

View File

@ -11,12 +11,10 @@ import mozilla.components.service.pocket.ext.recordNewImpression
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.ext.filterOutTab
import org.mozilla.fenix.ext.getFilteredStories
import org.mozilla.fenix.ext.recentSearchGroup
import org.mozilla.fenix.gleanplumb.state.MessagingReducer
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesSelectedCategory
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.gleanplumb.state.MessagingReducer
/**
* Reducer for [AppStore].
@ -44,12 +42,7 @@ internal object AppStoreReducer {
topSites = action.topSites,
recentBookmarks = action.recentBookmarks,
recentTabs = action.recentTabs,
recentHistory = if (action.recentHistory.isNotEmpty() && action.recentTabs.isNotEmpty()) {
val recentSearchGroup = action.recentTabs.find { it is RecentTab.SearchGroup } as RecentTab.SearchGroup?
action.recentHistory.filterOut(recentSearchGroup?.searchTerm)
} else {
action.recentHistory
}
recentHistory = action.recentHistory,
)
is AppAction.CollectionExpanded -> {
val newExpandedCollection = state.expandedCollections.toMutableSet()
@ -69,10 +62,9 @@ internal object AppStoreReducer {
state.copy(showCollectionPlaceholder = false)
}
is AppAction.RecentTabsChange -> {
val recentSearchGroup = action.recentTabs.find { it is RecentTab.SearchGroup } as RecentTab.SearchGroup?
state.copy(
recentTabs = action.recentTabs,
recentHistory = state.recentHistory.filterOut(recentSearchGroup?.searchTerm)
recentHistory = state.recentHistory,
)
}
is AppAction.RemoveRecentTab -> {
@ -90,7 +82,7 @@ internal object AppStoreReducer {
state.copy(recentBookmarks = state.recentBookmarks.filterNot { it.url == action.recentBookmark.url })
}
is AppAction.RecentHistoryChange -> state.copy(
recentHistory = action.recentHistory.filterOut(state.recentSearchGroup?.searchTerm)
recentHistory = action.recentHistory
)
is AppAction.RemoveRecentHistoryHighlight -> state.copy(
recentHistory = state.recentHistory.filterNot {
@ -99,10 +91,7 @@ internal object AppStoreReducer {
)
is AppAction.DisbandSearchGroupAction -> state.copy(
recentHistory = state.recentHistory.filterNot {
it is RecentHistoryGroup && (
it.title.equals(action.searchTerm, true) ||
it.title.equals(state.recentSearchGroup?.searchTerm, true)
)
it is RecentHistoryGroup && it.title.equals(action.searchTerm, true)
}
)
is AppAction.SelectPocketStoriesCategory -> {

View File

@ -16,7 +16,6 @@ import org.mozilla.fenix.home.pocket.POCKET_STORIES_DEFAULT_CATEGORY_NAME
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.pocket.PocketStory
import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState
import org.mozilla.fenix.home.recenttabs.RecentTab.SearchGroup
import org.mozilla.fenix.utils.Settings
/**
@ -162,13 +161,6 @@ internal fun getFilteredSponsoredStories(
.toList()
}
/**
* Get the [SearchGroup] shown in the "Jump back in" section.
* May be null if no search group is shown.
*/
internal val AppState.recentSearchGroup: SearchGroup?
get() = recentTabs.find { it is SearchGroup } as SearchGroup?
/**
* Filter a [AppState] by the blocklist.
*

View File

@ -4,16 +4,12 @@
package org.mozilla.fenix.ext
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.selectedNormalTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabGroup
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.feature.tabs.ext.hasMediaPlayed
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.tabstray.SEARCH_TERM_TAB_GROUPS
import org.mozilla.fenix.tabstray.SEARCH_TERM_TAB_GROUPS_MIN_SIZE
import org.mozilla.fenix.tabstray.ext.isNormalTabInactive
import org.mozilla.fenix.utils.Settings
import java.util.concurrent.TimeUnit
@ -29,26 +25,15 @@ const val DEFAULT_ACTIVE_DAYS = 14L
val maxActiveTime = TimeUnit.DAYS.toMillis(DEFAULT_ACTIVE_DAYS)
/**
* Get the last opened normal tab, last tab with in progress media and last search term group, if available.
* Get the last opened normal tab or last tab with in progress media.
*
* @return A list of the last opened tab not part of the last active search group and
* the last active search group if these are available or an empty list.
* @return A list of the last opened tab or an empty list.
*/
fun BrowserState.asRecentTabs(): List<RecentTab> {
return mutableListOf<RecentTab>().apply {
val mostRecentTabsGroup = lastSearchGroup
val mostRecentTabNotInGroup = if (mostRecentTabsGroup == null) {
lastOpenedNormalTab
} else {
listOf(selectedNormalTab)
.plus(normalTabs.sortedByDescending { it.lastAccess })
.filterNot { lastTabGroup?.tabIds?.contains(it?.id) ?: false }
.firstOrNull()
}
mostRecentTabNotInGroup?.let { add(RecentTab.Tab(it)) }
mostRecentTabsGroup?.let { add(it) }
return if (lastOpenedNormalTab == null) {
mutableListOf()
} else {
mutableListOf(RecentTab.Tab(lastOpenedNormalTab!!))
}
}
@ -76,32 +61,6 @@ val BrowserState.inProgressMediaTab: TabSessionState?
.filter { it.hasMediaPlayed() }
.maxByOrNull { it.lastMediaAccessState.lastMediaAccess }
/**
* Get the most recently accessed [TabGroup].
* Result will be `null` if the currently open normal tabs are not part of a search group.
*/
val BrowserState.lastTabGroup: TabGroup?
get() = tabPartitions[SEARCH_TERM_TAB_GROUPS]?.tabGroups
?.lastOrNull { it.tabIds.size >= SEARCH_TERM_TAB_GROUPS_MIN_SIZE }
/**
* Get the most recent search term group.
*/
val BrowserState.lastSearchGroup: RecentTab.SearchGroup?
get() {
val tabGroup = lastTabGroup ?: return null
val firstTabId = tabGroup.tabIds.firstOrNull() ?: return null
val firstTab = findTab(firstTabId) ?: return null
return RecentTab.SearchGroup(
tabGroup.id,
firstTabId,
firstTab.content.url,
firstTab.content.thumbnail,
tabGroup.tabIds.size
)
}
/**
* List of all inactive tabs based on [maxActiveTime].
* The user may have disabled the feature so for user interactions consider using the [actualInactiveTabs] method

View File

@ -8,7 +8,7 @@ import androidx.annotation.VisibleForTesting
import org.mozilla.fenix.home.recenttabs.RecentTab
/**
* Removes a [RecentTab.Tab] from a list of [RecentTab]. [RecentTab.SearchGroup]s will not be filtered.
* Removes a [RecentTab.Tab] from a list of [RecentTab].
*
* @param tab [RecentTab] to remove from the list
*/

View File

@ -4,10 +4,8 @@
package org.mozilla.fenix.home.recenttabs
import android.graphics.Bitmap
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
@ -29,8 +27,7 @@ class RecentTabsListFeature(
) : AbstractBinding<BrowserState>(browserStore) {
override suspend fun onState(flow: Flow<BrowserState>) {
// Listen for changes regarding the currently selected tab, in progress media tab
// and search term groups.
// Listen for changes regarding the currently selected tab and in progress media tab.
flow
.map { it.asRecentTabs() }
.ifChanged()
@ -47,21 +44,4 @@ sealed class RecentTab {
* @param state Recently viewed [TabSessionState]
*/
data class Tab(val state: TabSessionState) : RecentTab()
/**
* A search term group that was recently viewed
*
* @param searchTerm The search term that was recently viewed. Forced to start with uppercase.
* @param tabId The id of the tab that was recently viewed
* @param url The url that was recently viewed
* @param thumbnail The thumbnail of the search term that was recently viewed
* @param count The number of tabs in the search term group
*/
data class SearchGroup(
val searchTerm: String,
val tabId: String,
val url: String,
val thumbnail: Bitmap?,
val count: Int
) : RecentTab()
}

View File

@ -10,7 +10,7 @@ import androidx.navigation.NavController
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.TabsUseCases.SelectTabUseCase
import mozilla.components.service.glean.private.NoExtras
import org.mozilla.fenix.GleanMetrics.SearchTerms
import org.mozilla.fenix.GleanMetrics.RecentTabs
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
@ -18,7 +18,6 @@ import org.mozilla.fenix.ext.inProgressMediaTab
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor
import org.mozilla.fenix.GleanMetrics.RecentTabs as RecentTabs
/**
* An interface that handles the view manipulation of the recent tabs in the Home screen.
@ -30,11 +29,6 @@ interface RecentTabController {
*/
fun handleRecentTabClicked(tabId: String)
/**
* @see [RecentTabInteractor.onRecentSearchGroupClicked]
*/
fun handleRecentSearchGroupClicked(tabId: String)
/**
* @see [RecentTabInteractor.onRecentTabShowAllClicked]
*/
@ -76,15 +70,6 @@ class DefaultRecentTabsController(
navController.navigate(HomeFragmentDirections.actionGlobalTabsTrayFragment())
}
override fun handleRecentSearchGroupClicked(tabId: String) {
SearchTerms.jumpBackInGroupTapped.record(NoExtras())
navController.navigate(
HomeFragmentDirections.actionGlobalTabsTrayFragment(
focusGroupTabId = tabId
)
)
}
override fun handleRecentTabRemoved(tab: RecentTab.Tab) {
appStore.dispatch(AppAction.RemoveRecentTab(tab))
}

View File

@ -17,13 +17,6 @@ interface RecentTabInteractor {
*/
fun onRecentTabClicked(tabId: String)
/**
* Opens the tabs tray and scroll to the search group. Called when a user clicks on a search group.
*
* @param tabId The ID of the tab to open.
*/
fun onRecentSearchGroupClicked(tabId: String)
/**
* Show the tabs tray. Called when a user clicks on the "Show all" button besides the recent
* tabs.

View File

@ -58,7 +58,6 @@ class RecentTabViewHolder(
RecentTabs(
recentTabs = recentTabs.value ?: emptyList(),
onRecentTabClick = { recentTabInteractor.onRecentTabClicked(it) },
onRecentSearchGroupClick = { recentTabInteractor.onRecentSearchGroupClicked(it) },
menuItems = listOf(
RecentTabMenuItem(
title = stringResource(id = R.string.recent_tab_menu_item_remove),

View File

@ -10,7 +10,6 @@ import android.graphics.Bitmap
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
@ -29,7 +28,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@ -44,8 +42,6 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@ -53,7 +49,6 @@ import mozilla.components.browser.icons.compose.Loader
import mozilla.components.browser.icons.compose.Placeholder
import mozilla.components.browser.icons.compose.WithIcon
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.Image
import org.mozilla.fenix.compose.ThumbnailCard
@ -66,14 +61,12 @@ import org.mozilla.fenix.theme.FirefoxTheme
* @param recentTabs List of [RecentTab] to display.
* @param menuItems List of [RecentTabMenuItem] shown long clicking a [RecentTab].
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
* @param onRecentSearchGroupClick Invoked when the user clicks on a recent search group.
*/
@Composable
fun RecentTabs(
recentTabs: List<RecentTab>,
menuItems: List<RecentTabMenuItem>,
onRecentTabClick: (String) -> Unit = {},
onRecentSearchGroupClick: (String) -> Unit = {},
) {
Column(
modifier = Modifier.fillMaxWidth(),
@ -88,16 +81,6 @@ fun RecentTabs(
onRecentTabClick = onRecentTabClick
)
}
is RecentTab.SearchGroup -> {
if (components.settings.searchTermTabGroupsAreEnabled) {
RecentSearchGroupItem(
searchTerm = tab.searchTerm,
tabId = tab.tabId,
count = tab.count,
onSearchGroupClick = onRecentSearchGroupClick
)
}
}
}
}
}
@ -185,81 +168,6 @@ private fun RecentTabItem(
}
}
/**
* A recent search group item.
*
* @param searchTerm The search term for the group.
* @param tabId The id of the last accessed tab in the group.
* @param count Count of how many tabs belongs to the group.
* @param onSearchGroupClick Invoked when the user clicks on a group.
*/
@Composable
private fun RecentSearchGroupItem(
searchTerm: String,
tabId: String,
count: Int,
onSearchGroupClick: (String) -> Unit = {}
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(112.dp)
.clickable { onSearchGroupClick(tabId) },
shape = RoundedCornerShape(8.dp),
backgroundColor = FirefoxTheme.colors.layer2,
elevation = 6.dp
) {
Row(
modifier = Modifier.padding(16.dp)
) {
Image(
painter = painterResource(id = R.drawable.ic_search_group_thumbnail),
contentDescription = null,
modifier = Modifier.size(108.dp, 80.dp),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween
) {
Text(
text = stringResource(R.string.recent_tabs_search_term, searchTerm),
color = FirefoxTheme.colors.textPrimary,
fontSize = 14.sp,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
)
Row {
Icon(
painter = painterResource(id = R.drawable.ic_all_tabs),
modifier = Modifier.size(18.dp),
contentDescription = null,
tint = when (isSystemInDarkTheme()) {
true -> FirefoxTheme.colors.textPrimary
false -> FirefoxTheme.colors.textSecondary
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.recent_tabs_search_term_count_2, count),
color = FirefoxTheme.colors.textSecondary,
fontSize = 12.sp,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
}
}
}
}
/**
* A recent tab image.
*

View File

@ -372,10 +372,6 @@ class SessionControlInteractor(
recentTabController.handleRecentTabClicked(tabId)
}
override fun onRecentSearchGroupClicked(tabId: String) {
recentTabController.handleRecentSearchGroupClicked(tabId)
}
override fun onRecentTabShowAllClicked() {
recentTabController.handleRecentTabShowAllClicked()
}

View File

@ -1,60 +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.tabstray
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.HistoryMetadataAction
import mozilla.components.browser.state.action.TabGroupAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.getGroupByName
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.MiddlewareContext
const val SEARCH_TERM_TAB_GROUPS = "searchTermTabGroups"
const val SEARCH_TERM_TAB_GROUPS_MIN_SIZE = 2
/**
* This [Middleware] manages tab groups for search terms.
*/
class SearchTermTabGroupMiddleware : Middleware<BrowserState, BrowserAction> {
override fun invoke(
context: MiddlewareContext<BrowserState, BrowserAction>,
next: (BrowserAction) -> Unit,
action: BrowserAction
) {
next(action)
when (action) {
is HistoryMetadataAction.SetHistoryMetadataKeyAction -> {
action.historyMetadataKey.searchTerm?.let { searchTerm ->
context.dispatch(
TabGroupAction.AddTabAction(SEARCH_TERM_TAB_GROUPS, searchTerm, action.tabId)
)
}
}
is HistoryMetadataAction.DisbandSearchGroupAction -> {
val group = context.state.tabPartitions[SEARCH_TERM_TAB_GROUPS]?.getGroupByName(action.searchTerm)
group?.let {
context.dispatch(TabGroupAction.RemoveTabGroupAction(SEARCH_TERM_TAB_GROUPS, it.id))
}
}
is TabListAction.RestoreAction -> {
action.tabs.forEach { tab ->
tab.state.historyMetadata?.searchTerm?.let { searchTerm ->
context.dispatch(
TabGroupAction.AddTabAction(SEARCH_TERM_TAB_GROUPS, searchTerm, tab.state.id)
)
}
}
}
else -> {
// no-op
}
}
}
}

View File

@ -153,7 +153,6 @@ class TabsTrayFragment : AppCompatDialogFragment() {
initialState = TabsTrayState(
selectedPage = initialPage,
mode = initialMode,
focusGroupTabId = args.focusGroupTabId
),
middlewares = listOf(
TabsTrayMiddleware()

View File

@ -19,10 +19,9 @@ import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsListItem
* @property mode Whether the browser tab list is in multi-select mode or not with the set of
* currently selected tabs.
* @property inactiveTabs The list of tabs are considered inactive.
* @property normalTabs The list of normal tabs that do not fall under [inactiveTabs] or search term groups.
* @property normalTabs The list of normal tabs that do not fall under [inactiveTabs].
* @property privateTabs The list of tabs that are [ContentState.private].
* @property syncing Whether the Synced Tabs feature should fetch the latest tabs from paired devices.
* @property focusGroupTabId The search group tab id to focus. Defaults to null.
*/
data class TabsTrayState(
val selectedPage: Page = Page.NormalTabs,
@ -32,7 +31,6 @@ data class TabsTrayState(
val privateTabs: List<TabSessionState> = emptyList(),
val syncedTabs: List<SyncedTabsListItem> = emptyList(),
val syncing: Boolean = false,
val focusGroupTabId: String? = null
) : State {
/**
@ -130,11 +128,6 @@ sealed class TabsTrayAction : Action {
*/
object SyncCompleted : TabsTrayAction()
/**
* Removes the [TabsTrayState.focusGroupTabId] of the [TabsTrayState].
*/
object ConsumeFocusGroupTabId : TabsTrayAction()
/**
* Updates the list of tabs in [TabsTrayState.inactiveTabs].
*/
@ -184,8 +177,6 @@ internal object TabsTrayReducer {
state.copy(syncing = true)
is TabsTrayAction.SyncCompleted ->
state.copy(syncing = false)
is TabsTrayAction.ConsumeFocusGroupTabId ->
state.copy(focusGroupTabId = null)
is TabsTrayAction.UpdateInactiveTabs ->
state.copy(inactiveTabs = action.tabs)
is TabsTrayAction.UpdateNormalTabs ->

View File

@ -84,10 +84,6 @@ class NormalBrowserPageViewHolder(
val inactiveTabsAreEnabled = containerView.context.settings().inactiveTabsAreEnabled
val selectedTab = browserStore.state.selectedNormalTab ?: return
// It's safe to read the state directly (i.e. won't cause bugs because of the store actions
// processed on a separate thread) instead of observing it because this value is only set during
// the initialState of the TabsTrayStore being created.
val focusGroupTabId = tabsTrayStore.state.focusGroupTabId
// Update tabs into the inactive adapter.
if (inactiveTabsAreEnabled && selectedTab.isNormalTabInactive(maxActiveTime)) {
@ -105,9 +101,7 @@ class NormalBrowserPageViewHolder(
}
}
}
}
if (focusGroupTabId.isNullOrEmpty()) {
} else {
// Updates tabs into the normal browser tabs adapter.
browserAdapter.observeFirstInsert {
val activeTabsList = browserStore.state.getNormalTrayTabs(inactiveTabsAreEnabled)

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M0,2.5C0,1.119 1.119,0 2.5,0H15.5C16.881,0 18,1.119 18,2.5V10.5C18,11.881 16.881,13 15.5,13H2.5C1.119,13 0,11.881 0,10.5V2.5ZM15.7,11.5L16.5,10.7V2.3L15.7,1.5H2.3L1.5,2.3V10.7L2.3,11.5H15.7ZM1.5,15.7L2.3,16.5H15.7L16.5,15.7V14.75C16.5,14.336 16.836,14 17.25,14C17.664,14 18,14.336 18,14.75V15.5C18,16.881 16.881,18 15.5,18H2.5C1.119,18 0,16.881 0,15.5V14.75C0,14.336 0.336,14 0.75,14C1.164,14 1.5,14.336 1.5,14.75V15.7Z"
android:fillColor="?attr/textSecondary"
android:fillType="evenOdd"/>
</vector>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="80dp"
android:viewportWidth="108"
android:viewportHeight="80">
<group>
<clip-path
android:pathData="M8,0L100,0A8,8 0,0 1,108 8L108,72A8,8 0,0 1,100 80L8,80A8,8 0,0 1,0 72L0,8A8,8 0,0 1,8 0z"/>
<path
android:pathData="M8,0L100,0A8,8 0,0 1,108 8L108,72A8,8 0,0 1,100 80L8,80A8,8 0,0 1,0 72L0,8A8,8 0,0 1,8 0z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="0"
android:startX="108"
android:endY="103.313"
android:endX="31.4721"
android:type="linear">
<item android:offset="0" android:color="#FF9059FF"/>
<item android:offset="1" android:color="#FF0250BB"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M100,80L8,80A8,8 0,0 1,0 72L0,8A8,8 0,0 1,8 0L100,0A8,8 0,0 1,108 8L108,72A8,8 0,0 1,100 80z"
android:strokeAlpha="0.5"
android:fillAlpha="0.5">
<aapt:attr name="android:fillColor">
<gradient
android:startY="80"
android:startX="54"
android:endY="0"
android:endX="54"
android:type="linear">
<item android:offset="0.0104167" android:color="#FF000000"/>
<item android:offset="0.567708" android:color="#FF81535C"/>
<item android:offset="1" android:color="#00C4C4C4"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M60.55,43.687C62.019,41.645 62.888,39.145 62.888,36.444C62.888,29.584 57.306,24 50.444,24C43.582,24 38,29.584 38,36.444C38,43.305 43.582,48.889 50.444,48.889C53.155,48.889 55.663,48.014 57.709,46.535L58.641,46.524L67.725,55.609C67.985,55.868 68.326,56 68.667,56C69.008,56 69.35,55.87 69.609,55.609C70.13,55.088 70.13,54.244 69.609,53.723L60.532,44.644L60.55,43.687ZM50.444,46.222C45.052,46.222 40.667,41.835 40.667,36.444C40.667,31.054 45.052,26.667 50.444,26.667C55.836,26.667 60.221,31.054 60.221,36.444C60.221,41.835 55.836,46.222 50.444,46.222Z"
android:fillColor="#F9F9FB"/>
</group>
</vector>

View File

@ -161,11 +161,6 @@
android:name="enterMultiselect"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="focusGroupTabId"
app:nullable="true"
android:defaultValue="@null"
app:argType="string" />
<argument
android:name="page"
android:defaultValue="NormalTabs"

View File

@ -120,10 +120,10 @@
<string name="recent_tabs_show_all_content_description_2">Show all recent tabs button</string>
<!-- Title for showing a group item in the 'Jump back in' section of the new tab
The first parameter is the search term that the user used. (for example: your search for "cat")-->
<string name="recent_tabs_search_term">Your search for \"%1$s\"</string>
<string name="recent_tabs_search_term" moz:RemovedIn="105" tools:ignore="UnusedResources">Your search for \"%1$s\"</string>
<!-- Text for the number of tabs in a group in the 'Jump back in' section of the new tab
%d is a placeholder for the number of sites in the group. This number will always be more than one. -->
<string name="recent_tabs_search_term_count_2">%d sites</string>
<string name="recent_tabs_search_term_count_2" moz:RemovedIn="105" tools:ignore="UnusedResources">%d sites</string>
<!-- Text for button in synced tab card that opens synced tabs tray -->
<string name="recent_tabs_see_all_synced_tabs_button_text">See all synced tabs</string>
<!-- Accessibility description for device icon used for recent synced tab -->

View File

@ -145,14 +145,12 @@ class AppStoreTest {
assertEquals(0, appStore.state.recentTabs.size)
// Add 2 RecentTabs to the AppStore
// A new SearchGroup already shown in history should hide the HistoryGroup.
val recentTab1: RecentTab.Tab = mockk()
val recentTab2 = RecentTab.SearchGroup(group2.title, "tabId", "url", null, 2)
val recentTabs: List<RecentTab> = listOf(recentTab1, recentTab2)
val recentTabs: List<RecentTab> = listOf(recentTab1)
appStore.dispatch(AppAction.RecentTabsChange(recentTabs)).join()
assertEquals(recentTabs, appStore.state.recentTabs)
assertEquals(listOf(group1, group3, highlight), appStore.state.recentHistory)
assertEquals(listOf(group1, group2, group3, highlight), appStore.state.recentHistory)
}
@Test
@ -256,13 +254,12 @@ class AppStoreTest {
assertEquals(0, appStore.state.recentHistory.size)
assertEquals(Mode.Normal, appStore.state.mode)
val recentGroup = RecentTab.SearchGroup("testSearchTerm", "id", "url", null, 3)
val collections: List<TabCollection> = listOf(mockk())
val topSites: List<TopSite> = listOf(mockk(), mockk())
val recentTabs: List<RecentTab> = listOf(mockk(), recentGroup, mockk())
val recentTabs: List<RecentTab> = listOf(mockk(), mockk())
val recentBookmarks: List<RecentBookmark> = listOf(mockk(), mockk())
val group1 = RecentHistoryGroup(title = "test One")
val group2 = RecentHistoryGroup(title = recentGroup.searchTerm.lowercase())
val group2 = RecentHistoryGroup(title = "testSearchTerm")
val group3 = RecentHistoryGroup(title = "test two")
val highlight = RecentHistoryHighlight(group2.title, "")
val recentHistory: List<RecentlyVisitedItem> = listOf(group1, group2, group3, highlight)
@ -283,7 +280,7 @@ class AppStoreTest {
assertEquals(topSites, appStore.state.topSites)
assertEquals(recentTabs, appStore.state.recentTabs)
assertEquals(recentBookmarks, appStore.state.recentBookmarks)
assertEquals(listOf(group1, group3, highlight), appStore.state.recentHistory)
assertEquals(listOf(group1, group2, group3, highlight), appStore.state.recentHistory)
assertEquals(Mode.Private, appStore.state.mode)
}

View File

@ -21,7 +21,6 @@ import org.mozilla.fenix.home.pocket.POCKET_STORIES_DEFAULT_CATEGORY_NAME
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesSelectedCategory
import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.utils.Settings
import java.util.concurrent.TimeUnit
import kotlin.random.Random
@ -498,24 +497,6 @@ class AppStateTest {
)
}
@Test
fun `GIVEN recentTabs contains a SearchGroup WHEN recentSearchGroup is called THEN return the group`() {
val searchGroup: RecentTab.SearchGroup = mockk()
val normalTab: RecentTab.Tab = mockk()
val state = AppState(recentTabs = listOf(normalTab, searchGroup))
assertEquals(searchGroup, state.recentSearchGroup)
}
@Test
fun `GIVEN recentTabs does not contains SearchGroup WHEN recentSearchGroup is called THEN return null`() {
val normalTab1: RecentTab.Tab = mockk()
val normalTab2: RecentTab.Tab = mockk()
val state = AppState(recentTabs = listOf(normalTab1, normalTab2))
assertNull(state.recentSearchGroup)
}
@Test
fun `GIVEN recent tabs disabled in settings WHEN checking to show tabs THEN section should not be shown`() {
val settings = mockk<Settings> {

View File

@ -8,8 +8,6 @@ import io.mockk.every
import io.mockk.mockk
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.LastMediaAccessState
import mozilla.components.browser.state.state.TabGroup
import mozilla.components.browser.state.state.TabPartition
import mozilla.components.browser.state.state.createTab
import mozilla.components.concept.storage.HistoryMetadataKey
import org.junit.Assert.assertEquals
@ -17,7 +15,6 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.tabstray.SEARCH_TERM_TAB_GROUPS
import org.mozilla.fenix.utils.Settings
class BrowserStateTest {
@ -160,74 +157,6 @@ class BrowserStateTest {
assertEquals(searchGroupTab, (result[0] as RecentTab.Tab).state)
}
@Test
fun `GIVEN only normal tabs from a search group are open WHEN recentTabs is called THEN return only the tab group`() {
val searchGroupTab1 = createTab(
url = "https://www.mozilla.org",
id = "1",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test",
referrerUrl = "https://www.mozilla.org"
)
)
val searchGroupTab2 = createTab(
url = "https://www.mozilla.org",
id = "2",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test",
referrerUrl = "https://www.mozilla.org"
)
)
val tabGroup = listOf(TabGroup("Test", "", listOf(searchGroupTab1.id, searchGroupTab2.id)))
val browserState = BrowserState(
tabs = listOf(searchGroupTab1, searchGroupTab2),
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, tabGroup))),
selectedTabId = searchGroupTab1.id
)
val result = browserState.asRecentTabs()
assertEquals(1, result.size)
assert(result[0] is RecentTab.SearchGroup)
assertEquals(searchGroupTab1.historyMetadata?.searchTerm, (result[0] as RecentTab.SearchGroup).searchTerm)
assertEquals(searchGroupTab1.id, (result[0] as RecentTab.SearchGroup).tabId)
assertEquals(searchGroupTab1.content.url, (result[0] as RecentTab.SearchGroup).url)
assertEquals(searchGroupTab1.content.thumbnail, (result[0] as RecentTab.SearchGroup).thumbnail)
assertEquals(2, (result[0] as RecentTab.SearchGroup).count)
}
@Test
fun `GIVEN tabs with different search terms are opened WHEN recentTabs is called THEN return the most recent tab and tab group`() {
val searchGroupTab = createTab(
url = "https://www.mozilla.org",
id = "1",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test",
referrerUrl = "https://www.mozilla.org"
)
)
val otherTab = createTab(url = "https://www.mozilla.org/firefox", id = "2")
val browserState = BrowserState(
tabs = listOf(searchGroupTab, otherTab, searchGroupTab),
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(TabGroup("Test", "", listOf("1", "3")))))),
selectedTabId = searchGroupTab.id
)
val result = browserState.asRecentTabs()
assertEquals(2, result.size)
assertEquals(otherTab, (result[0] as RecentTab.Tab).state)
assert(result[1] is RecentTab.SearchGroup)
assertEquals("Test", (result[1] as RecentTab.SearchGroup).searchTerm)
assertEquals(searchGroupTab.id, (result[1] as RecentTab.SearchGroup).tabId)
assertEquals(searchGroupTab.content.url, (result[1] as RecentTab.SearchGroup).url)
assertEquals(searchGroupTab.content.thumbnail, (result[1] as RecentTab.SearchGroup).thumbnail)
assertEquals(2, (result[1] as RecentTab.SearchGroup).count)
}
@Test
fun `GIVEN the selected tab is a normal tab and tab group with one tab exists WHEN asRecentTabs is called THEN return only the normal tab`() {
val selectedTab = createTab(url = "url", id = "3")
@ -260,46 +189,6 @@ class BrowserStateTest {
assertEquals(selectedTab, (result[0] as RecentTab.Tab).state)
}
@Test
fun `GIVEN the selected tab is a normal tab and tab group with two tabs exists WHEN asRecentTabs is called THEN return a list of these tabs`() {
val selectedTab = createTab(url = "url", id = "3")
val searchGroupTab1 = createTab(
url = "https://www.mozilla.org",
id = "4",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test",
referrerUrl = "https://www.mozilla.org"
)
)
val searchGroupTab2 = createTab(
url = "https://www.mozilla.org",
id = "5",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test",
referrerUrl = "https://www.mozilla.org"
)
)
val tabGroup = listOf(TabGroup("Test", "", listOf(searchGroupTab1.id, searchGroupTab2.id)))
val browserState = BrowserState(
tabs = listOf(mockk(relaxed = true), selectedTab, searchGroupTab1, searchGroupTab1),
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, tabGroup))),
selectedTabId = selectedTab.id
)
val result = browserState.asRecentTabs()
assertEquals(2, result.size)
assertEquals(selectedTab, (result[0] as RecentTab.Tab).state)
assert(result[1] is RecentTab.SearchGroup)
assertEquals(searchGroupTab1.historyMetadata?.searchTerm, (result[1] as RecentTab.SearchGroup).searchTerm)
assertEquals(searchGroupTab1.id, (result[1] as RecentTab.SearchGroup).tabId)
assertEquals(searchGroupTab1.content.url, (result[1] as RecentTab.SearchGroup).url)
assertEquals(searchGroupTab1.content.thumbnail, (result[1] as RecentTab.SearchGroup).thumbnail)
assertEquals(2, (result[1] as RecentTab.SearchGroup).count)
}
@Test
fun `GIVEN only private tabs and a private one selected WHEN lastOpenedNormalTab is called THEN return null`() {
val selectedPrivateTab = createTab(url = "url", id = "1", private = true)
@ -467,38 +356,4 @@ class BrowserStateTest {
assertEquals(2, result.size)
assertTrue(result.containsAll(listOf(normalTab1, normalTab3)))
}
@Test
fun `GIVEN tabs exist with search terms WHEN lastTabGroup is called THEN return the last accessed TabGroup`() {
val tab1 = createTab(url = "url1", id = "id1", searchTerms = "test1", lastAccess = 10)
val tab2 = createTab(url = "url2", id = "id2", searchTerms = "test1", lastAccess = 11)
val tab3 = createTab(url = "url3", id = "id3", searchTerms = "test3", lastAccess = 100)
val tab4 = createTab(url = "url4", id = "id4", searchTerms = "test3", lastAccess = 111)
val tab5 = createTab(url = "url5", id = "id5", searchTerms = "test5", lastAccess = 1000)
val tab6 = createTab(url = "url6", id = "id6", searchTerms = "test5", lastAccess = 1111)
val tabGroup1 = TabGroup("test1", "", listOf(tab1.id, tab2.id))
val tabGroup2 = TabGroup("test3", "", listOf(tab3.id, tab4.id))
val tabGroup3 = TabGroup("test5", "", listOf(tab5.id, tab6.id))
val browserState = BrowserState(
tabs = listOf(tab1, tab2, tab3, tab4, tab5, tab6),
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(tabGroup1, tabGroup2, tabGroup3))))
)
val expected = TabGroup("test5", "", listOf(tab5.id, tab6.id))
val result = browserState.lastTabGroup
assertEquals(expected, result)
}
@Test
fun `GIVEN no tabs exist with search terms WHEN lastTabGroup is called THEN return the last accessed TabGroup`() {
val tab1 = createTab(url = "url1", id = "id1", lastAccess = 10)
val tab2 = createTab(url = "url2", id = "id2", lastAccess = 11)
val browserState = BrowserState(tabs = listOf(tab1, tab2))
val result = browserState.lastTabGroup
assertNull(result)
}
}

View File

@ -1,34 +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.every
import io.mockk.mockk
import mozilla.components.browser.state.state.TabSessionState
import org.junit.Assert.assertEquals
import org.junit.Test
import org.mozilla.fenix.home.recenttabs.RecentTab
class RecentTabsTest {
@Test
fun `Test filtering out tab`() {
val filteredId = "id"
val mockSessionState: TabSessionState = mockk()
every { mockSessionState.id } returns filteredId
val tab = RecentTab.Tab(mockSessionState)
val searchGroup = RecentTab.SearchGroup(
tabId = filteredId,
searchTerm = "",
url = "",
thumbnail = null,
count = 0
)
val recentTabs = listOf(tab, searchGroup)
val result = recentTabs.filterOutTab(tab)
assertEquals(listOf(searchGroup), result)
}
}

View File

@ -4,7 +4,6 @@
package org.mozilla.fenix.home
import android.graphics.Bitmap
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.action.ContentAction.UpdateIconAction
@ -13,12 +12,9 @@ import mozilla.components.browser.state.action.MediaSessionAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.LastMediaAccessState
import mozilla.components.browser.state.state.TabGroup
import mozilla.components.browser.state.state.TabPartition
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.mediasession.MediaSession
import mozilla.components.concept.storage.HistoryMetadataKey
import mozilla.components.feature.media.middleware.LastMediaAccessMiddleware
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.libstate.ext.waitUntilIdle
@ -38,7 +34,6 @@ import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recenttabs.RecentTabsListFeature
import org.mozilla.fenix.tabstray.SEARCH_TERM_TAB_GROUPS
class RecentTabsListFeatureTest {
@ -388,245 +383,4 @@ class RecentTabsListFeatureTest {
assertTrue(appStore.state.recentTabs[0] is RecentTab.Tab)
assertEquals(selectedTab, (appStore.state.recentTabs[0] as RecentTab.Tab).state)
}
@Test
fun `GIVEN only tabs from a search group WHEN the feature starts THEN dispatch the selected tab group as a recent tab list`() {
val tab1 = createTab(
url = "https://www.mozilla.org",
id = "1",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tab2 = createTab(
url = "https://www.mozilla.org",
id = "2",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tabs = listOf(tab1, tab2)
val tabGroup = TabGroup("Test search term", "", listOf(tab1.id, tab2.id))
val browserStore = BrowserStore(
BrowserState(
tabs = tabs,
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(tabGroup)))),
selectedTabId = "1"
)
)
val feature = RecentTabsListFeature(
browserStore = browserStore,
appStore = appStore
)
feature.start()
appStore.waitUntilIdle()
assertEquals(1, appStore.state.recentTabs.size)
val searchGroup = (appStore.state.recentTabs[0] as RecentTab.SearchGroup)
assertEquals(searchGroup.searchTerm, "Test search term")
assertEquals(searchGroup.tabId, "1")
assertEquals(searchGroup.url, "https://www.mozilla.org")
assertEquals(searchGroup.thumbnail, null)
assertEquals(searchGroup.count, 2)
}
@Test
fun `GIVEN tabs with different search terms are opened WHEN the feature starts THEN dispatch the last active tab and last active search group as recent tabs list`() {
val tab1 = createTab(
url = "https://www.mozilla.org",
id = "1",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tab2 = createTab(
url = "https://www.mozilla.org",
id = "2",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "Test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tab3 = createTab(url = "https://www.mozilla.org/firefox", id = "3")
val tabs = listOf(tab1, tab2, tab3)
val tabGroup = TabGroup("Test search term", "", listOf(tab1.id, tab2.id))
val browserStore = BrowserStore(
BrowserState(
tabs = tabs,
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(tabGroup)))),
selectedTabId = "1"
)
)
val feature = RecentTabsListFeature(
browserStore = browserStore,
appStore = appStore
)
feature.start()
appStore.waitUntilIdle()
assertEquals(2, appStore.state.recentTabs.size)
assertTrue(appStore.state.recentTabs[0] is RecentTab.Tab)
assertEquals(tab3, (appStore.state.recentTabs[0] as RecentTab.Tab).state)
val searchGroup = (appStore.state.recentTabs[1] as RecentTab.SearchGroup)
assertEquals(searchGroup.searchTerm, "Test search term")
assertEquals(searchGroup.tabId, "1")
assertEquals(searchGroup.url, "https://www.mozilla.org")
assertEquals(searchGroup.thumbnail, null)
assertEquals(searchGroup.count, 2)
}
@Test
fun `GIVEN a tab group with one tab and a selected tab WHEN the feature starts THEN dispatch selected tab as a recent tab list`() {
val tab1 = createTab(
url = "https://www.mozilla.org",
id = "1"
)
val tab2 = createTab(
url = "https://www.mozilla.org",
id = "2",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tabs = listOf(tab1, tab2)
val browserStore = BrowserStore(
BrowserState(
tabs = tabs,
selectedTabId = "1"
)
)
val feature = RecentTabsListFeature(
browserStore = browserStore,
appStore = appStore
)
feature.start()
appStore.waitUntilIdle()
assertEquals(1, appStore.state.recentTabs.size)
assertTrue(appStore.state.recentTabs[0] is RecentTab.Tab)
assertEquals(tab1, (appStore.state.recentTabs[0] as RecentTab.Tab).state)
}
@Test
fun `GIVEN a tab group with two tabs and a selected tab WHEN the feature starts THEN dispatch both the selected tab and the selected tab group as a recent tab list`() {
val tab1 = createTab(
url = "https://www.mozilla.org",
id = "1"
)
val tab2 = createTab(
url = "https://www.mozilla.org",
id = "2",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tab3 = createTab(
url = "https://www.mozilla.org",
id = "3",
historyMetadata = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
)
val tabs = listOf(tab1, tab2, tab3)
val tabGroup = TabGroup("test search term", "", listOf(tab2.id, tab3.id))
val browserStore = BrowserStore(
BrowserState(
tabs = tabs,
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(tabGroup)))),
selectedTabId = "1"
)
)
val feature = RecentTabsListFeature(
browserStore = browserStore,
appStore = appStore
)
feature.start()
appStore.waitUntilIdle()
assertEquals(2, appStore.state.recentTabs.size)
assertTrue(appStore.state.recentTabs[0] is RecentTab.Tab)
assertEquals(tab1, (appStore.state.recentTabs[0] as RecentTab.Tab).state)
val searchGroup = (appStore.state.recentTabs[1] as RecentTab.SearchGroup)
assertEquals(searchGroup.searchTerm, "test search term")
assertEquals(searchGroup.tabId, "2")
assertEquals(searchGroup.url, "https://www.mozilla.org")
assertEquals(searchGroup.thumbnail, null)
assertEquals(searchGroup.count, 2)
}
@Test
fun `GIVEN a valid inProgressMediaTabId, selected tab and tab group exists WHEN the feature starts THEN dispatch all as as a recent tabs list`() {
val mediaTab = createTab(
url = "https://mozilla.com", id = "42",
lastMediaAccessState = LastMediaAccessState("https://mozilla.com", 123)
)
val selectedTab = createTab("https://mozilla.com", id = "43")
val historyMetadataKey = HistoryMetadataKey(
url = "https://www.mozilla.org",
searchTerm = "test search term",
referrerUrl = "https://www.mozilla.org"
)
val thumbnail = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
val searchTermTab1 = createTab(
url = "https://www.mozilla.org",
id = "44",
thumbnail = thumbnail,
historyMetadata = historyMetadataKey
)
val searchTermTab2 = createTab(
url = "https://www.mozilla.org",
id = "45",
thumbnail = thumbnail,
historyMetadata = historyMetadataKey
)
val searchTermTabGroup = TabGroup(historyMetadataKey.searchTerm!!, "", listOf(searchTermTab1.id, searchTermTab2.id))
val browserStore = BrowserStore(
BrowserState(
tabs = listOf(mediaTab, selectedTab, searchTermTab1, searchTermTab2),
tabPartitions = mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(searchTermTabGroup)))),
selectedTabId = "43"
)
)
val feature = RecentTabsListFeature(
browserStore = browserStore,
appStore = appStore
)
feature.start()
appStore.waitUntilIdle()
assertEquals(2, appStore.state.recentTabs.size)
assertTrue(appStore.state.recentTabs[0] is RecentTab.Tab)
assertEquals(selectedTab, (appStore.state.recentTabs[0] as RecentTab.Tab).state)
val searchGroup = (appStore.state.recentTabs[1] as RecentTab.SearchGroup)
assertEquals(searchGroup.searchTerm, "test search term")
assertEquals(searchGroup.tabId, "44")
assertEquals(searchGroup.url, "https://www.mozilla.org")
assertEquals(searchGroup.thumbnail, thumbnail)
assertEquals(searchGroup.count, 2)
}
}

View File

@ -162,13 +162,6 @@ class SessionControlInteractorTest {
verify { recentTabController.handleRecentTabClicked(tabId) }
}
@Test
fun onRecentSearchGroupClicked() {
val tabId = "tabId"
interactor.onRecentSearchGroupClicked(tabId)
verify { recentTabController.handleRecentSearchGroupClicked(tabId) }
}
@Test
fun onRecentTabShowAllClicked() {
interactor.onRecentTabShowAllClicked()

View File

@ -28,7 +28,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.GleanMetrics.RecentTabs
import org.mozilla.fenix.GleanMetrics.SearchTerms
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@ -163,21 +162,4 @@ class RecentTabControllerTest {
assertNotNull(RecentTabs.showAllClicked.testGetValue())
}
@Test
fun `WHEN handleRecentSearchGroupClicked is called THEN navigate to the tabsTrayFragment and record the correct metric`() {
assertNull(SearchTerms.jumpBackInGroupTapped.testGetValue())
controller.handleRecentSearchGroupClicked("1")
verify {
navController.navigate(
match<NavDirections> {
it.actionId == R.id.action_global_tabsTrayFragment &&
it.arguments["focusGroupTabId"] == "1"
}
)
}
assertNotNull(SearchTerms.jumpBackInGroupTapped.testGetValue())
}
}

View File

@ -1,104 +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.tabstray
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.HistoryMetadataAction
import mozilla.components.browser.state.action.TabGroupAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.engine.EngineMiddleware
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabGroup
import mozilla.components.browser.state.state.TabPartition
import mozilla.components.browser.state.state.recover.RecoverableTab
import mozilla.components.browser.state.state.recover.TabState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.storage.HistoryMetadataKey
import mozilla.components.lib.state.MiddlewareContext
import org.junit.Before
import org.junit.Test
class SearchTermTabGroupMiddlewareTest {
private lateinit var store: BrowserStore
private lateinit var searchTermTabGroupMiddleware: SearchTermTabGroupMiddleware
@Before
fun setUp() {
searchTermTabGroupMiddleware = SearchTermTabGroupMiddleware()
store = BrowserStore(
middleware = listOf(searchTermTabGroupMiddleware) + EngineMiddleware.create(engine = mockk()),
initialState = BrowserState()
)
}
@Test
fun `WHEN invoking with set history metadata key action THEN dispatch add tab action`() {
val context: MiddlewareContext<BrowserState, BrowserAction> = mockk()
val next: (BrowserAction) -> Unit = {}
every { context.dispatch(any()) } returns Unit
searchTermTabGroupMiddleware.invoke(
context,
next,
HistoryMetadataAction.SetHistoryMetadataKeyAction("tabId", HistoryMetadataKey("url", "search term", "url"))
)
verify { context.dispatch(TabGroupAction.AddTabAction(SEARCH_TERM_TAB_GROUPS, "search term", "tabId")) }
}
@Test
fun `WHEN invoking with disband search group action THEN dispatch remove tab group action`() {
val context: MiddlewareContext<BrowserState, BrowserAction> = mockk()
val next: (BrowserAction) -> Unit = {}
val state: BrowserState = mockk()
val tabPartitions =
mapOf(Pair(SEARCH_TERM_TAB_GROUPS, TabPartition(SEARCH_TERM_TAB_GROUPS, listOf(TabGroup("testId", "search term", listOf("tab1"))))))
every { context.dispatch(any()) } returns Unit
every { context.state } returns state
every { state.tabPartitions } returns tabPartitions
searchTermTabGroupMiddleware.invoke(
context,
next,
HistoryMetadataAction.DisbandSearchGroupAction("search term")
)
verify { context.dispatch(TabGroupAction.RemoveTabGroupAction(SEARCH_TERM_TAB_GROUPS, "testId")) }
}
@Test
fun `WHEN invoking with restore action THEN dispatch add tab action`() {
val context: MiddlewareContext<BrowserState, BrowserAction> = mockk()
val next: (BrowserAction) -> Unit = {}
every { context.dispatch(any()) } returns Unit
searchTermTabGroupMiddleware.invoke(
context,
next,
TabListAction.RestoreAction(
listOf(
RecoverableTab(
engineSessionState = null,
state = TabState(
id = "testId",
url = "url",
historyMetadata = HistoryMetadataKey("url", "search term", "url")
)
)
),
restoreLocation = TabListAction.RestoreAction.RestoreLocation.BEGINNING
)
)
verify { context.dispatch(TabGroupAction.AddTabAction(SEARCH_TERM_TAB_GROUPS, "search term", "testId")) }
}
}

View File

@ -10,18 +10,6 @@ import org.junit.Test
import org.mozilla.fenix.tabstray.syncedtabs.getFakeSyncedTabList
class TabsTrayStoreReducerTest {
@Test
fun `GIVEN focusGroupTabId WHEN ConsumeFocusGroupTabIdAction THEN focusGroupTabId must be consumed`() {
val initialState = TabsTrayState(focusGroupTabId = "id")
val expectedState = initialState.copy(focusGroupTabId = null)
val resultState = TabsTrayReducer.reduce(
initialState,
TabsTrayAction.ConsumeFocusGroupTabId
)
assertEquals(expectedState, resultState)
}
@Test
fun `WHEN UpdateInactiveTabs THEN inactive tabs are added`() {