From 399df170629bbb164ed84912287248559dddc31a Mon Sep 17 00:00:00 2001 From: Aaron Train Date: Mon, 13 Jan 2020 12:24:23 -0500 Subject: [PATCH] Closes #7611: Add UI tests for content context menus (#7631) Closes #7611: Add UI tests for content context menus --- .../androidTest/assets/pages/generic4.html | 19 ++ .../androidTest/assets/resources/rabbit.jpg | Bin 0 -> 5038 bytes .../org/mozilla/fenix/helpers/Constants.kt | 5 + .../mozilla/fenix/helpers/TestAssetHelper.kt | 8 +- .../org/mozilla/fenix/helpers/TestHelper.kt | 13 +- .../org/mozilla/fenix/ui/ContextMenusTest.kt | 220 ++++++++++++++++++ .../mozilla/fenix/ui/robots/BrowserRobot.kt | 177 +++++++++++++- .../mozilla/fenix/ui/robots/DownloadRobot.kt | 60 ++++- .../fenix/ui/robots/HomeScreenRobot.kt | 10 + .../fenix/ui/robots/NavigationToolbarRobot.kt | 23 ++ 10 files changed, 526 insertions(+), 9 deletions(-) create mode 100644 app/src/androidTest/assets/pages/generic4.html create mode 100644 app/src/androidTest/assets/resources/rabbit.jpg create mode 100644 app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt diff --git a/app/src/androidTest/assets/pages/generic4.html b/app/src/androidTest/assets/pages/generic4.html new file mode 100644 index 000000000..78a55d22b --- /dev/null +++ b/app/src/androidTest/assets/pages/generic4.html @@ -0,0 +1,19 @@ + + + Test_Page_4 + + +

Page content: 4

+ Link 1 + Link 2 + Link 3 +

+ + + +

+

+ +

+ + diff --git a/app/src/androidTest/assets/resources/rabbit.jpg b/app/src/androidTest/assets/resources/rabbit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3225407b1cdddb8c17b5db0b0b983ac47d37c401 GIT binary patch literal 5038 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 YuF0XQ6C_^gUtD+k5?uO{O3&v02j9#9D*ylh literal 0 HcmV?d00001 diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt index b83e20a72..a14d459d3 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt @@ -4,5 +4,10 @@ object Constants { object PackageName { const val GOOGLE_PLAY_SERVICES = "com.android.vending" + const val GOOGLE_APPS_PHOTOS = "com.google.android.apps.photos" + } + + object LongClickDuration { + const val LONG_CLICK_DURATION: Long = 5000 } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt index e2fc73027..be7724396 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt @@ -30,7 +30,7 @@ object TestAssetHelper { */ fun getGenericAssets(server: MockWebServer): List { @Suppress("MagicNumber") - return (1..3).map { + return (1..4).map { TestAsset( server.url("pages/generic$it.html").toString().toUri()!!, "Page content: $it" @@ -77,4 +77,10 @@ object TestAssetHelper { return TestAsset(url, "") } + + fun getImageAsset(server: MockWebServer): TestAsset { + val url = server.url("resources/rabbit.jpg").toString().toUri()!! + + return TestAsset(url, "") + } } 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 b671cd4d7..d82f47731 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt @@ -4,7 +4,10 @@ package org.mozilla.fenix.helpers +import android.content.Context import android.net.Uri +import android.os.Build +import android.preference.PreferenceManager import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.longClick import androidx.test.espresso.matcher.ViewMatchers.withId @@ -17,8 +20,6 @@ import org.hamcrest.CoreMatchers.allOf import org.mozilla.fenix.R import org.mozilla.fenix.helpers.ext.waitNotNull import org.mozilla.fenix.ui.robots.mDevice -import android.preference.PreferenceManager -import android.content.Context object TestHelper { fun scrollToElementByText(text: String): UiScrollable { @@ -46,4 +47,12 @@ object TestHelper { editor.putInt(pref, value) editor.apply() } + + fun getPermissionAllowID(): String { + return when + (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + true -> "com.android.permissioncontroller" + false -> "com.android.packageinstaller" + } + } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt new file mode 100644 index 000000000..1c6da3843 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt @@ -0,0 +1,220 @@ +/* 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.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.helpers.AndroidAssetDispatcher +import org.mozilla.fenix.helpers.HomeActivityIntentTestRule +import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.ui.robots.downloadRobot +import org.mozilla.fenix.ui.robots.navigationToolbar + +/** + * Tests for verifying basic functionality of content context menus + * + * - Verifies long click "Open link in new tab" UI and functionality + * - Verifies long click "Open link in new Private tab" UI and functionality + * - Verifies long click "Copy Link" UI and functionality + * - Verifies long click "Share Link" UI and functionality + * - Verifies long click "Open image in new tab" UI and functionality + * - Verifies long click "Save Image" UI and functionality + * - Verifies long click "Copy image location" UI and functionality + * - Verifies long click items of mixed hypertext items + * + */ + +class ContextMenusTest { + private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + private lateinit var mockWebServer: MockWebServer + + @get:Rule + val activityIntentTestRule = HomeActivityIntentTestRule() + + @Before + fun setUp() { + mockWebServer = MockWebServer().apply { + setDispatcher(AndroidAssetDispatcher()) + start() + } + } + + @After + fun tearDown() { + mockWebServer.shutdown() + } + + @Test + fun verifyContextOpenLinkNewTab() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val genericURL = + TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("Link 1") + verifyLinkContextMenuItems(genericURL.url) + clickContextOpenLinkInNewTab() + verifySnackBarText("New tab opened") + snackBarButtonClick("Switch") + verifyUrl(genericURL.url.toString()) + }.openHomeScreen { + verifyHomeScreen() + verifyExistingOpenTabs("Test_Page_1") + verifyExistingOpenTabs("Test_Page_4") + } + } + + @Test + fun verifyContextOpenLinkPrivateTab() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val genericURL = + TestAssetHelper.getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("Link 2") + verifyLinkContextMenuItems(genericURL.url) + clickContextOpenLinkInPrivateTab() + verifySnackBarText("New private tab opened") + snackBarButtonClick("Switch") + verifyUrl(genericURL.url.toString()) + }.openHomeScreen { + verifyPrivateSessionHeader() + verifyExistingOpenTabs("Test_Page_2") + } + } + + @Test + fun verifyContextCopyLink() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val genericURL = + TestAssetHelper.getGenericAsset(mockWebServer, 3) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("Link 3") + verifyLinkContextMenuItems(genericURL.url) + clickContextCopyLink() + verifySnackBarText("Link copied to clipboard") + }.openNavigationToolbar { + }.visitLinkFromClipboard(genericURL.url) { + verifyUrl(genericURL.url.toString()) + } + } + + @Test + fun verifyContextShareLink() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val genericURL = + TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("Link 1") + verifyLinkContextMenuItems(genericURL.url) + clickContextShareLink(genericURL.url) // verify share intent is matched with associated URL + } + } + + @Test + fun verifyContextOpenImageNewTab() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val imageResource = + TestAssetHelper.getImageAsset(mockWebServer) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("test_link_image") + verifyLinkImageContextMenuItems(imageResource.url) + clickContextOpenImageNewTab() + verifySnackBarText("New tab opened") + snackBarButtonClick("Switch") + verifyUrl(imageResource.url.toString()) + } + } + + @Test + fun verifyContextCopyImageLocation() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val imageResource = + TestAssetHelper.getImageAsset(mockWebServer) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("test_link_image") + verifyLinkImageContextMenuItems(imageResource.url) + clickContextCopyImageLocation() + verifySnackBarText("Link copied to clipboard") + }.openNavigationToolbar { + }.visitLinkFromClipboard(imageResource.url) { + verifyUrl(imageResource.url.toString()) + } + } + + @Test + fun verifyContextSaveImage() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val imageResource = + TestAssetHelper.getImageAsset(mockWebServer) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("test_link_image") + verifyLinkImageContextMenuItems(imageResource.url) + clickContextSaveImage() + } + + downloadRobot { + }.clickAllowPermission { + verifyDownloadNotificationPopup() + }.clickOpen("image/jpeg") {} // verify open intent is matched with associated data type + downloadRobot { + verifyPhotosAppOpens() + } + } + + @Test + fun verifyContextMixedVariations() { + val pageLinks = + TestAssetHelper.getGenericAsset(mockWebServer, 4) + val genericURL = + TestAssetHelper.getGenericAsset(mockWebServer, 1) + val imageResource = + TestAssetHelper.getImageAsset(mockWebServer) + + navigationToolbar { + }.enterURLAndEnterToBrowser(pageLinks.url) { + verifyPageContent(pageLinks.content) + longClickMatchingText("Link 1") + verifyLinkContextMenuItems(genericURL.url) + mDevice.pressBack() + longClickMatchingText("test_link_image") + verifyLinkImageContextMenuItems(imageResource.url) + mDevice.pressBack() + longClickMatchingText("test_no_link_image") + verifyNoLinkImageContextMenuItems("test_no_link_image") + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index b99c96803..b0964dea3 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -6,10 +6,14 @@ package org.mozilla.fenix.ui.robots +import android.content.Intent import android.net.Uri import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.BundleMatchers +import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId @@ -19,11 +23,13 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers +import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull +import org.mozilla.fenix.helpers.Constants.LongClickDuration class BrowserRobot { @@ -34,7 +40,10 @@ class BrowserRobot { fun verifyUrl(redirectUrl: String) { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - mDevice.waitNotNull(Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view")), + TestAssetHelper.waitingTime + ) onView(withId(R.id.mozac_browser_toolbar_url_view)) .check(matches(withText(containsString(redirectUrl)))) } @@ -64,9 +73,157 @@ class BrowserRobot { .check((matches(withText(containsString(expectedText))))) } + fun verifySnackBarText(expectedText: String) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime) + } + + fun verifyLinkContextMenuItems(containsURL: Uri) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.textContains(containsURL.toString())), + TestAssetHelper.waitingTime + ) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in new tab")), + TestAssetHelper.waitingTime + ) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in private tab")), + TestAssetHelper.waitingTime + ) + mDevice.waitNotNull(Until.findObject(By.text("Copy link")), TestAssetHelper.waitingTime) + mDevice.waitNotNull(Until.findObject(By.text("Share link")), TestAssetHelper.waitingTime) + } + + fun verifyLinkImageContextMenuItems(containsURL: Uri) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.textContains(containsURL.toString()))) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in new tab")), TestAssetHelper.waitingTime + ) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in private tab")), TestAssetHelper.waitingTime + ) + mDevice.waitNotNull(Until.findObject(By.text("Copy link")), TestAssetHelper.waitingTime) + mDevice.waitNotNull(Until.findObject(By.text("Share link")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObject(By.text("Open image in new tab")), TestAssetHelper.waitingTime + ) + mDevice.waitNotNull(Until.findObject(By.text("Save image")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObject(By.text("Copy image location")), TestAssetHelper.waitingTime + ) + } + + fun verifyNoLinkImageContextMenuItems(containsTitle: String) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.textContains(containsTitle))) + mDevice.waitNotNull( + Until.findObject(By.text("Open image in new tab")), + TestAssetHelper.waitingTime + ) + mDevice.waitNotNull(Until.findObject(By.text("Save image")), TestAssetHelper.waitingTime) + mDevice.waitNotNull( + Until.findObject(By.text("Copy image location")), TestAssetHelper.waitingTime + ) + } + + fun clickContextOpenLinkInNewTab() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in new tab")), + TestAssetHelper.waitingTime + ) + + val menuOpenInNewTab = mDevice.findObject(By.text("Open link in new tab")) + menuOpenInNewTab.click() + } + + fun clickContextOpenLinkInPrivateTab() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.text("Open link in private tab")), + TestAssetHelper.waitingTime + ) + + val menuOpenInPrivateTab = mDevice.findObject(By.text("Open link in private tab")) + menuOpenInPrivateTab.click() + } + + fun clickContextCopyLink() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text("Copy link")), TestAssetHelper.waitingTime) + + val menuCopyLink = mDevice.findObject(By.text("Copy link")) + menuCopyLink.click() + } + + fun clickContextShareLink(url: Uri) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text("Share link")), TestAssetHelper.waitingTime) + + val menuShareLink = mDevice.findObject(By.text("Share link")) + menuShareLink.click() + + // verify share intent is launched and matched with associated passed in URL + Intents.intended( + allOf( + IntentMatchers.hasAction(Intent.ACTION_CHOOSER), + IntentMatchers.hasExtras( + allOf( + BundleMatchers.hasEntry( + Intent.EXTRA_INTENT, + allOf( + IntentMatchers.hasAction(Intent.ACTION_SEND), + IntentMatchers.hasType("text/plain"), + IntentMatchers.hasExtra( + Intent.EXTRA_TEXT, + url.toString() + ) + ) + ) + ) + ) + ) + ) + } + + fun clickContextCopyImageLocation() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.text("Copy image location")), + TestAssetHelper.waitingTime + ) + + val menuCopyImageLocation = mDevice.findObject(By.text("Copy image location")) + menuCopyImageLocation.click() + } + + fun clickContextOpenImageNewTab() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.text("Open image in new tab")), + TestAssetHelper.waitingTime + ) + + val menuOpenImageNewTab = mDevice.findObject(By.text("Open image in new tab")) + menuOpenImageNewTab.click() + } + + fun clickContextSaveImage() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text("Save image")), TestAssetHelper.waitingTime) + + val menuSaveImage = mDevice.findObject(By.text("Save image")) + menuSaveImage.click() + } + fun waitForCollectionSavedPopup() { - mDevice.wait(Until.findObject(By.text("Tab saved!")), - TestAssetHelper.waitingTime) + mDevice.wait( + Until.findObject(By.text("Tab saved!")), + TestAssetHelper.waitingTime + ) } fun createBookmark(url: Uri) { @@ -85,6 +242,20 @@ class BrowserRobot { element.click() } + fun longClickMatchingText(expectedText: String) { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime) + + val element = mDevice.findObject(By.text(expectedText)) + element.click(LongClickDuration.LONG_CLICK_DURATION) + } + + fun snackBarButtonClick(expectedText: String) { + onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check( + matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) + ).perform(ViewActions.click()) + } + class Transition { private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) private fun threeDotButton() = onView( diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt index 531084fe1..5d8d2a2bf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt @@ -6,12 +6,15 @@ package org.mozilla.fenix.ui.robots +import android.content.Intent import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.RootMatchers.isDialog -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice @@ -19,8 +22,10 @@ import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestHelper import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull +import org.mozilla.fenix.helpers.Constants.PackageName.GOOGLE_APPS_PHOTOS /** * Implementation of Robot Pattern for download UI handling. @@ -34,13 +39,15 @@ class DownloadRobot { fun verifyDownloadNotificationShade() = assertDownloadNotificationShade() + fun verifyPhotosAppOpens() = assertPhotosOpens() + class Transition { - fun clickDownload(interact: DownloadRobot.() -> Unit): DownloadRobot.Transition { + fun clickDownload(interact: DownloadRobot.() -> Unit): Transition { clickDownloadButton().click() DownloadRobot().interact() - return DownloadRobot.Transition() + return Transition() } fun closePrompt(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { @@ -49,6 +56,36 @@ class DownloadRobot { BrowserRobot().interact() return BrowserRobot.Transition() } + + fun clickOpen(type: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + clickOpenButton().click() + + // verify open intent is matched with associated data type + Intents.intended( + CoreMatchers.allOf( + IntentMatchers.hasAction(Intent.ACTION_VIEW), + IntentMatchers.hasType(type) + ) + ) + + BrowserRobot().interact() + return BrowserRobot.Transition() + } + + fun clickAllowPermission(interact: DownloadRobot.() -> Unit): Transition { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + mDevice.waitNotNull( + Until.findObject(By.res(TestHelper.getPermissionAllowID() + ":id/permission_allow_button")), + TestAssetHelper.waitingTime + ) + + val allowPermissionButton = mDevice.findObject(By.res(TestHelper.getPermissionAllowID() + ":id/permission_allow_button")) + allowPermissionButton.click() + + DownloadRobot().interact() + return Transition() + } } } @@ -84,3 +121,20 @@ private fun closePromptButton() = private fun clickDownloadButton() = onView(withText("Download")).inRoot(isDialog()).check(matches(isDisplayed())) + +private fun clickOpenButton() = + onView(withId(R.id.download_notification_action_button)).inRoot(isDialog()).check( + matches(isDisplayed()) + ) + +private fun assertPhotosOpens() { + if (isPackageInstalled(GOOGLE_APPS_PHOTOS)) { + Intents.intended(IntentMatchers.toPackage(GOOGLE_APPS_PHOTOS)) + } else { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.waitNotNull( + Until.findObject(By.text("Could not open file")), + TestAssetHelper.waitingTime + ) + } +} 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 feda6ba00..aa2f264fe 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 @@ -6,11 +6,13 @@ package org.mozilla.fenix.ui.robots +import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click 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 @@ -82,6 +84,7 @@ class HomeScreenRobot { fun verifyCloseTabsButton(visible: Boolean = true) = assertCloseTabsButton(visible) fun verifyExistingTabList() = assertExistingTabList() + fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title) // Collections element fun clickCollectionThreeDotButton() { @@ -361,6 +364,13 @@ private fun assertExistingTabList() = onView(CoreMatchers.allOf(withId(R.id.item_tab))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) +private fun assertExistingOpenTabs(title: String) = + onView(withId(R.id.home_component)).perform( + RecyclerViewActions.scrollTo( + ViewMatchers.hasDescendant(withText(title)) + ) + ).check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + private fun tabsListThreeDotButton() = onView(allOf(withId(R.id.tabs_overflow_button))) private fun collectionThreeDotButton() = onView(allOf(withId(R.id.collection_overflow_button))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt index bc923adba..4f5e6f400 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt @@ -53,6 +53,27 @@ class NavigationToolbarRobot { BrowserRobot().interact() return BrowserRobot.Transition() } + + fun visitLinkFromClipboard(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + mDevice.waitNotNull( + Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_clear_view")), + waitingTime + ) + clearAddressBar().click() + + mDevice.waitNotNull( + Until.findObject(By.text(url.toString())), waitingTime + ) + + mDevice.waitNotNull( + Until.findObject(By.res("org.mozilla.fenix.debug:id/fill_link_from_clipboard")), + waitingTime + ) + fillLinkButton().click() + + BrowserRobot().interact() + return BrowserRobot.Transition() + } } } @@ -66,3 +87,5 @@ private fun urlBar() = onView(ViewMatchers.withId(R.id.toolbar)) private fun awesomeBar() = onView(ViewMatchers.withId(R.id.mozac_browser_toolbar_edit_url_view)) private fun threeDotButton() = onView(ViewMatchers.withContentDescription("Menu")) private fun newTab() = onView(ViewMatchers.withContentDescription("Add tab")) +private fun fillLinkButton() = onView(ViewMatchers.withId(R.id.fill_link_from_clipboard)) +private fun clearAddressBar() = onView(ViewMatchers.withId(R.id.mozac_browser_toolbar_clear_view))