1
0
Fork 0

For #1048 - Add ability to view tab history by long-pressing the back or forward button.

master
Kainalu Hagiwara 2020-07-14 16:20:42 -07:00 committed by Jeff Boek
parent 2a0a11f740
commit 921b16233b
19 changed files with 464 additions and 9 deletions

View File

@ -6,11 +6,14 @@ package org.mozilla.fenix
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.StrictMode import android.os.StrictMode
import android.text.format.DateUtils import android.text.format.DateUtils
import android.util.AttributeSet import android.util.AttributeSet
import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.ViewConfiguration
import android.view.WindowManager import android.view.WindowManager
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.annotation.IdRes import androidx.annotation.IdRes
@ -30,6 +33,8 @@ import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.search.SearchEngine
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
@ -139,6 +144,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
) )
} }
// See onKeyDown for why this is necessary
private var backLongPressJob: Job? = null
private lateinit var navigationToolbar: Toolbar private lateinit var navigationToolbar: Toolbar
final override fun onCreate(savedInstanceState: Bundle?) { final override fun onCreate(savedInstanceState: Bundle?) {
@ -349,6 +357,50 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
super.onBackPressed() super.onBackPressed()
} }
private fun isAndroidN(): Boolean =
Build.VERSION.SDK_INT == Build.VERSION_CODES.N || Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1
private fun handleBackLongPress(): Boolean {
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
if (it is OnBackLongPressedListener && it.onBackLongPressed()) {
return true
}
}
return false
}
final override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
// Inspired by https://searchfox.org/mozilla-esr68/source/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java#584-613
// Android N has broken passing onKeyLongPress events for the back button, so we
// instead implement the long press behavior ourselves
// - For short presses, we cancel the callback in onKeyUp
// - For long presses, the normal keypress is marked as cancelled, hence won't be handled elsewhere
// (but Android still provides the haptic feedback), and the long press action is run
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
backLongPressJob = lifecycleScope.launch {
delay(ViewConfiguration.getLongPressTimeout().toLong())
handleBackLongPress()
}
}
return super.onKeyDown(keyCode, event)
}
final override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
backLongPressJob?.cancel()
}
return super.onKeyUp(keyCode, event)
}
final override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
// onKeyLongPress is broken in Android N so we don't handle back button long presses here
// for N. The version check ensures we don't handle back button long presses twice.
if (!isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
return handleBackLongPress()
}
return super.onKeyLongPress(keyCode, event)
}
final override fun onUserLeaveHint() { final override fun onUserLeaveHint() {
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
if (it is UserInteractionHandler && it.onHomePressed()) { if (it is UserInteractionHandler && it.onHomePressed()) {

View File

@ -0,0 +1,21 @@
/* 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
/**
* Interface for features and fragments that want to handle long presses of the system back button
*/
interface OnBackLongPressedListener {
/**
* Called when the system back button is long pressed.
*
* Note: This cannot be called when gesture navigation is enabled on Android 10+ due to system
* limitations.
*
* @return true if the event was handled
*/
fun onBackLongPressed(): Boolean
}

View File

@ -77,6 +77,7 @@ import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.OnBackLongPressedListener
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
@ -116,7 +117,8 @@ import java.lang.ref.WeakReference
*/ */
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@Suppress("TooManyFunctions", "LargeClass") @Suppress("TooManyFunctions", "LargeClass")
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer { abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer,
OnBackLongPressedListener {
private lateinit var browserFragmentStore: BrowserFragmentStore private lateinit var browserFragmentStore: BrowserFragmentStore
private lateinit var browserAnimator: BrowserAnimator private lateinit var browserAnimator: BrowserAnimator
@ -757,6 +759,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
removeSessionIfNeeded() removeSessionIfNeeded()
} }
override fun onBackLongPressed(): Boolean {
findNavController().navigate(R.id.action_global_tabHistoryDialogFragment)
return true
}
/** /**
* Saves the external app session ID to be restored later in [onViewStateRestored]. * Saves the external app session ID to be restored later in [onViewStateRestored].
*/ */

View File

@ -170,7 +170,13 @@ class DefaultBrowserToolbarController(
Do exhaustive when (item) { Do exhaustive when (item) {
ToolbarMenu.Item.Back -> sessionUseCases.goBack.invoke(currentSession) ToolbarMenu.Item.Back -> sessionUseCases.goBack.invoke(currentSession)
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession) is ToolbarMenu.Item.Forward -> {
if (item.viewHistory) {
navController.navigate(R.id.action_global_tabHistoryDialogFragment)
} else {
sessionUseCases.goForward.invoke(currentSession)
}
}
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession) ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession) ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically { ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically {
@ -333,7 +339,7 @@ class DefaultBrowserToolbarController(
private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) { private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) {
val eventItem = when (item) { val eventItem = when (item) {
ToolbarMenu.Item.Back -> Event.BrowserMenuItemTapped.Item.BACK ToolbarMenu.Item.Back -> Event.BrowserMenuItemTapped.Item.BACK
ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD is ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
ToolbarMenu.Item.Reload -> Event.BrowserMenuItemTapped.Item.RELOAD ToolbarMenu.Item.Reload -> Event.BrowserMenuItemTapped.Item.RELOAD
ToolbarMenu.Item.Stop -> Event.BrowserMenuItemTapped.Item.STOP ToolbarMenu.Item.Stop -> Event.BrowserMenuItemTapped.Item.STOP
ToolbarMenu.Item.Settings -> Event.BrowserMenuItemTapped.Item.SETTINGS ToolbarMenu.Item.Settings -> Event.BrowserMenuItemTapped.Item.SETTINGS

View File

@ -82,9 +82,10 @@ class DefaultToolbarMenu(
session?.canGoForward ?: true session?.canGoForward ?: true
}, },
secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context), secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context),
disableInSecondaryState = true disableInSecondaryState = true,
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
) { ) {
onItemTapped.invoke(ToolbarMenu.Item.Forward) onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
} }
val refresh = BrowserMenuItemToolbar.TwoStateButton( val refresh = BrowserMenuItemToolbar.TwoStateButton(

View File

@ -14,7 +14,7 @@ interface ToolbarMenu {
object FindInPage : Item() object FindInPage : Item()
object Share : Item() object Share : Item()
object Back : Item() object Back : Item()
object Forward : Item() data class Forward(val viewHistory: Boolean) : Item()
object Reload : Item() object Reload : Item()
object Stop : Item() object Stop : Item()
object OpenInFenix : Item() object OpenInFenix : Item()

View File

@ -75,9 +75,10 @@ class CustomTabToolbarMenu(
R.attr.disabled, R.attr.disabled,
context context
), ),
disableInSecondaryState = true disableInSecondaryState = true,
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
) { ) {
onItemTapped.invoke(ToolbarMenu.Item.Forward) onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
} }
val refresh = BrowserMenuItemToolbar.TwoStateButton( val refresh = BrowserMenuItemToolbar.TwoStateButton(

View File

@ -0,0 +1,40 @@
/* 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.tabhistory
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
data class TabHistoryItem(
val title: String,
val url: String,
val index: Int,
val isSelected: Boolean
)
class TabHistoryAdapter(
private val interactor: TabHistoryViewInteractor
) : RecyclerView.Adapter<TabHistoryViewHolder>() {
var historyList: List<TabHistoryItem> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabHistoryViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.history_list_item, parent, false)
return TabHistoryViewHolder(view, interactor)
}
override fun onBindViewHolder(holder: TabHistoryViewHolder, position: Int) {
holder.bind(historyList[position])
}
override fun getItemCount(): Int = historyList.size
}

View File

@ -0,0 +1,24 @@
/* 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.tabhistory
import androidx.navigation.NavController
import mozilla.components.feature.session.SessionUseCases
import org.mozilla.fenix.R
interface TabHistoryController {
fun handleGoToHistoryItem(item: TabHistoryItem)
}
class DefaultTabHistoryController(
private val navController: NavController,
private val goToHistoryIndexUseCase: SessionUseCases.GoToHistoryIndexUseCase
) : TabHistoryController {
override fun handleGoToHistoryItem(item: TabHistoryItem) {
navController.popBackStack(R.id.browserFragment, false)
goToHistoryIndexUseCase.invoke(item.index)
}
}

View File

@ -0,0 +1,56 @@
/* 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.tabhistory
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_tab_history_dialog.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.lib.state.ext.consumeFrom
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.requireComponents
class TabHistoryDialogFragment : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.BottomSheet)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_tab_history_dialog, container, false)
@ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val controller = DefaultTabHistoryController(
navController = findNavController(),
goToHistoryIndexUseCase = requireComponents.useCases.sessionUseCases.goToHistoryIndex
)
val tabHistoryView = TabHistoryView(
container = tabHistoryLayout,
expandDialog = ::expand,
interactor = TabHistoryInteractor(controller)
)
consumeFrom(requireComponents.core.store) {
tabHistoryView.updateState(it)
}
}
private fun expand() {
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}

View File

@ -0,0 +1,14 @@
/* 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.tabhistory
class TabHistoryInteractor(
private val controller: TabHistoryController
) : TabHistoryViewInteractor {
override fun goToHistoryItem(item: TabHistoryItem) {
controller.handleGoToHistoryItem(item)
}
}

View File

@ -0,0 +1,79 @@
/* 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.tabhistory
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_tabhistory.*
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.state.BrowserState
import org.mozilla.fenix.R
interface TabHistoryViewInteractor {
/**
* Jump to a specific index in the tab's history.
*/
fun goToHistoryItem(item: TabHistoryItem)
}
class TabHistoryView(
private val container: ViewGroup,
private val expandDialog: () -> Unit,
interactor: TabHistoryViewInteractor
) : LayoutContainer {
override val containerView: View?
get() = container
val view: View = LayoutInflater.from(container.context)
.inflate(R.layout.component_tabhistory, container, true)
private val adapter = TabHistoryAdapter(interactor)
private val layoutManager = object : LinearLayoutManager(view.context) {
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
currentIndex?.let { index ->
// Force expansion of the dialog, otherwise scrolling to the current history item
// won't work when its position is near the bottom of the recyclerview.
expandDialog.invoke()
// Also, attempt to center the current history item.
val itemView = tabHistoryRecyclerView.findViewHolderForLayoutPosition(
findFirstCompletelyVisibleItemPosition()
)?.itemView
val offset = tabHistoryRecyclerView.height / 2 - (itemView?.height ?: 0) / 2
scrollToPositionWithOffset(index, offset)
}
}
}.apply {
reverseLayout = true
}
private var currentIndex: Int? = null
init {
tabHistoryRecyclerView.adapter = adapter
tabHistoryRecyclerView.layoutManager = layoutManager
}
fun updateState(state: BrowserState) {
state.selectedTab?.content?.history?.let { historyState ->
currentIndex = historyState.currentIndex
val items = historyState.items.mapIndexed { index, historyItem ->
TabHistoryItem(
title = historyItem.title,
url = historyItem.uri,
index = index,
isSelected = index == historyState.currentIndex
)
}
adapter.historyList = items
}
}
}

View File

@ -0,0 +1,34 @@
/* 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.tabhistory
import android.view.View
import androidx.core.text.bold
import androidx.core.text.buildSpannedString
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.history_list_item.view.*
class TabHistoryViewHolder(
private val view: View,
private val interactor: TabHistoryViewInteractor
) : RecyclerView.ViewHolder(view) {
fun bind(item: TabHistoryItem) {
view.history_layout.overflowView.isVisible = false
view.history_layout.urlView.text = item.url
view.history_layout.loadFavicon(item.url)
view.history_layout.titleView.text = if (item.isSelected) {
buildSpannedString {
bold { append(item.title) }
}
} else {
item.title
}
view.setOnClickListener { interactor.goToHistoryItem(item) }
}
}

View File

@ -0,0 +1,31 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tabHistoryWrapper"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="@+id/handle"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginTop="8dp"
android:background="@color/secondary_text_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.1" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabHistoryRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="19dp"
tools:listitem="@layout/history_list_item" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,10 @@
<?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/. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tabHistoryLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -102,6 +102,9 @@
<action <action
android:id="@+id/action_global_savedLoginsAuthFragment" android:id="@+id/action_global_savedLoginsAuthFragment"
app:destination="@id/savedLoginsAuthFragment" /> app:destination="@id/savedLoginsAuthFragment" />
<action
android:id="@+id/action_global_tabHistoryDialogFragment"
app:destination="@id/tabHistoryDialogFragment" />
<dialog <dialog
android:id="@+id/tabTrayDialogFragment" android:id="@+id/tabTrayDialogFragment"
@ -859,4 +862,7 @@
app:argType="string" app:argType="string"
app:nullable="true" /> app:nullable="true" />
</fragment> </fragment>
<dialog
android:id="@+id/tabHistoryDialogFragment"
android:name="org.mozilla.fenix.tabhistory.TabHistoryDialogFragment" />
</navigation> </navigation>

View File

@ -220,7 +220,7 @@ class DefaultBrowserToolbarControllerTest {
@Test @Test
fun handleToolbarForwardPress() = runBlockingTest { fun handleToolbarForwardPress() = runBlockingTest {
val item = ToolbarMenu.Item.Forward val item = ToolbarMenu.Item.Forward(false)
val controller = createController(scope = this) val controller = createController(scope = this)
controller.handleToolbarItemInteraction(item) controller.handleToolbarItemInteraction(item)
@ -229,6 +229,17 @@ class DefaultBrowserToolbarControllerTest {
verify { sessionUseCases.goForward(currentSession) } verify { sessionUseCases.goForward(currentSession) }
} }
@Test
fun handleToolbarForwardLongPress() = runBlockingTest {
val item = ToolbarMenu.Item.Forward(true)
val controller = createController(scope = this)
controller.handleToolbarItemInteraction(item)
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.FORWARD)) }
verify { navController.navigate(R.id.action_global_tabHistoryDialogFragment) }
}
@Test @Test
fun handleToolbarReloadPress() = runBlockingTest { fun handleToolbarReloadPress() = runBlockingTest {
val item = ToolbarMenu.Item.Reload val item = ToolbarMenu.Item.Reload

View File

@ -0,0 +1,38 @@
/* 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.tabhistory
import androidx.navigation.NavController
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.session.SessionUseCases
import org.junit.Test
class TabHistoryControllerTest {
val sessionManager: SessionManager = mockk(relaxed = true)
val navController: NavController = mockk(relaxed = true)
val sessionUseCases = SessionUseCases(sessionManager)
val goToHistoryIndexUseCase = sessionUseCases.goToHistoryIndex
val controller = DefaultTabHistoryController(
navController = navController,
goToHistoryIndexUseCase = goToHistoryIndexUseCase
)
val currentItem = TabHistoryItem(
index = 0,
title = "",
url = "",
isSelected = true
)
@Test
fun handleGoToHistoryIndex() {
controller.handleGoToHistoryItem(currentItem)
verify { goToHistoryIndexUseCase.invoke(currentItem.index) }
}
}

View File

@ -0,0 +1,24 @@
/* 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.tabhistory
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
class TabHistoryInteractorTest {
val controller: TabHistoryController = mockk(relaxed = true)
val interactor = TabHistoryInteractor(controller)
@Test
fun onGoToHistoryItem() {
val item: TabHistoryItem = mockk()
interactor.goToHistoryItem(item)
verify { controller.handleGoToHistoryItem(item) }
}
}