From 7813fb22d475701d5087b385150c50696006766f Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Tue, 29 Oct 2019 11:36:19 +0200 Subject: [PATCH] History menu UI tests (#6272) Removed failing verifyOverflowMenuButton method Added new Robot for MultipleSelectionTooolbar Renamed MultipleSelectionToolbarRobot to LibrarySubMenusMultipleSelectionToolbarRobot --- .../androidTest/assets/pages/generic1.html | 2 +- .../org/mozilla/fenix/helpers/TestHelper.kt | 8 + .../java/org/mozilla/fenix/ui/HistoryTest.kt | 242 ++++++++++++++---- .../mozilla/fenix/ui/robots/HistoryRobot.kt | 115 +++++++-- .../fenix/ui/robots/HomeScreenRobot.kt | 9 + ...rySubMenusMultipleSelectionToolbarRobot.kt | 131 ++++++++++ 6 files changed, 446 insertions(+), 61 deletions(-) create mode 100644 app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt diff --git a/app/src/androidTest/assets/pages/generic1.html b/app/src/androidTest/assets/pages/generic1.html index 778816fa6..97bcf87a2 100644 --- a/app/src/androidTest/assets/pages/generic1.html +++ b/app/src/androidTest/assets/pages/generic1.html @@ -1,6 +1,6 @@ -Test_Page_1 + Test_Page_1

diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt index 841e41eaa..8c1f78e57 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt @@ -4,6 +4,10 @@ package org.mozilla.fenix.helpers +import android.net.Uri +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.longClick +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector @@ -13,4 +17,8 @@ object TestHelper { appView.scrollTextIntoView(text) return appView } + + fun longTapSelectItem(url: Uri) { + onView(withText(url.toString())).perform(longClick()) + } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index a6fd232f7..099535257 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -4,29 +4,29 @@ package org.mozilla.fenix.ui -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice +import android.content.Context +import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu +import kotlinx.coroutines.runBlocking +import mozilla.components.browser.storage.sync.PlacesHistoryStorage import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem import org.mozilla.fenix.ui.robots.homeScreen +import org.mozilla.fenix.ui.robots.multipleSelectionToolbar import org.mozilla.fenix.ui.robots.navigationToolbar /** * Tests for verifying basic functionality of history * */ - class HistoryTest { /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping. - - private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) private lateinit var mockWebServer: MockWebServer @get:Rule @@ -43,52 +43,208 @@ class HistoryTest { @After fun tearDown() { mockWebServer.shutdown() + // Clearing all history data after each test to avoid overlapping data + val applicationContext: Context = activityTestRule.activity.applicationContext + val historyStorage = PlacesHistoryStorage(applicationContext) + runBlocking { + historyStorage.deleteEverything() + } } - @Ignore("This is a stub test, ignore for now") @Test fun noHistoryItemsInCacheTest() { - homeScreen { }.dismissOnboarding() - - // Verify "Your Library" in 3-dot menu is visible - // Verify "History" line-item in library is visible - // Verify "No history here" is visible - // Verify "History" UI elements + homeScreen { + }.openThreeDotMenu { + verifyLibraryButton() + }.openLibrary { + verifyHistoryButton() + }.openHistory { + verifyHistoryMenuView() + verifyEmptyHistoryView() + } } - @Ignore("This is a stub test, ignore for now") @Test - fun historyTest() { - // Setup: - // - Visit a URL - // - Visit a second URL - // Verify browser view exists for each visit - // Verify "Your Library" in 3-dot menu is visible - // Click "Your Library" - // Verify "History" line-item in Library is visible - // Click "History" - // Verify "History" UI elements (view is visible) - // Verify history is added, URLs match history added in Library - - // Verify history 3-dot menu functions: - // 1. Delete - - // Verify history visibility in new URL search - - // Verify "Delete history" - // Verify "This will delete all your browsing data." - // Verify "No history here" UI element - - // Verify return to "Your Library" - } - - @Ignore("This is a sample test, ignore") - @Test - fun sampleTest() { - val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + fun visitedUrlHistoryTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) navigationToolbar { - }.enterURLAndEnterToBrowser(defaultWebPage.url) { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + verifyHistoryMenuView() + verifyVisitedTimeTitle() + verifyFirstTestPageTitle("Test_Page_1") + verifyTestPageUrl(firstWebPage.url) + } + } + + @Test + fun deleteHistoryItemTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + openOverflowMenu() + clickThreeDotMenuDelete() + verifyEmptyHistoryView() + } + } + + @Test + fun deleteAllHistoryTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + clickDeleteHistoryButton() + verifyDeleteConfirmationMessage() + confirmDeleteAllHistory() + verifyEmptyHistoryView() + } + } + + @Test + fun multiSelectionToolbarItemsTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + longTapSelectItem(firstWebPage.url) + } + + multipleSelectionToolbar { + verifyMultiSelectionCheckmark() + verifyMultiSelectionCounter() + verifyShareButton() + verifyCloseToolbarButton() + }.closeToolbarReturnToHistory { + verifyHistoryMenuView() + } + } + + @Test + fun openHistoryInNewTabTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + closeTab() + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + longTapSelectItem(firstWebPage.url) + openActionBarOverflowOrOptionsMenu(activityTestRule.getActivity()) + } + + multipleSelectionToolbar { + }.clickOpenNewTab { + verifyExistingTabList() + verifyOpenTabsHeader() + } + } + + @Test + fun openHistoryInPrivateTabTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + longTapSelectItem(firstWebPage.url) + openActionBarOverflowOrOptionsMenu(activityTestRule.getActivity()) + } + + multipleSelectionToolbar { + }.clickOpenPrivateTab { + verifyExistingTabList() + verifyPrivateSessionHeader() + } + } + + @Test + fun deleteMultipleSelectionTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openNavigationToolbar { + }.enterURLAndEnterToBrowser(secondWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + longTapSelectItem(firstWebPage.url) + longTapSelectItem(secondWebPage.url) + openActionBarOverflowOrOptionsMenu(activityTestRule.getActivity()) + } + + multipleSelectionToolbar { + }.clickMultiSelectionDelete { + verifyEmptyHistoryView() + } + } + + @Test + fun shareButtonTest() { + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openHomeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + longTapSelectItem(firstWebPage.url) + } + + multipleSelectionToolbar { + clickShareButton() + verifyShareOverlay() + verifyShareTabFavicon() + verifyShareTabTitle() + verifyShareTabUrl() + } + } + + @Test + fun verifyBackNavigation() { + homeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + }.goBack { + verifyLibraryView() + } + } + + @Test + fun verifyCloseMenu() { + homeScreen { + }.openThreeDotMenu { + }.openLibrary { + }.openHistory { + }.closeMenu { + verifyHomeScreen() } } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt index f8465d671..b4f01d0b5 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt @@ -1,20 +1,60 @@ +/* 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 android.net.Uri import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.matcher.ViewMatchers.withParent -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility -import androidx.test.espresso.matcher.ViewMatchers.Visibility +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.RootMatchers.isDialog +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withParent +import androidx.test.espresso.matcher.ViewMatchers.withText +import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf import org.mozilla.fenix.R import org.mozilla.fenix.helpers.click +/** + * Implementation of Robot Pattern for the history menu. + */ class HistoryRobot { - fun verifyHistoryMenuView() = assertHistoryView() + fun verifyHistoryMenuView() = assertHistoryMenuView() + + fun verifyEmptyHistoryView() = assertEmptyHistoryView() + + fun verifyVisitedTimeTitle() = assertVisitedTimeTitle() + + fun verifyFirstTestPageTitle(title: String) = assertTestPageTitle(title) + + fun verifyTestPageUrl(expectedUrl: Uri) = assertPageUrl(expectedUrl) + + fun verifyDeleteConfirmationMessage() = assertDeleteConfirmationMessage() + + fun openOverflowMenu() { + overflowMenu().click() + } + + fun clickThreeDotMenuDelete() { + threeDotMenuDeleteButton().click() + } + + fun clickDeleteHistoryButton() { + deleteAllHistoryButton().click() + } + + fun confirmDeleteAllHistory() { + onView(withText("Delete")) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + .click() + } class Transition { fun goBack(interact: LibraryRobot.() -> Unit): LibraryRobot.Transition { @@ -23,17 +63,58 @@ class HistoryRobot { LibraryRobot().interact() return LibraryRobot.Transition() } + + fun closeMenu(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { + closeButton().click() + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } } } -fun historyMenu(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition { - HistoryRobot().interact() - return HistoryRobot.Transition() -} - -private fun assertHistoryView() { - onView(allOf(withText("History"), withParent(withId(R.id.navigationToolbar)))) - .check(ViewAssertions.matches(withEffectiveVisibility(Visibility.VISIBLE))) -} - private fun goBackButton() = onView(withContentDescription("Navigate up")) + +private fun testPageTitle() = onView(allOf(withId(R.id.title), withText("Test_Page_1"))) + +private fun pageUrl() = onView(withId(R.id.url)) + +private fun overflowMenu() = onView(withId(R.id.overflow_menu)) + +private fun threeDotMenuDeleteButton() = onView(withId(R.id.simple_text)) + +private fun deleteAllHistoryButton() = onView(withId(R.id.delete_button)) + +private fun assertHistoryMenuView() { + onView( + allOf(withText("History"), withParent(withId(R.id.navigationToolbar))) + ) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) +} + +private fun assertEmptyHistoryView() = + onView( + allOf( + withId(R.id.history_empty_view), + withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + ) + ) + .check(matches(withText("No history here"))) + +private fun assertVisitedTimeTitle() = + onView(withId(R.id.header_title)).check(matches(withText("Last 24 hours"))) + +private fun assertTestPageTitle(title: String) = testPageTitle() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withText(title))) + +private fun assertPageUrl(expectedUrl: Uri) = pageUrl() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withText(Matchers.containsString(expectedUrl.toString())))) + +private fun assertDeleteConfirmationMessage() = + onView(withText("This will delete all of your browsing data.")) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + +private fun closeButton() = onView(withId(R.id.libraryClose)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt index a82cedd66..364ec359f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt @@ -16,6 +16,8 @@ import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.hasFocus +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.Until import androidx.test.uiautomator.By @@ -26,6 +28,7 @@ import org.hamcrest.CoreMatchers import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.containsString import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.click @@ -113,6 +116,10 @@ class HomeScreenRobot { scrollToElementByText("Start browsing") } + fun closeTab() { + closeTabButton().click() + } + class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -165,6 +172,8 @@ val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) private fun navigationToolbar() = onView(CoreMatchers.allOf(ViewMatchers.withText("Search or enter address"))) +private fun closeTabButton() = onView(withId(R.id.close_tab_button)) + private fun assertNavigationToolbar() = onView(CoreMatchers.allOf(ViewMatchers.withText("Search or enter address"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt new file mode 100644 index 000000000..607bd67bf --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt @@ -0,0 +1,131 @@ +/* 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.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.withId +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.waitingTime +import org.mozilla.fenix.helpers.click +import org.mozilla.fenix.helpers.ext.waitNotNull + +/* + * Implementation of Robot Pattern for the multiple selection toolbar of History and Bookmarks menus. + */ +class LibrarySubMenusMultipleSelectionToolbarRobot { + + fun verifyMultiSelectionCheckmark() = assertMultiSelectionCheckmark() + + fun verifyMultiSelectionCounter() = assertMultiSelectionCounter() + + fun verifyShareButton() = assertShareButton() + + fun verifyShareOverlay() = assertShareOverlay() + + fun verifyShareTabFavicon() = assertShareTabFavicon() + + fun verifyShareTabTitle() = assertShareTabTitle() + + fun verifyShareTabUrl() = assertShareTabUrl() + + fun verifyCloseToolbarButton() = assertCloseToolbarButton() + + fun clickShareButton() { + shareButton().click() + + mDevice.waitNotNull( + Until.findObject( + By.text("SHARE A LINK") + ), waitingTime + ) + } + + class Transition { + fun closeToolbarReturnToHistory(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition { + closeToolbarButton().click() + + HistoryRobot().interact() + return HistoryRobot.Transition() + } + + fun closeToolbarReturnToBookmarks(interact: BookmarksRobot.() -> Unit): BookmarksRobot.Transition { + closeToolbarButton().click() + + BookmarksRobot().interact() + return BookmarksRobot.Transition() + } + + fun clickOpenNewTab(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { + openInNewTabButton().click() + mDevice.waitNotNull(Until.findObject(By.text("Open tabs")), waitingTime) + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } + + fun clickOpenPrivateTab(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { + openInPrivateTabButton().click() + mDevice.waitNotNull( + Until.findObject(By.text("Private session")), + waitingTime + ) + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } + + fun clickMultiSelectionDelete(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition { + multiSelectionDeleteButton().click() + + HistoryRobot().interact() + return HistoryRobot.Transition() + } + } +} + +fun multipleSelectionToolbar(interact: LibrarySubMenusMultipleSelectionToolbarRobot.() -> Unit): LibrarySubMenusMultipleSelectionToolbarRobot.Transition { + + LibrarySubMenusMultipleSelectionToolbarRobot().interact() + return LibrarySubMenusMultipleSelectionToolbarRobot.Transition() +} + +private fun closeToolbarButton() = onView(withContentDescription("Navigate up")) + +private fun shareButton() = onView(withId(R.id.share_history_multi_select)) + +private fun openInNewTabButton() = onView(withText("Open in new tab")) + +private fun openInPrivateTabButton() = onView(withText("Open in private tab")) + +private fun multiSelectionDeleteButton() = onView(withText("Delete")) + +private fun assertMultiSelectionCheckmark() = + onView(withId(R.id.checkmark)) + .check(matches(isDisplayed())) + +private fun assertMultiSelectionCounter() = + onView(withText("1 selected")).check(matches(isDisplayed())) + +private fun assertShareButton() = + shareButton().check(matches(isDisplayed())) + +private fun assertShareOverlay() = + onView(withId(R.id.shareWrapper)).check(matches(isDisplayed())) + +private fun assertShareTabTitle() = + onView(withId(R.id.share_tab_title)).check(matches(isDisplayed())) + +private fun assertShareTabFavicon() = + onView(withId(R.id.share_tab_favicon)).check(matches(isDisplayed())) + +private fun assertShareTabUrl() = onView(withId(R.id.share_tab_url)) + +private fun assertCloseToolbarButton() = closeToolbarButton().check(matches(isDisplayed()))