For #21551 - Add delete history metadata in the History view

Co-authored-by: Christian Sadilek <christian.sadilek@gmail.com>
upstream-sync
Gabriel Luong 3 years ago committed by Christian Sadilek
parent c60de0bc6c
commit 15d1a0aa17

@ -13,6 +13,8 @@ import org.mozilla.fenix.library.history.History
import org.mozilla.fenix.library.history.toHistoryMetadata
import org.mozilla.fenix.perf.runBlockingIncrement
private const val BUFFER_TIME = 15000 /* 15 seconds in ms */
/**
* An Interface for providing a paginated list of [History].
*/
@ -35,7 +37,7 @@ class DefaultPagedHistoryProvider(
private val showHistorySearchGroups: Boolean = FeatureFlags.showHistorySearchGroups,
) : PagedHistoryProvider {
private var historyGroups: List<History.Group>? = null
@Volatile private var historyGroups: List<History.Group>? = null
@Suppress("LongMethod")
override fun getHistory(
@ -52,7 +54,6 @@ class DefaultPagedHistoryProvider(
// We need to refetch all the history metadata if the offset resets back at 0
// in the case of a pull to refresh.
if (historyGroups == null || offset == 0L) {
historyGroups = historyStorage.getHistoryMetadataSince(Long.MIN_VALUE)
.sortedByDescending { it.createdAt }
.filter { it.key.searchTerm != null }
@ -90,6 +91,36 @@ class DefaultPagedHistoryProvider(
}
}
/**
* Returns the [History.Regular] corresponding to the given [History.Metadata] item.
*
* @param historyMetadata The [History.Metadata] to match.
* @return the [History.Regular] corresponding to the given [History.Metadata] item or null.
*/
suspend fun getMatchingHistory(historyMetadata: History.Metadata): VisitInfo? {
val history = historyStorage.getDetailedVisits(
start = historyMetadata.visitedAt - BUFFER_TIME,
end = historyMetadata.visitedAt,
excludeTypes = listOf(
VisitType.NOT_A_VISIT,
VisitType.DOWNLOAD,
VisitType.REDIRECT_TEMPORARY,
VisitType.RELOAD,
VisitType.EMBED,
VisitType.FRAMED_LINK,
VisitType.REDIRECT_PERMANENT
)
)
return history.lastOrNull { it.url == historyMetadata.url }
}
/**
* Clears the history groups to refetch the most history metadata after any changes.
*/
fun clearHistoryGroups() {
historyGroups = null
}
@Suppress("MagicNumber")
private suspend fun getHistoryAndSearchGroups(
offset: Long,
@ -115,7 +146,7 @@ class DefaultPagedHistoryProvider(
// History metadata items are recorded after their associated visited info, we add an
// additional buffer time to the most recent visit to account for a history group
// appearing as the most recent item.
val visitedAtBuffer = if (offset == 0L) 15000 else 0 /* 15 seconds in ms */
val visitedAtBuffer = if (offset == 0L) BUFFER_TIME else 0
// Get the history groups that fit within the range of visited times in the current history
// items.
@ -129,8 +160,8 @@ class DefaultPagedHistoryProvider(
}
val historyMetadata = historyGroupsInOffset.flatMap { it.items }
// Add all items that are not in a group filtering out any matches with a history metadata
// item.
// Add all history items that are not in a group filtering out any matches with a history
// metadata item.
result.addAll(history.filter { item -> historyMetadata.find { it.url == item.url } == null })
// Filter history metadata items with no view time and dedupe by url.

@ -33,7 +33,7 @@ class DefaultHistoryController(
private val openToBrowser: (item: History.Regular) -> Unit,
private val displayDeleteAll: () -> Unit,
private val invalidateOptionsMenu: () -> Unit,
private val deleteHistoryItems: (Set<History.Regular>) -> Unit,
private val deleteHistoryItems: (Set<History>) -> Unit,
private val syncHistory: suspend () -> Unit,
private val metrics: MetricController
) : HistoryController {
@ -59,15 +59,11 @@ class DefaultHistoryController(
return
}
if (item is History.Regular) {
store.dispatch(HistoryFragmentAction.AddItemForRemoval(item))
}
store.dispatch(HistoryFragmentAction.AddItemForRemoval(item))
}
override fun handleDeselect(item: History) {
if (item is History.Regular) {
store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(item))
}
store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(item))
}
override fun handleBackPressed(): Boolean {
@ -88,9 +84,7 @@ class DefaultHistoryController(
}
override fun handleDeleteSome(items: Set<History>) {
items.filterIsInstance<History.Regular>().let {
deleteHistoryItems.invoke(it.toSet())
}
deleteHistoryItems.invoke(items)
}
override fun handleRequestSync() {

@ -53,6 +53,8 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
private lateinit var historyStore: HistoryFragmentStore
private lateinit var historyInteractor: HistoryInteractor
private lateinit var viewModel: HistoryViewModel
private lateinit var historyProvider: DefaultPagedHistoryProvider
private var undoScope: CoroutineScope? = null
private var pendingHistoryDeletionJob: (suspend () -> Unit)? = null
@ -65,7 +67,7 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View {
_binding = FragmentHistoryBinding.inflate(inflater, container, false)
val view = binding.root
@ -110,9 +112,9 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = HistoryViewModel(
historyProvider = DefaultPagedHistoryProvider(requireComponents.core.historyStorage)
)
historyProvider = DefaultPagedHistoryProvider(requireComponents.core.historyStorage)
viewModel = HistoryViewModel(historyProvider)
viewModel.userHasHistory.observe(
this,
@ -126,7 +128,7 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
setHasOptionsMenu(true)
}
private fun deleteHistoryItems(items: Set<History.Regular>) {
private fun deleteHistoryItems(items: Set<History>) {
updatePendingHistoryToDelete(items)
undoScope = CoroutineScope(IO)
undoScope?.allowUndo(
@ -178,8 +180,28 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.share_history_multi_select -> {
val selectedHistory = historyStore.state.mode.selectedItems
val shareTabs = selectedHistory.map { ShareData(url = it.url, title = it.title) }
val shareTabs = mutableListOf<ShareData>()
for (history in selectedHistory) {
when (history) {
is History.Regular -> {
shareTabs.add(ShareData(url = history.url, title = history.title))
}
is History.Group -> {
shareTabs.addAll(
history.items.map { metadata ->
ShareData(url = metadata.url, title = metadata.title)
}
)
}
else -> {
// no-op, There is no [History.Metadata] in the HistoryFragment.
}
}
}
share(shareTabs)
true
}
R.id.delete_history_multi_select -> {
@ -227,15 +249,19 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
)
}
private fun getMultiSelectSnackBarMessage(historyItems: Set<History.Regular>): String {
private fun getMultiSelectSnackBarMessage(historyItems: Set<History>): String {
return if (historyItems.size > 1) {
getString(R.string.history_delete_multiple_items_snackbar)
} else {
val historyItem = historyItems.first()
String.format(
requireContext().getString(
R.string.history_delete_single_item_snackbar
),
historyItems.first().url.toShortUrl(requireComponents.publicSuffixList)
requireContext().getString(R.string.history_delete_single_item_snackbar),
if (historyItem is History.Regular) {
historyItem.url.toShortUrl(requireComponents.publicSuffixList)
} else {
historyItem.title
}
)
}
}
@ -318,14 +344,35 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
)
}
private fun getDeleteHistoryItemsOperation(items: Set<History.Regular>): (suspend () -> Unit) {
private fun getDeleteHistoryItemsOperation(items: Set<History>): (suspend () -> Unit) {
return {
CoroutineScope(IO).launch {
historyStore.dispatch(HistoryFragmentAction.EnterDeletionMode)
context?.components?.run {
for (item in items) {
analytics.metrics.track(Event.HistoryItemRemoved)
core.historyStorage.deleteVisit(item.url, item.visitedAt)
if (item is History.Regular) {
core.historyStorage.deleteVisit(
url = item.url,
timestamp = item.visitedAt
)
} else if (item is History.Group) {
for (historyMetadata in item.items) {
historyProvider.getMatchingHistory(historyMetadata)?.let {
core.historyStorage.deleteVisit(
url = it.url,
timestamp = it.visitTime
)
}
}
core.historyStorage.deleteHistoryMetadata(
searchTerm = item.title
)
historyProvider.clearHistoryGroups()
}
}
}
historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode)
@ -334,13 +381,13 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
}
}
private fun updatePendingHistoryToDelete(items: Set<History.Regular>) {
private fun updatePendingHistoryToDelete(items: Set<History>) {
pendingHistoryDeletionJob = getDeleteHistoryItemsOperation(items)
val ids = items.map { item -> item.visitedAt }.toSet()
historyStore.dispatch(HistoryFragmentAction.AddPendingDeletionSet(ids))
}
private fun undoPendingDeletion(items: Set<History.Regular>) {
private fun undoPendingDeletion(items: Set<History>) {
pendingHistoryDeletionJob = null
val ids = items.map { item -> item.visitedAt }.toSet()
historyStore.dispatch(HistoryFragmentAction.UndoPendingDeletionSet(ids))

@ -105,8 +105,8 @@ class HistoryFragmentStore(initialState: HistoryFragmentState) :
*/
sealed class HistoryFragmentAction : Action {
object ExitEditMode : HistoryFragmentAction()
data class AddItemForRemoval(val item: History.Regular) : HistoryFragmentAction()
data class RemoveItemForRemoval(val item: History.Regular) : HistoryFragmentAction()
data class AddItemForRemoval(val item: History) : HistoryFragmentAction()
data class RemoveItemForRemoval(val item: History) : HistoryFragmentAction()
data class AddPendingDeletionSet(val itemIds: Set<Long>) : HistoryFragmentAction()
data class UndoPendingDeletionSet(val itemIds: Set<Long>) : HistoryFragmentAction()
object EnterDeletionMode : HistoryFragmentAction()
@ -127,11 +127,11 @@ data class HistoryFragmentState(
val isDeletingItems: Boolean
) : State {
sealed class Mode {
open val selectedItems = emptySet<History.Regular>()
open val selectedItems = emptySet<History>()
object Normal : Mode()
object Syncing : Mode()
data class Editing(override val selectedItems: Set<History.Regular>) : Mode()
data class Editing(override val selectedItems: Set<History>) : Mode()
}
}

@ -53,8 +53,6 @@ class HistoryListItemViewHolder(
binding.historyLayout.visibility = View.VISIBLE
}
binding.historyLayout.overflowView.isVisible = item !is History.Group
binding.historyLayout.titleView.text = item.title
binding.historyLayout.urlView.text = Do exhaustive when (item) {

@ -12,6 +12,7 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -35,7 +36,8 @@ import org.mozilla.fenix.library.historymetadata.view.HistoryMetadataGroupView
/**
* Displays a list of history metadata items for a history metadata search group.
*/
class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), UserInteractionHandler {
class HistoryMetadataGroupFragment :
LibraryPageFragment<History.Metadata>(), UserInteractionHandler {
private lateinit var historyMetadataGroupStore: HistoryMetadataGroupFragmentStore
private lateinit var interactor: HistoryMetadataGroupInteractor
@ -43,6 +45,8 @@ class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), Us
private var _historyMetadataGroupView: HistoryMetadataGroupView? = null
private val historyMetadataGroupView: HistoryMetadataGroupView
get() = _historyMetadataGroupView!!
private var _binding: FragmentHistoryMetadataGroupBinding? = null
private val binding get() = _binding!!
private val args by navArgs<HistoryMetadataGroupFragmentArgs>()
@ -54,9 +58,9 @@ class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), Us
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentHistoryMetadataGroupBinding.inflate(inflater, container, false)
savedInstanceState: Bundle?,
): View {
_binding = FragmentHistoryMetadataGroupBinding.inflate(inflater, container, false)
historyMetadataGroupStore = StoreProvider.get(this) {
HistoryMetadataGroupFragmentStore(
@ -70,7 +74,9 @@ class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), Us
controller = DefaultHistoryMetadataGroupController(
activity = activity as HomeActivity,
store = historyMetadataGroupStore,
navController = findNavController()
navController = findNavController(),
scope = lifecycleScope,
searchTerm = args.title
)
)
@ -117,7 +123,7 @@ class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), Us
true
}
R.id.delete_history_multi_select -> {
interactor.onDeleteMenuItem(selectedItems)
interactor.onDelete(selectedItems)
true
}
R.id.open_history_in_new_tabs_multi_select -> {
@ -152,10 +158,12 @@ class HistoryMetadataGroupFragment : LibraryPageFragment<History.Metadata>(), Us
override fun onDestroyView() {
super.onDestroyView()
_historyMetadataGroupView = null
_binding = null
}
override val selectedItems: Set<History.Metadata> get() =
historyMetadataGroupStore.state.items.filter { it.selected }.toSet()
override val selectedItems: Set<History.Metadata>
get() =
historyMetadataGroupStore.state.items.filter { it.selected }.toSet()
override fun onBackPressed(): Boolean = interactor.onBackPressed(selectedItems)

@ -29,6 +29,8 @@ sealed class HistoryMetadataGroupFragmentAction : Action {
data class Select(val item: History.Metadata) : HistoryMetadataGroupFragmentAction()
data class Deselect(val item: History.Metadata) : HistoryMetadataGroupFragmentAction()
object DeselectAll : HistoryMetadataGroupFragmentAction()
data class Delete(val item: History.Metadata) : HistoryMetadataGroupFragmentAction()
object DeleteAll : HistoryMetadataGroupFragmentAction()
}
/**
@ -82,5 +84,12 @@ private fun historyStateReducer(
items = state.items.toMutableList()
.map { it.copy(selected = false) }
)
is HistoryMetadataGroupFragmentAction.Delete -> {
val items = state.items.toMutableList()
items.remove(action.item)
state.copy(items = items)
}
is HistoryMetadataGroupFragmentAction.DeleteAll ->
state.copy(items = emptyList())
}
}

@ -5,9 +5,12 @@
package org.mozilla.fenix.library.historymetadata.controller
import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mozilla.components.concept.engine.prompt.ShareData
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.library.history.History
import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentAction
import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentDirections
@ -53,6 +56,16 @@ interface HistoryMetadataGroupController {
* @param items The set of [History]s to share.
*/
fun handleShare(items: Set<History.Metadata>)
/**
* Deletes the given history metadata [items] from storage.
*/
fun handleDelete(items: Set<History.Metadata>)
/**
* Deletes all the history metadata items in this group.
*/
fun handleDeleteAll()
}
/**
@ -62,6 +75,8 @@ class DefaultHistoryMetadataGroupController(
private val activity: HomeActivity,
private val store: HistoryMetadataGroupFragmentStore,
private val navController: NavController,
private val scope: CoroutineScope,
private val searchTerm: String,
) : HistoryMetadataGroupController {
override fun handleOpen(item: History.Metadata) {
@ -97,4 +112,20 @@ class DefaultHistoryMetadataGroupController(
)
)
}
override fun handleDelete(items: Set<History.Metadata>) {
scope.launch {
items.forEach {
store.dispatch(HistoryMetadataGroupFragmentAction.Delete(it))
activity.components.core.historyStorage.deleteHistoryMetadata(it.historyMetadataKey)
}
}
}
override fun handleDeleteAll() {
scope.launch {
store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll)
activity.components.core.historyStorage.deleteHistoryMetadata(searchTerm)
}
}
}

@ -21,15 +21,15 @@ interface HistoryMetadataGroupInteractor : SelectionInteractor<History.Metadata>
fun onBackPressed(items: Set<History.Metadata>): Boolean
/**
* Deletes the given set of history [items] that are selected. Called when a user clicks on the
* "Delete" menu item.
* Deletes the given set of history metadata [items]. Called when a user clicks on the
* "Delete" menu item or the "x" button associated with a history metadata item.
*
* @param items The set of [History]s to delete.
*/
fun onDeleteMenuItem(items: Set<History.Metadata>)
fun onDelete(items: Set<History.Metadata>)
/**
* Deletes the all the history items in the history metadata group. Called when a user clicks
* Deletes all the history items in the history metadata group. Called when a user clicks
* on the "Delete history" menu item.
*/
fun onDeleteAllMenuItem()
@ -66,12 +66,12 @@ class DefaultHistoryMetadataGroupInteractor(
return controller.handleBackPressed(items)
}
override fun onDeleteMenuItem(items: Set<History.Metadata>) {
// no-op
override fun onDelete(items: Set<History.Metadata>) {
controller.handleDelete(items)
}
override fun onDeleteAllMenuItem() {
// no-op
controller.handleDeleteAll()
}
override fun onShareMenuItem(items: Set<History.Metadata>) {

@ -27,6 +27,14 @@ class HistoryMetadataGroupItemViewHolder(
private var item: History.Metadata? = null
init {
binding.historyLayout.overflowView.setImageResource(R.drawable.ic_close)
binding.historyLayout.overflowView.setOnClickListener {
val item = this.item ?: return@setOnClickListener
interactor.onDelete(setOf(item))
}
}
fun bind(item: History.Metadata) {
binding.historyLayout.titleView.text = item.title
binding.historyLayout.urlView.text = item.url
@ -38,8 +46,6 @@ class HistoryMetadataGroupItemViewHolder(
binding.historyLayout.loadFavicon(item.url)
}
binding.historyLayout.overflowView.setImageResource(R.drawable.ic_close)
if (selectionHolder.selectedItems.isEmpty()) {
binding.historyLayout.overflowView.showAndEnable()
} else {

@ -82,4 +82,25 @@ class HistoryMetadataGroupFragmentStoreTest {
assertFalse(store.state.items[0].selected)
assertFalse(store.state.items[1].selected)
}
@Test
fun `Test deleting an item in HistoryMetadataGroupFragmentStore`() = runBlocking {
val items = listOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem)
store.dispatch(HistoryMetadataGroupFragmentAction.UpdateHistoryItems(items)).join()
store.dispatch(HistoryMetadataGroupFragmentAction.Delete(mozillaHistoryMetadataItem)).join()
assertEquals(1, store.state.items.size)
assertEquals(firefoxHistoryMetadataItem, store.state.items.first())
}
@Test
fun `Test deleting all items in HistoryMetadataGroupFragmentStore`() = runBlocking {
val items = listOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem)
store.dispatch(HistoryMetadataGroupFragmentAction.UpdateHistoryItems(items)).join()
store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll).join()
assertEquals(0, store.state.items.size)
}
}

@ -5,13 +5,19 @@
package org.mozilla.fenix.library.historymetadata.controller
import androidx.navigation.NavController
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.concept.storage.HistoryMetadataKey
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@ -20,6 +26,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.directionsEq
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.library.history.History
@ -32,6 +39,7 @@ import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentSto
class HistoryMetadataGroupControllerTest {
private val testDispatcher = TestCoroutineDispatcher()
private val scope = TestCoroutineScope()
@get:Rule
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
@ -39,14 +47,17 @@ class HistoryMetadataGroupControllerTest {
private val activity: HomeActivity = mockk(relaxed = true)
private val store: HistoryMetadataGroupFragmentStore = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true)
private val historyStorage: PlacesHistoryStorage = mockk(relaxed = true)
private val searchTerm = "mozilla"
private val historyMetadataKey = HistoryMetadataKey("http://www.mozilla.com", searchTerm, null)
private val mozillaHistoryMetadataItem = History.Metadata(
id = 0,
title = "Mozilla",
url = "mozilla.org",
visitedAt = 0,
totalViewTime = 1,
historyMetadataKey = HistoryMetadataKey("http://www.mozilla.com", "mozilla", null)
historyMetadataKey = historyMetadataKey
)
private val firefoxHistoryMetadataItem = History.Metadata(
id = 0,
@ -54,7 +65,7 @@ class HistoryMetadataGroupControllerTest {
url = "firefox.com",
visitedAt = 0,
totalViewTime = 1,
historyMetadataKey = HistoryMetadataKey("http://www.firefox.com", "mozilla", null)
historyMetadataKey = historyMetadataKey
)
private lateinit var controller: DefaultHistoryMetadataGroupController
@ -64,8 +75,17 @@ class HistoryMetadataGroupControllerTest {
controller = DefaultHistoryMetadataGroupController(
activity = activity,
store = store,
navController = navController
navController = navController,
scope = scope,
searchTerm = "mozilla"
)
every { activity.components.core.historyStorage } returns historyStorage
}
@After
fun cleanUp() {
scope.cleanupTestCoroutines()
}
@Test
@ -132,4 +152,26 @@ class HistoryMetadataGroupControllerTest {
)
}
}
@Test
fun handleDelete() = testDispatcher.runBlockingTest {
controller.handleDelete(setOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem))
coVerify {
store.dispatch(HistoryMetadataGroupFragmentAction.Delete(mozillaHistoryMetadataItem))
store.dispatch(HistoryMetadataGroupFragmentAction.Delete(firefoxHistoryMetadataItem))
historyStorage.deleteHistoryMetadata(mozillaHistoryMetadataItem.historyMetadataKey)
historyStorage.deleteHistoryMetadata(firefoxHistoryMetadataItem.historyMetadataKey)
}
}
@Test
fun handleDeleteAll() = testDispatcher.runBlockingTest {
controller.handleDeleteAll()
coVerify {
store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll)
historyStorage.deleteHistoryMetadata(searchTerm)
}
}
}

Loading…
Cancel
Save