diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt index 6f802bb60..d0b47eb89 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt @@ -110,7 +110,6 @@ class HomeScreenTest { verifyHomeWordmark() verifyAddTabButton() verifyShareTabsButton(visible = false) - verifyCloseTabsButton(visible = false) verifyPrivateSessionHeader() verifyPrivateSessionMessage(visible = true) verifyHomeToolbar() @@ -130,7 +129,6 @@ class HomeScreenTest { verifyHomeWordmark() verifyAddTabButton() verifyShareTabsButton(visible = true) - verifyCloseTabsButton(visible = true) verifyPrivateSessionHeader() verifyPrivateSessionMessage(visible = false) verifyHomeToolbar() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index ced946432..4675da8fb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -28,8 +28,10 @@ import org.mozilla.fenix.ui.robots.navigationToolbar * - Opening a private tab * - Verifying tab list * - Closing all tabs + * - Close tab + * - Swipe to close tab + * - Undo close tab * - * TODO: Tab Collections */ class TabbedBrowsingTest { @@ -76,7 +78,10 @@ class TabbedBrowsingTest { homeScreen { // Timing issue on slow devices on Firebase - mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), + TestAssetHelper.waitingTime + ) verifyExistingTabList() }.openTabsListThreeDotMenu { @@ -106,10 +111,13 @@ class TabbedBrowsingTest { verifyTabCounter("1") }.openHomeScreen { // Timing issue on slow devices on Firebase - mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), + TestAssetHelper.waitingTime + ) verifyExistingTabList() verifyShareTabsButton(true) - verifyCloseTabsButton(true) + verifyCloseTabsButton("Test_Page_1") }.togglePrivateBrowsingMode() // Verify private tabs remain in private browsing mode @@ -135,7 +143,10 @@ class TabbedBrowsingTest { homeScreen { // Timing issue on slow devices on Firebase - mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), + TestAssetHelper.waitingTime + ) verifyExistingTabList() }.openTabsListThreeDotMenu { verifyCloseAllTabsButton() @@ -147,5 +158,59 @@ class TabbedBrowsingTest { verifyNoTabsOpenedHeader() verifyNoTabsOpenedText() } + + // Repeat for Private Tabs + homeScreen { + }.togglePrivateBrowsingMode() + + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyPageContent(defaultWebPage.content) + }.openHomeScreen { } + + homeScreen { + // Timing issue on slow devices on Firebase + mDevice.waitNotNull( + Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), + TestAssetHelper.waitingTime + ) + verifyExistingTabList() + verifyPrivateTabsCloseTabsButton() + }.closeAllPrivateTabs { + verifyPrivateSessionHeader() + verifyPrivateSessionMessage(true) + } + } + + @Test + fun closeTabTest() { + var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer) + + genericURLS.forEachIndexed { index, element -> + navigationToolbar { + }.openNewTabAndEnterToBrowser(element.url) { + verifyPageContent(element.content) + }.openHomeScreen { } + + homeScreen { + verifyExistingOpenTabs("Test_Page_${index + 1}") + verifyCloseTabsButton("Test_Page_${index + 1}") + closeTabViaXButton("Test_Page_${index + 1}") + verifySnackBarText("Tab closed") + snackBarButtonClick("UNDO") + verifyExistingOpenTabs("Test_Page_${index + 1}") + verifyCloseTabsButton("Test_Page_${index + 1}") + swipeTabRight("Test_Page_${index + 1}") + verifySnackBarText("Tab closed") + snackBarButtonClick("UNDO") + verifyExistingOpenTabs("Test_Page_${index + 1}") + verifyCloseTabsButton("Test_Page_${index + 1}") + swipeTabLeft("Test_Page_${index + 1}") + verifySnackBarText("Tab closed") + snackBarButtonClick("UNDO") + verifyExistingOpenTabs("Test_Page_${index + 1}") + verifyCloseTabsButton("Test_Page_${index + 1}") + } + } } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabsTest.kt deleted file mode 100644 index d8607363c..000000000 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabsTest.kt +++ /dev/null @@ -1,122 +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.ui - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import okhttp3.mockwebserver.MockWebServer -import org.junit.Ignore -import org.junit.Rule -import org.junit.Test -import org.junit.Before -import org.junit.After -import org.mozilla.fenix.helpers.AndroidAssetDispatcher -import org.mozilla.fenix.helpers.HomeActivityTestRule -import org.mozilla.fenix.helpers.TestAssetHelper -import org.mozilla.fenix.ui.robots.homeScreen -import org.mozilla.fenix.ui.robots.navigationToolbar - -/** - * Tests for verifying basic functionality of tabs - * - */ - -class TabsTest { - /* 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 - val activityTestRule = HomeActivityTestRule() - - @Before - fun setUp() { - mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) - start() - } - } - - @After - fun tearDown() { - mockWebServer.shutdown() - } - - @Ignore("This is a stub test, ignore for now") - @Test - fun tabsItemsTest() { - homeScreen { }.dismissOnboarding() - - // Setup browser so that tabs are visible in UI - // Verify all tabs elements are visible: - // "open tabs header, + button, etc. - // Verify tabs 3-dot menu elements - } - - @Ignore("This is a stub test, ignore for now") - @Test - fun noTabsInCacheTest() { - // Verify open tabs header and text exists (when no previous browsing) - // Verify + button redirects to navigation bar UI - // Verify "Collections" header exists - // Verify "No collections" text (when no previous browsing) - } - - @Ignore("This is a stub test, ignore for now") - @Test - fun browsingWithTabsTest() { - // Setup: - // - Verify + button redirects to navigation bar UI - // - Enter mock website via navigation bar - // Verify "Open tabs" header exits - // Verify Collections header exits - // Verify that tabs counter is augmented by 1 count - // Click on tabs counter - // Verify that new page is listed in "Open tabs" - // Repeat for several sites - - } - - @Ignore("This is a stub test, ignore for now") - @Test - fun tabsThreeDotMenuTest() { - // short 3-dot menu setup: - // - create multiple tabs (using mock web server) for the following... - // Verify tabs 3-dot menu functions: - // 1. "Close all tabs" - // 2. "Share tabs" - opens share sub-menu - // 3. "Save to collection" - verify saved to collection - - // NOTE: extended 3 dot menu test is verified in a separate class - } - - @Ignore("This is a stub test, ignore for now") - @Test - fun collectionsTest() { - // Setup: - // - create multiple tabs (using mock web server) for the following... - // Verify collections header exits - // Verify multiple collections can be saved, named - // Verify "Select tabs to save" - // Verify collections dropdown toggle - // Verify send and share button works - opens share menu - - // Verify collections 3-dot menu functions: - // 1. Delete collection - // 2. Rename collection - // 3. Open tabs - } - - @Ignore("This is a sample test, ignore") - @Test - fun sampleTest() { - val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) - - navigationToolbar { - }.enterURLAndEnterToBrowser(defaultWebPage.url) { - } - } -} 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 aa2f264fe..835e0f5a0 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 @@ -14,23 +14,25 @@ import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility -import androidx.test.espresso.matcher.ViewMatchers.withText 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 import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector +import androidx.test.uiautomator.Until 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 import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull /** @@ -80,8 +82,11 @@ class HomeScreenRobot { // Private mode elements fun verifyPrivateSessionHeader() = assertPrivateSessionHeader() fun verifyPrivateSessionMessage(visible: Boolean = true) = assertPrivateSessionMessage(visible) + fun verifyPrivateTabsCloseTabsButton() = assertPrivateTabsCloseTabsButton() + fun verifyShareTabsButton(visible: Boolean = true) = assertShareTabsButton(visible) - fun verifyCloseTabsButton(visible: Boolean = true) = assertCloseTabsButton(visible) + fun verifyCloseTabsButton(title: String) = + assertCloseTabsButton(title) fun verifyExistingTabList() = assertExistingTabList() fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title) @@ -91,29 +96,35 @@ class HomeScreenRobot { collectionThreeDotButton().click() mDevice.waitNotNull(Until.findObject(By.text("Delete collection")), waitingTime) } + fun selectRenameCollection() { onView(allOf(ViewMatchers.withText("Rename collection"))).click() mDevice.waitNotNull(Until.findObject(By.res("name_collection_edittext"))) } + fun selectDeleteCollection() { onView(allOf(ViewMatchers.withText("Delete collection"))).click() mDevice.waitNotNull(Until.findObject(By.res("message")), waitingTime) } + fun confirmDeleteCollection() { onView(allOf(ViewMatchers.withText("DELETE"))).click() mDevice.waitNotNull(Until.findObject(By.res("collections_header")), waitingTime) } + fun typeCollectionName(name: String) { mDevice.wait(Until.findObject(By.res("name_collection_edittext")), waitingTime) collectionNameTextField().perform(ViewActions.replaceText(name)) collectionNameTextField().perform(ViewActions.pressImeActionButton()) } + fun scrollToElementByText(text: String): UiScrollable { val appView = UiScrollable(UiSelector().scrollable(true)) appView.scrollTextIntoView(text) return appView } + fun swipeUpToDismissFirstRun() { scrollToElementByText("Start browsing") } @@ -124,12 +135,31 @@ class HomeScreenRobot { fun togglePrivateBrowsingModeOnOff() { onView(ViewMatchers.withResourceName("privateBrowsingButton")) - .perform(click()) + .perform(click()) } - fun swipeToBottom() = onView(ViewMatchers.withId(R.id.home_component)).perform(ViewActions.swipeUp()) + fun swipeToBottom() = onView(withId(R.id.home_component)).perform(ViewActions.swipeUp()) - fun swipeToTop() = onView(ViewMatchers.withId(R.id.home_component)).perform(ViewActions.swipeDown()) + fun swipeToTop() = onView(withId(R.id.home_component)).perform(ViewActions.swipeDown()) + + fun swipeTabRight(title: String) = + onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeRight()) + + fun swipeTabLeft(title: String) = + onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeLeft()) + + fun closeTabViaXButton(title: String) = closeTabViaX(title) + + fun verifySnackBarText(expectedText: String) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime) + } + + fun snackBarButtonClick(expectedText: String) { + onView(CoreMatchers.allOf(withId(R.id.snackbar_btn), withText(expectedText))).check( + matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) + ).perform(ViewActions.click()) + } class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -167,6 +197,15 @@ class HomeScreenRobot { ThreeDotMenuMainRobot().interact() return ThreeDotMenuMainRobot.Transition() } + + fun closeAllPrivateTabs(interact: HomeScreenRobot.() -> Unit): Transition { + onView(withId(R.id.close_tabs_button)) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + .perform(click()) + + HomeScreenRobot().interact() + return Transition() + } } } @@ -322,8 +361,13 @@ private fun assertYourPrivacyHeader() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertYourPrivacyText() = - onView(CoreMatchers.allOf(withText( - "We’ve designed Firefox Preview to give you control over what you share online and what you share with us."))) + onView( + CoreMatchers.allOf( + withText( + "We’ve designed Firefox Preview to give you control over what you share online and what you share with us." + ) + ) + ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertPrivacyNoticeButton() = @@ -337,7 +381,7 @@ private fun assertStartBrowsingButton() = // Private mode elements private fun assertPrivateSessionHeader() = onView(CoreMatchers.allOf(withText("Private tabs"))) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " + "when you quit the app or close all private tabs. While this doesn’t make you anonymous to websites or " + @@ -354,11 +398,12 @@ private fun assertShareTabsButton(visible: Boolean) = onView(CoreMatchers.allOf(withId(R.id.share_tabs_button), isDisplayed())) .check(matches(withEffectiveVisibility(visibleOrGone(visible)))) -private fun assertCloseTabsButton(visible: Boolean) = - onView(CoreMatchers.allOf(withId(R.id.close_tab_button), isDisplayed())) - .check(matches(withEffectiveVisibility(visibleOrGone(visible)))) +private fun assertCloseTabsButton(title: String) = + onView(allOf(withId(R.id.close_tab_button), withContentDescription("Close tab $title"))) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun visibleOrGone(visibility: Boolean) = if (visibility) Visibility.VISIBLE else Visibility.GONE +private fun visibleOrGone(visibility: Boolean) = + if (visibility) Visibility.VISIBLE else Visibility.GONE private fun assertExistingTabList() = onView(CoreMatchers.allOf(withId(R.id.item_tab))) @@ -375,4 +420,17 @@ private fun tabsListThreeDotButton() = onView(allOf(withId(R.id.tabs_overflow_bu private fun collectionThreeDotButton() = onView(allOf(withId(R.id.collection_overflow_button))) -private fun collectionNameTextField() = onView(allOf(ViewMatchers.withResourceName("name_collection_edittext"))) +private fun collectionNameTextField() = + onView(allOf(ViewMatchers.withResourceName("name_collection_edittext"))) + +private fun closeTabViaX(title: String) { + val closeButton = onView( + allOf( + withId(R.id.close_tab_button), + withContentDescription("Close tab $title") + ) + ) + closeButton.perform(click()) +} + +private fun assertPrivateTabsCloseTabsButton() = onView(allOf(withId(R.id.close_tabs_button)))