Bug 1842304 - Remove feature flag for history interactor removal
parent
e2c03c8b77
commit
585e29def3
@ -1,209 +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.library.history
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavOptions
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import mozilla.components.browser.state.action.EngineAction
|
|
||||||
import mozilla.components.browser.state.action.HistoryMetadataAction
|
|
||||||
import mozilla.components.browser.state.action.RecentlyClosedAction
|
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
|
||||||
import mozilla.components.service.glean.private.NoExtras
|
|
||||||
import org.mozilla.fenix.GleanMetrics.Events
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.components.AppStore
|
|
||||||
import org.mozilla.fenix.components.appstate.AppAction
|
|
||||||
import org.mozilla.fenix.components.history.DefaultPagedHistoryProvider
|
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
import org.mozilla.fenix.ext.navigateSafe
|
|
||||||
import org.mozilla.fenix.library.history.HistoryFragment.DeleteConfirmationDialogFragment
|
|
||||||
import org.mozilla.fenix.GleanMetrics.History as GleanHistory
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
|
||||||
interface HistoryController {
|
|
||||||
fun handleOpen(item: History)
|
|
||||||
fun handleSelect(item: History)
|
|
||||||
fun handleDeselect(item: History)
|
|
||||||
fun handleBackPressed(): Boolean
|
|
||||||
fun handleModeSwitched()
|
|
||||||
fun handleSearch()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a [DeleteConfirmationDialogFragment].
|
|
||||||
*/
|
|
||||||
fun handleDeleteTimeRange()
|
|
||||||
fun handleDeleteSome(items: Set<History>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes history items inside the time frame.
|
|
||||||
*
|
|
||||||
* @param timeFrame Selected time frame by the user. If `null`, removes all history.
|
|
||||||
*/
|
|
||||||
fun handleDeleteTimeRangeConfirmed(timeFrame: RemoveTimeFrame?)
|
|
||||||
fun handleRequestSync()
|
|
||||||
fun handleEnterRecentlyClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions", "LongParameterList")
|
|
||||||
class DefaultHistoryController(
|
|
||||||
private val store: HistoryFragmentStore,
|
|
||||||
private val appStore: AppStore,
|
|
||||||
private val browserStore: BrowserStore,
|
|
||||||
private val historyStorage: PlacesHistoryStorage,
|
|
||||||
private var historyProvider: DefaultPagedHistoryProvider,
|
|
||||||
private val navController: NavController,
|
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val openToBrowser: (item: History.Regular) -> Unit,
|
|
||||||
private val displayDeleteTimeRange: () -> Unit,
|
|
||||||
private val onTimeFrameDeleted: () -> Unit,
|
|
||||||
private val invalidateOptionsMenu: () -> Unit,
|
|
||||||
private val deleteSnackbar: (
|
|
||||||
items: Set<History>,
|
|
||||||
undo: suspend (Set<History>) -> Unit,
|
|
||||||
delete: (Set<History>) -> suspend (context: Context) -> Unit,
|
|
||||||
) -> Unit,
|
|
||||||
private val syncHistory: suspend () -> Unit,
|
|
||||||
) : HistoryController {
|
|
||||||
|
|
||||||
override fun handleOpen(item: History) {
|
|
||||||
when (item) {
|
|
||||||
is History.Regular -> openToBrowser(item)
|
|
||||||
is History.Group -> {
|
|
||||||
GleanHistory.searchTermGroupTapped.record(NoExtras())
|
|
||||||
navController.navigate(
|
|
||||||
HistoryFragmentDirections.actionGlobalHistoryMetadataGroup(
|
|
||||||
title = item.title,
|
|
||||||
historyMetadataItems = item.items.toTypedArray(),
|
|
||||||
),
|
|
||||||
NavOptions.Builder().setPopUpTo(R.id.historyMetadataGroupFragment, true).build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> { /* noop */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleSelect(item: History) {
|
|
||||||
if (store.state.mode === HistoryFragmentState.Mode.Syncing) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch(HistoryFragmentAction.AddItemForRemoval(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleDeselect(item: History) {
|
|
||||||
store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleBackPressed(): Boolean {
|
|
||||||
return if (store.state.mode is HistoryFragmentState.Mode.Editing) {
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitEditMode)
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleModeSwitched() {
|
|
||||||
invalidateOptionsMenu.invoke()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleSearch() {
|
|
||||||
navController.navigateSafe(
|
|
||||||
R.id.historyFragment,
|
|
||||||
HistoryFragmentDirections.actionGlobalSearchDialog(sessionId = null),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleDeleteTimeRange() {
|
|
||||||
displayDeleteTimeRange.invoke()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleDeleteSome(items: Set<History>) {
|
|
||||||
val pendingDeletionItems = items.map { it.toPendingDeletionHistory() }.toSet()
|
|
||||||
appStore.dispatch(AppAction.AddPendingDeletionSet(pendingDeletionItems))
|
|
||||||
deleteSnackbar.invoke(items, ::undo, ::delete)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleDeleteTimeRangeConfirmed(timeFrame: RemoveTimeFrame?) {
|
|
||||||
scope.launch {
|
|
||||||
store.dispatch(HistoryFragmentAction.EnterDeletionMode)
|
|
||||||
if (timeFrame == null) {
|
|
||||||
historyStorage.deleteEverything()
|
|
||||||
} else {
|
|
||||||
val longRange = timeFrame.toLongRange()
|
|
||||||
historyStorage.deleteVisitsBetween(
|
|
||||||
startTime = longRange.first,
|
|
||||||
endTime = longRange.last,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
when (timeFrame) {
|
|
||||||
RemoveTimeFrame.LastHour -> GleanHistory.removedLastHour.record(NoExtras())
|
|
||||||
RemoveTimeFrame.TodayAndYesterday -> GleanHistory.removedTodayAndYesterday.record(NoExtras())
|
|
||||||
null -> GleanHistory.removedAll.record(NoExtras())
|
|
||||||
}
|
|
||||||
// We introduced more deleting options, but are keeping these actions for all options.
|
|
||||||
// The approach could be improved: https://github.com/mozilla-mobile/fenix/issues/26102
|
|
||||||
browserStore.dispatch(RecentlyClosedAction.RemoveAllClosedTabAction)
|
|
||||||
browserStore.dispatch(EngineAction.PurgeHistoryAction).join()
|
|
||||||
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitDeletionMode)
|
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
onTimeFrameDeleted.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun undo(items: Set<History>) {
|
|
||||||
val pendingDeletionItems = items.map { it.toPendingDeletionHistory() }.toSet()
|
|
||||||
appStore.dispatch(AppAction.UndoPendingDeletionSet(pendingDeletionItems))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun delete(items: Set<History>): suspend (context: Context) -> Unit {
|
|
||||||
return { context ->
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
store.dispatch(HistoryFragmentAction.EnterDeletionMode)
|
|
||||||
for (item in items) {
|
|
||||||
GleanHistory.removed.record(NoExtras())
|
|
||||||
|
|
||||||
when (item) {
|
|
||||||
is History.Regular -> context.components.core.historyStorage.deleteVisitsFor(item.url)
|
|
||||||
is History.Group -> {
|
|
||||||
// NB: If we have non-search groups, this logic needs to be updated.
|
|
||||||
historyProvider.deleteMetadataSearchGroup(item)
|
|
||||||
context.components.core.store.dispatch(
|
|
||||||
HistoryMetadataAction.DisbandSearchGroupAction(searchTerm = item.title),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// We won't encounter individual metadata entries outside of groups.
|
|
||||||
is History.Metadata -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitDeletionMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleRequestSync() {
|
|
||||||
scope.launch {
|
|
||||||
store.dispatch(HistoryFragmentAction.StartSync)
|
|
||||||
syncHistory.invoke()
|
|
||||||
store.dispatch(HistoryFragmentAction.FinishSync)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleEnterRecentlyClosed() {
|
|
||||||
navController.navigate(
|
|
||||||
HistoryFragmentDirections.actionGlobalRecentlyClosed(),
|
|
||||||
NavOptions.Builder().setPopUpTo(R.id.recentlyClosedFragment, true).build(),
|
|
||||||
)
|
|
||||||
Events.recentlyClosedTabsOpened.record(NoExtras())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +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.library.history
|
|
||||||
|
|
||||||
import mozilla.components.service.glean.private.NoExtras
|
|
||||||
import org.mozilla.fenix.selection.SelectionInteractor
|
|
||||||
import org.mozilla.fenix.GleanMetrics.History as GleanHistory
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for the HistoryInteractor. This interface is implemented by objects that want
|
|
||||||
* to respond to user interaction on the HistoryView
|
|
||||||
*/
|
|
||||||
interface HistoryInteractor : SelectionInteractor<History> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on backpressed to exit edit mode
|
|
||||||
*/
|
|
||||||
fun onBackPressed(): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the mode is switched so we can invalidate the menu
|
|
||||||
*/
|
|
||||||
fun onModeSwitched()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when search is tapped
|
|
||||||
*/
|
|
||||||
fun onSearch()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the delete menu button is tapped.
|
|
||||||
*/
|
|
||||||
fun onDeleteTimeRange()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when multiple history items are deleted
|
|
||||||
* @param items the history items to delete
|
|
||||||
*/
|
|
||||||
fun onDeleteSome(items: Set<History>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the user has confirmed deletion of a time range.
|
|
||||||
*
|
|
||||||
* @param timeFrame The selected timeframe. `null` means no specific time frame has been
|
|
||||||
* selected; should remove everything.
|
|
||||||
*/
|
|
||||||
fun onDeleteTimeRangeConfirmed(timeFrame: RemoveTimeFrame?)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the user requests a sync of the history
|
|
||||||
*/
|
|
||||||
fun onRequestSync()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the user clicks on recently closed tab button.
|
|
||||||
*/
|
|
||||||
fun onRecentlyClosedClicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interactor for the history screen
|
|
||||||
* Provides implementations for the HistoryInteractor
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("TooManyFunctions")
|
|
||||||
class DefaultHistoryInteractor(
|
|
||||||
private val historyController: HistoryController,
|
|
||||||
) : HistoryInteractor {
|
|
||||||
override fun open(item: History) {
|
|
||||||
historyController.handleOpen(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun select(item: History) {
|
|
||||||
historyController.handleSelect(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deselect(item: History) {
|
|
||||||
historyController.handleDeselect(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBackPressed(): Boolean {
|
|
||||||
return historyController.handleBackPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onModeSwitched() {
|
|
||||||
historyController.handleModeSwitched()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSearch() {
|
|
||||||
GleanHistory.searchIconTapped.record(NoExtras())
|
|
||||||
historyController.handleSearch()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeleteTimeRange() {
|
|
||||||
historyController.handleDeleteTimeRange()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeleteSome(items: Set<History>) {
|
|
||||||
historyController.handleDeleteSome(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeleteTimeRangeConfirmed(timeFrame: RemoveTimeFrame?) {
|
|
||||||
historyController.handleDeleteTimeRangeConfirmed(timeFrame)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestSync() {
|
|
||||||
historyController.handleRequestSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRecentlyClosedClicked() {
|
|
||||||
historyController.handleEnterRecentlyClosed()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,272 +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.library.history
|
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import io.mockk.coVerifyOrder
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.verify
|
|
||||||
import mozilla.components.browser.state.action.EngineAction
|
|
||||||
import mozilla.components.browser.state.action.RecentlyClosedAction
|
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
|
||||||
import mozilla.components.service.glean.testing.GleanTestRule
|
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
|
||||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Assert.assertNull
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.components.AppStore
|
|
||||||
import org.mozilla.fenix.components.history.DefaultPagedHistoryProvider
|
|
||||||
import org.mozilla.fenix.ext.navigateSafe
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class HistoryControllerTest {
|
|
||||||
private val historyItem = History.Regular(
|
|
||||||
0,
|
|
||||||
"title",
|
|
||||||
"url",
|
|
||||||
0.toLong(),
|
|
||||||
HistoryItemTimeGroup.timeGroupForTimestamp(0),
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val gleanTestRule = GleanTestRule(testContext)
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val coroutinesTestRule = MainCoroutineRule()
|
|
||||||
private val scope = coroutinesTestRule.scope
|
|
||||||
|
|
||||||
private val store: HistoryFragmentStore = mockk(relaxed = true)
|
|
||||||
private val appStore: AppStore = mockk(relaxed = true)
|
|
||||||
private val browserStore: BrowserStore = mockk(relaxed = true)
|
|
||||||
private val historyStorage: PlacesHistoryStorage = mockk(relaxed = true)
|
|
||||||
private val state: HistoryFragmentState = mockk(relaxed = true)
|
|
||||||
private val navController: NavController = mockk(relaxed = true)
|
|
||||||
private val historyProvider: DefaultPagedHistoryProvider = mockk(relaxed = true)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
every { store.state } returns state
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onPressHistoryItemInNormalMode() {
|
|
||||||
var actualHistoryItem: History? = null
|
|
||||||
val controller = createController(
|
|
||||||
openInBrowser = {
|
|
||||||
actualHistoryItem = it
|
|
||||||
},
|
|
||||||
)
|
|
||||||
controller.handleOpen(historyItem)
|
|
||||||
assertEquals(historyItem, actualHistoryItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onPressHistoryItemInEditMode() {
|
|
||||||
every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf())
|
|
||||||
|
|
||||||
createController().handleSelect(historyItem)
|
|
||||||
|
|
||||||
verify {
|
|
||||||
store.dispatch(HistoryFragmentAction.AddItemForRemoval(historyItem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onPressSelectedHistoryItemInEditMode() {
|
|
||||||
every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf(historyItem))
|
|
||||||
|
|
||||||
createController().handleDeselect(historyItem)
|
|
||||||
|
|
||||||
verify {
|
|
||||||
store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(historyItem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onSelectHistoryItemDuringSync() {
|
|
||||||
every { state.mode } returns HistoryFragmentState.Mode.Syncing
|
|
||||||
|
|
||||||
createController().handleSelect(historyItem)
|
|
||||||
|
|
||||||
verify(exactly = 0) {
|
|
||||||
store.dispatch(HistoryFragmentAction.AddItemForRemoval(historyItem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onBackPressedInNormalMode() {
|
|
||||||
every { state.mode } returns HistoryFragmentState.Mode.Normal
|
|
||||||
|
|
||||||
assertFalse(createController().handleBackPressed())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onBackPressedInEditMode() {
|
|
||||||
every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf())
|
|
||||||
|
|
||||||
assertTrue(createController().handleBackPressed())
|
|
||||||
verify {
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitEditMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onModeSwitched() {
|
|
||||||
var invalidateOptionsMenuInvoked = false
|
|
||||||
val controller = createController(
|
|
||||||
invalidateOptionsMenu = {
|
|
||||||
invalidateOptionsMenuInvoked = true
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
controller.handleModeSwitched()
|
|
||||||
assertTrue(invalidateOptionsMenuInvoked)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onSearch() {
|
|
||||||
val controller = createController()
|
|
||||||
|
|
||||||
controller.handleSearch()
|
|
||||||
verify {
|
|
||||||
navController.navigateSafe(
|
|
||||||
R.id.historyFragment,
|
|
||||||
HistoryFragmentDirections.actionGlobalSearchDialog(sessionId = null),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeleteTimeRange() {
|
|
||||||
var displayDeleteTimeRangeInvoked = false
|
|
||||||
val controller = createController(
|
|
||||||
displayDeleteTimeRange = {
|
|
||||||
displayDeleteTimeRangeInvoked = true
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
controller.handleDeleteTimeRange()
|
|
||||||
assertTrue(displayDeleteTimeRangeInvoked)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WHEN user confirms history deletion GIVEN timeFrame is null THEN delete all history, log the event, purge history and remove recently closed items`() {
|
|
||||||
val controller = createController()
|
|
||||||
assertNull(org.mozilla.fenix.GleanMetrics.History.removedAll.testGetValue())
|
|
||||||
|
|
||||||
controller.handleDeleteTimeRangeConfirmed(null)
|
|
||||||
coVerifyOrder {
|
|
||||||
store.dispatch(HistoryFragmentAction.EnterDeletionMode)
|
|
||||||
historyStorage.deleteEverything()
|
|
||||||
browserStore.dispatch(RecentlyClosedAction.RemoveAllClosedTabAction)
|
|
||||||
browserStore.dispatch(EngineAction.PurgeHistoryAction)
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitDeletionMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(org.mozilla.fenix.GleanMetrics.History.removedAll.testGetValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WHEN user confirms history deletion GIVEN timeFrame is lastHour THEN delete visits between the time frame, log the event, purge history and remove recently closed items`() {
|
|
||||||
val controller = createController()
|
|
||||||
assertNull(org.mozilla.fenix.GleanMetrics.History.removedLastHour.testGetValue())
|
|
||||||
|
|
||||||
controller.handleDeleteTimeRangeConfirmed(RemoveTimeFrame.LastHour)
|
|
||||||
coVerifyOrder {
|
|
||||||
store.dispatch(HistoryFragmentAction.EnterDeletionMode)
|
|
||||||
historyStorage.deleteVisitsBetween(any(), any())
|
|
||||||
browserStore.dispatch(RecentlyClosedAction.RemoveAllClosedTabAction)
|
|
||||||
browserStore.dispatch(EngineAction.PurgeHistoryAction)
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitDeletionMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(org.mozilla.fenix.GleanMetrics.History.removedLastHour.testGetValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WHEN user confirms history deletion GIVEN timeFrame is todayAndYesterday THEN delete visits between the time frame, log the event, purge history and remove recently closed items`() {
|
|
||||||
val controller = createController()
|
|
||||||
assertNull(org.mozilla.fenix.GleanMetrics.History.removedTodayAndYesterday.testGetValue())
|
|
||||||
|
|
||||||
controller.handleDeleteTimeRangeConfirmed(RemoveTimeFrame.TodayAndYesterday)
|
|
||||||
coVerifyOrder {
|
|
||||||
store.dispatch(HistoryFragmentAction.EnterDeletionMode)
|
|
||||||
historyStorage.deleteVisitsBetween(any(), any())
|
|
||||||
browserStore.dispatch(RecentlyClosedAction.RemoveAllClosedTabAction)
|
|
||||||
browserStore.dispatch(EngineAction.PurgeHistoryAction)
|
|
||||||
store.dispatch(HistoryFragmentAction.ExitDeletionMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(org.mozilla.fenix.GleanMetrics.History.removedTodayAndYesterday.testGetValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeleteSome() {
|
|
||||||
val itemsToDelete = setOf(historyItem)
|
|
||||||
var actualItems: Set<History>? = null
|
|
||||||
val controller = createController(
|
|
||||||
deleteHistoryItems = { items ->
|
|
||||||
actualItems = items
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
controller.handleDeleteSome(itemsToDelete)
|
|
||||||
assertEquals(itemsToDelete, actualItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onRequestSync() {
|
|
||||||
var syncHistoryInvoked = false
|
|
||||||
createController(
|
|
||||||
syncHistory = {
|
|
||||||
syncHistoryInvoked = true
|
|
||||||
},
|
|
||||||
).handleRequestSync()
|
|
||||||
|
|
||||||
coVerifyOrder {
|
|
||||||
store.dispatch(HistoryFragmentAction.StartSync)
|
|
||||||
store.dispatch(HistoryFragmentAction.FinishSync)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(syncHistoryInvoked)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
|
||||||
private fun createController(
|
|
||||||
openInBrowser: (History) -> Unit = { _ -> },
|
|
||||||
displayDeleteTimeRange: () -> Unit = {},
|
|
||||||
onTimeFrameDeleted: () -> Unit = {},
|
|
||||||
invalidateOptionsMenu: () -> Unit = {},
|
|
||||||
deleteHistoryItems: (Set<History>) -> Unit = { _ -> },
|
|
||||||
syncHistory: suspend () -> Unit = {},
|
|
||||||
): HistoryController {
|
|
||||||
return DefaultHistoryController(
|
|
||||||
store,
|
|
||||||
appStore,
|
|
||||||
browserStore,
|
|
||||||
historyStorage,
|
|
||||||
historyProvider,
|
|
||||||
navController,
|
|
||||||
scope,
|
|
||||||
openInBrowser,
|
|
||||||
displayDeleteTimeRange,
|
|
||||||
onTimeFrameDeleted,
|
|
||||||
invalidateOptionsMenu,
|
|
||||||
{ items, _, _ -> deleteHistoryItems.invoke(items) },
|
|
||||||
syncHistory,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +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.library.history
|
|
||||||
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.verifyAll
|
|
||||||
import mozilla.components.service.glean.testing.GleanTestRule
|
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Assert.assertNull
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
import org.mozilla.fenix.GleanMetrics.History as GleanHistory
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class) // For GleanTestRule
|
|
||||||
class HistoryInteractorTest {
|
|
||||||
private val historyItem = History.Regular(0, "title", "url", 0.toLong(), HistoryItemTimeGroup.timeGroupForTimestamp(0))
|
|
||||||
val controller: HistoryController = mockk(relaxed = true)
|
|
||||||
val interactor = DefaultHistoryInteractor(controller)
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val gleanTestRule = GleanTestRule(testContext)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onOpen() {
|
|
||||||
interactor.open(historyItem)
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleOpen(historyItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onSelect() {
|
|
||||||
interactor.select(historyItem)
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleSelect(historyItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeselect() {
|
|
||||||
interactor.deselect(historyItem)
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleDeselect(historyItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onBackPressed() {
|
|
||||||
every {
|
|
||||||
controller.handleBackPressed()
|
|
||||||
} returns true
|
|
||||||
|
|
||||||
val backpressHandled = interactor.onBackPressed()
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleBackPressed()
|
|
||||||
}
|
|
||||||
assertTrue(backpressHandled)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onModeSwitched() {
|
|
||||||
interactor.onModeSwitched()
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleModeSwitched()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onSearch() {
|
|
||||||
assertNull(GleanHistory.searchIconTapped.testGetValue())
|
|
||||||
interactor.onSearch()
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleSearch()
|
|
||||||
}
|
|
||||||
assertNotNull(GleanHistory.searchIconTapped.testGetValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeleteTimeRange() {
|
|
||||||
interactor.onDeleteTimeRange()
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleDeleteTimeRange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeleteTimeRangeConfirmed() {
|
|
||||||
interactor.onDeleteTimeRangeConfirmed(null)
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleDeleteTimeRangeConfirmed(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onDeleteSome() {
|
|
||||||
val items = setOf(historyItem)
|
|
||||||
|
|
||||||
interactor.onDeleteSome(items)
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleDeleteSome(items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onRequestSync() {
|
|
||||||
interactor.onRequestSync()
|
|
||||||
|
|
||||||
verifyAll {
|
|
||||||
controller.handleRequestSync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue