Commit 9f3bbf1f authored by Mugurell's avatar Mugurell Committed by Emily Kager
Browse files

For #8651 - Add HistoryInteractor/Controller unit/ui tests

parent fc0260d6
Loading
Loading
Loading
Loading
+73 −2
Original line number Diff line number Diff line
@@ -80,6 +80,77 @@ class HistoryTest {
        }
    }

    @Test
    fun copyHistoryItemURLTest() {
        val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

        navigationToolbar {
        }.enterURLAndEnterToBrowser(firstWebPage.url) {
            verifyPageContent("Page content: 1")
        }.openThreeDotMenu {
        }.openLibrary {
        }.openHistory {
        }.openThreeDotMenu {
        }.clickCopy {
            verifyCopySnackBarText()
        }
    }

    @Test
    fun shareHistoryItemTest() {
        val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

        navigationToolbar {
        }.enterURLAndEnterToBrowser(firstWebPage.url) {
            verifyPageContent("Page content: 1")
        }.openThreeDotMenu {
        }.openLibrary {
        }.openHistory {
        }.openThreeDotMenu {
        }.clickShare {
            verifyShareOverlay()
            verifyShareTabFavicon()
            verifyShareTabTitle()
            verifyShareTabUrl()
        }
    }

    @Test
    fun openHistoryItemInNewTabTest() {
        val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

        navigationToolbar {
        }.enterURLAndEnterToBrowser(firstWebPage.url) {
            verifyPageContent("Page content: 1")
        }.openThreeDotMenu {
        }.openLibrary {
        }.openHistory {
        }.openThreeDotMenu {
        }.clickOpenInNormalTab {
            verifyPageContent(firstWebPage.content)
        }.openHomeScreen {
            verifyOpenTabsHeader()
        }
    }

    @Test
    fun openHistoryItemInNewPrivateTabTest() {
        val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

        navigationToolbar {
        }.enterURLAndEnterToBrowser(firstWebPage.url) {
            verifyPageContent("Page content: 1")
        }.openThreeDotMenu {
        }.openLibrary {
        }.openHistory {
        }.openThreeDotMenu {
        }.clickOpenInPrivateTab {
            verifyPageContent(firstWebPage.content)
        }.openHomeScreen {
            verifyPrivateSessionHeader()
        }
    }

    @Test
    fun deleteHistoryItemTest() {
        val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
@@ -90,8 +161,8 @@ class HistoryTest {
        }.openThreeDotMenu {
        }.openLibrary {
        }.openHistory {
            openOverflowMenu()
            clickThreeDotMenuDelete()
        }.openThreeDotMenu {
        }.clickDelete {
            verifyEmptyHistoryView()
        }
    }
+16 −7
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ class HistoryRobot {

    fun verifyTestPageUrl(expectedUrl: Uri) = assertPageUrl(expectedUrl)

    fun verifyCopySnackBarText() = assertCopySnackBarText()

    fun verifyDeleteConfirmationMessage() = assertDeleteConfirmationMessage()

    fun verifyHomeScreen() = HomeScreenRobot().verifyHomeScreen()
@@ -66,11 +68,7 @@ class HistoryRobot {
            ),
            waitingTime
        )
        overflowMenu().click()
    }

    fun clickThreeDotMenuDelete() {
        threeDotMenuDeleteButton().click()
        threeDotMenu().click()
    }

    fun clickDeleteHistoryButton() {
@@ -92,6 +90,15 @@ class HistoryRobot {
            HistoryRobot().interact()
            return Transition()
        }

        fun openThreeDotMenu(interact: ThreeDotMenuHistoryItemRobot.() -> Unit):
            ThreeDotMenuHistoryItemRobot.Transition {

            threeDotMenu().click()

            ThreeDotMenuHistoryItemRobot().interact()
            return ThreeDotMenuHistoryItemRobot.Transition()
        }
    }
}

@@ -106,9 +113,9 @@ private fun testPageTitle() = onView(allOf(withId(R.id.title), withText("Test_Pa

private fun pageUrl() = onView(withId(R.id.url))

private fun overflowMenu() = onView(withId(R.id.overflow_menu))
private fun threeDotMenu() = onView(withId(R.id.overflow_menu))

private fun threeDotMenuDeleteButton() = onView(withId(R.id.simple_text))
private fun snackBarText() = onView(withId(R.id.snackbar_text))

private fun deleteAllHistoryButton() = onView(withId(R.id.delete_button))

@@ -143,3 +150,5 @@ private fun assertDeleteConfirmationMessage() =
    onView(withText("This will delete all of your browsing data."))
        .inRoot(isDialog())
        .check(matches(isDisplayed()))

private fun assertCopySnackBarText() = snackBarText().check(matches(withText("URL copied")))
+74 −0
Original line number Diff line number Diff line
/* 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.ui.robots

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull

/**
 * Implementation of Robot Pattern for the History three dot menu.
 */
class ThreeDotMenuHistoryItemRobot {

    class Transition {

        fun clickCopy(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition {
            copyButton().click()
            HistoryRobot().interact()
            return HistoryRobot.Transition()
        }

        fun clickShare(interact: LibrarySubMenusMultipleSelectionToolbarRobot.() -> Unit):
            LibrarySubMenusMultipleSelectionToolbarRobot.Transition {

            shareButton().click()

            mDevice.waitNotNull(
                Until.findObject(
                    By.text("ALL ACTIONS")
                ), TestAssetHelper.waitingTime
            )

            LibrarySubMenusMultipleSelectionToolbarRobot().interact()
            return LibrarySubMenusMultipleSelectionToolbarRobot.Transition()
        }

        fun clickOpenInNormalTab(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
            openInNewNormalTabButton().click()
            BrowserRobot().interact()
            return BrowserRobot.Transition()
        }

        fun clickOpenInPrivateTab(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
            openInNewPrivateTabButton().click()
            BrowserRobot().interact()
            return BrowserRobot.Transition()
        }

        fun clickDelete(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition {
            deleteButton().click()
            HistoryRobot().interact()
            return HistoryRobot.Transition()
        }
    }
}

private fun copyButton() = onView(withText(R.string.history_menu_copy_button))

private fun shareButton() = onView(withText(R.string.history_menu_share_button))

private fun openInNewNormalTabButton() =
    onView(withText(R.string.history_menu_open_in_new_tab_button))

private fun openInNewPrivateTabButton() =
    onView(withText(R.string.history_menu_open_in_private_tab_button))

private fun deleteButton() = onView(withText(R.string.history_delete_item))
+121 −62
Original line number Diff line number Diff line
@@ -4,20 +4,58 @@

package org.mozilla.fenix.library.history

import android.content.ClipData
import android.content.ClipboardManager
import android.content.res.Resources
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import assertk.assertAll
import assertk.assertThat
import assertk.assertions.hasSize
import assertk.assertions.isEqualTo
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import mozilla.components.concept.engine.prompt.ShareData
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Test

import org.junit.runner.RunWith
import org.mozilla.fenix.TestApplication
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.FenixSnackbar
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

// Robolectric needed for `onShareItem()`
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApplication::class)
class HistoryControllerTest {

    private val historyItem = HistoryItem(0, "title", "url", 0.toLong())
    private val store: HistoryFragmentStore = mockk(relaxed = true)
    private val state: HistoryFragmentState = mockk(relaxed = true)
    private val navController: NavController = mockk(relaxed = true)
    private val resources: Resources = mockk(relaxed = true)
    private val snackbar: FenixSnackbar = mockk(relaxed = true)
    private val clipboardManager: ClipboardManager = mockk(relaxed = true)
    private val openInBrowser: (HistoryItem, BrowsingMode?) -> Unit = mockk(relaxed = true)
    private val displayDeleteAll: () -> Unit = mockk(relaxed = true)
    private val invalidateOptionsMenu: () -> Unit = mockk(relaxed = true)
    private val deleteHistoryItems: (Set<HistoryItem>) -> Unit = mockk(relaxed = true)
    private val controller = DefaultHistoryController(
        store,
        navController,
        resources,
        snackbar,
        clipboardManager,
        openInBrowser,
        displayDeleteAll,
        invalidateOptionsMenu,
        deleteHistoryItems
    )

    @Before
    fun setUp() {
@@ -26,34 +64,35 @@ class HistoryControllerTest {

    @Test
    fun onPressHistoryItemInNormalMode() {
        var historyItemReceived: HistoryItem? = null
        controller.handleOpen(historyItem)

        every { state.mode } returns HistoryFragmentState.Mode.Normal
        verify {
            openInBrowser(historyItem, null)
        }
    }

        val controller = DefaultHistoryController(
            store,
            { historyItemReceived = it },
            mockk(),
            mockk(),
            mockk()
        )
    @Test
    fun onOpenItemInNormalMode() {
        controller.handleOpen(historyItem, BrowsingMode.Normal)

        controller.handleOpen(historyItem)
        assertEquals(historyItem, historyItemReceived)
        verify {
            openInBrowser(historyItem, BrowsingMode.Normal)
        }
    }

    @Test
    fun onOpenItemInPrivateMode() {
        controller.handleOpen(historyItem, BrowsingMode.Private)

        verify {
            openInBrowser(historyItem, BrowsingMode.Private)
        }
    }

    @Test
    fun onPressHistoryItemInEditMode() {
        every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf())

        val controller = DefaultHistoryController(
            store,
            { },
            mockk(),
            mockk(),
            mockk()
        )

        controller.handleSelect(historyItem)

        verify {
@@ -65,14 +104,6 @@ class HistoryControllerTest {
    fun onPressSelectedHistoryItemInEditMode() {
        every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf(historyItem))

        val controller = DefaultHistoryController(
            store,
            { },
            mockk(),
            mockk(),
            mockk()
        )

        controller.handleDeselect(historyItem)

        verify {
@@ -84,7 +115,6 @@ class HistoryControllerTest {
    fun onBackPressedInNormalMode() {
        every { state.mode } returns HistoryFragmentState.Mode.Normal

        val controller = DefaultHistoryController(store, mockk(), mockk(), mockk(), mockk())
        assertFalse(controller.handleBackPressed())
    }

@@ -92,9 +122,7 @@ class HistoryControllerTest {
    fun onBackPressedInEditMode() {
        every { state.mode } returns HistoryFragmentState.Mode.Editing(setOf())

        val controller = DefaultHistoryController(store, mockk(), mockk(), mockk(), mockk())
        assertTrue(controller.handleBackPressed())

        verify {
            store.dispatch(HistoryFragmentAction.ExitEditMode)
        }
@@ -102,45 +130,76 @@ class HistoryControllerTest {

    @Test
    fun onModeSwitched() {
        var menuInvalidated = false
        val controller = DefaultHistoryController(
            mockk(),
            mockk(),
            mockk(),
            { menuInvalidated = true },
            mockk()
        )
        controller.handleModeSwitched()
        assertEquals(true, menuInvalidated)

        verify {
            invalidateOptionsMenu.invoke()
        }
    }

    @Test
    fun onDeleteAll() {
        var deleteAllDialogShown = false
        val controller = DefaultHistoryController(
            mockk(),
            mockk(),
            { deleteAllDialogShown = true },
            mockk(),
            mockk()
        )
        controller.handleDeleteAll()
        assertEquals(true, deleteAllDialogShown)

        verify {
            displayDeleteAll.invoke()
        }
    }

    @Test
    fun onDeleteSome() {
        var itemsToDelete: Set<HistoryItem>? = null
        val historyItem = HistoryItem(0, "title", "url", 0.toLong())
        val newHistoryItem = HistoryItem(1, "title", "url", 0.toLong())
        val controller = DefaultHistoryController(
                mockk(),
                mockk(),
                mockk(),
                mockk(),
                { itemsToDelete = it }
        val itemsToDelete = setOf(historyItem)

        controller.handleDeleteSome(itemsToDelete)

        verify {
            deleteHistoryItems(itemsToDelete)
        }
    }

    @Test
    fun onCopyItem() {
        val clipdata = slot<ClipData>()

        controller.handleCopyUrl(historyItem)

        verify {
            clipboardManager.primaryClip = capture(clipdata)
            snackbar.show()
        }
        assertAll {
            assertEquals(clipdata.captured.itemCount, 1)
            assertThat(clipdata.captured.description.label).isEqualTo(historyItem.url)
            assertThat(clipdata.captured.getItemAt(0).text).isEqualTo(historyItem.url)
        }
    }

    @Test
    @Suppress("UNCHECKED_CAST")
    fun onShareItem() {
        val directions = slot<NavDirections>()

        controller.handleShare(historyItem)

        // `verify` checks for referential equality.
        // This would fail as the NavDirections are created and used in place in the tested method.
        // Capture the NavDirections and `assert` for structural equality after.
        verify {
            navController.navigate(
                capture(directions)
            )
        controller.handleDeleteSome(setOf(historyItem, newHistoryItem))
        assertEquals(itemsToDelete, setOf(historyItem, newHistoryItem))
        }
        assertAll {
            // The below class is private, can't easily assert using `instanceOf`
            assertEquals(
                directions.captured::class.simpleName,
                "ActionHistoryFragmentToShareFragment"
            )
            assertThat(directions.captured.arguments["data"] as Array<ShareData>).hasSize(1)
            assertThat(((directions.captured.arguments["data"] as Array<ShareData>)[0]).title)
                .isEqualTo(historyItem.title)
            assertThat(((directions.captured.arguments["data"] as Array<ShareData>)[0]).url)
                .isEqualTo(historyItem.url)
        }
    }
}
+124 −0
Original line number Diff line number Diff line
/* 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 assertk.assertThat
import assertk.assertions.isTrue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verifyAll
import org.junit.Test
import org.mozilla.fenix.browser.browsingmode.BrowsingMode

class HistoryInteractorTest {
    private val historyItem = HistoryItem(0, "title", "url", 0.toLong())
    val controller: HistoryController = mockk(relaxed = true)
    val interactor = HistoryInteractor(controller)

    @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()
        }
        assertThat(backpressHandled).isTrue()
    }

    @Test
    fun onModeSwitched() {
        interactor.onModeSwitched()

        verifyAll {
            controller.handleModeSwitched()
        }
    }

    @Test
    fun onCopyPressed() {
        interactor.onCopyPressed(historyItem)

        verifyAll {
            controller.handleCopyUrl(historyItem)
        }
    }

    @Test
    fun onSharePressed() {
        interactor.onSharePressed(historyItem)

        verifyAll {
            controller.handleShare(historyItem)
        }
    }

    @Test
    fun onOpenInNormalTab() {
        interactor.onOpenInNormalTab(historyItem)

        verifyAll {
            controller.handleOpen(historyItem, BrowsingMode.Normal)
        }
    }

    @Test
    fun onOpenInPrivateTab() {
        interactor.onOpenInPrivateTab(historyItem)

        verifyAll {
            controller.handleOpen(historyItem, BrowsingMode.Private)
        }
    }

    @Test
    fun onDeleteAll() {
        interactor.onDeleteAll()

        verifyAll {
            controller.handleDeleteAll()
        }
    }

    @Test
    fun onDeleteSome() {
        val items = setOf(historyItem)

        interactor.onDeleteSome(items)
        verifyAll {
            controller.handleDeleteSome(items)
        }
    }
}