diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/matchers/RecyclerViewItemMatcher.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/matchers/RecyclerViewItemMatcher.kt new file mode 100644 index 000000000..d8adef4b9 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/matchers/RecyclerViewItemMatcher.kt @@ -0,0 +1,29 @@ +package org.mozilla.fenix.helpers.matchers + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.matcher.BoundedMatcher +import org.hamcrest.Description +import org.hamcrest.Matcher + +fun hasItem(matcher: Matcher): Matcher? { + return object : BoundedMatcher(RecyclerView::class.java) { + override fun describeTo(description: Description) { + description.appendText("has item: ") + matcher.describeTo(description) + } + + override fun matchesSafely(view: RecyclerView): Boolean { + val adapter = view.adapter + for (position in 0 until adapter!!.itemCount) { + val type = adapter.getItemViewType(position) + val holder = adapter.createViewHolder(view, type) + adapter.onBindViewHolder(holder, position) + if (matcher.matches(holder.itemView)) { + return true + } + } + return false + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt new file mode 100644 index 000000000..1473a1cdc --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -0,0 +1,162 @@ +/* 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.homeScreen +import org.mozilla.fenix.ui.robots.navigationToolbar + +/** + * Tests Top Sites functionality + * + * - Verifies 'Add to Firefox Home' UI functionality + * - Verifies 'Top Sites' context menu UI functionality + * - Verifies 'Top Site' usage UI functionality + * - Verifies existence of default top sites available on the home-screen + */ + +class TopSitesTest { + 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 verifyAddToFirefoxHome() { + val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val defaultWebPageTitle = "Test_Page_1" + + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyPageContent(defaultWebPage.content) + }.openThreeDotMenu { + verifyAddFirefoxHome() + }.addToFirefoxHome { + verifySnackBarText("Added to top sites!") + }.openHomeScreen { + verifyExistingTabList() + verifyExistingTopSitesList() + verifyExistingTopSitesTabs(defaultWebPageTitle) + } + } + + @Test + fun verifyOpenTopSiteNormalTab() { + val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val defaultWebPageTitle = "Test_Page_1" + + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyPageContent(defaultWebPage.content) + }.openThreeDotMenu { + verifyAddFirefoxHome() + }.addToFirefoxHome { + verifySnackBarText("Added to top sites!") + }.openHomeScreen { + verifyExistingTabList() + verifyExistingTopSitesList() + verifyExistingTopSitesTabs(defaultWebPageTitle) + }.openTopSiteTabWithTitle(title = defaultWebPageTitle) { + verifyPageContent(defaultWebPage.content) + verifyUrl(defaultWebPage.url.toString()) + }.openHomeScreen { + verifyExistingTopSitesList() + verifyExistingTopSitesTabs(defaultWebPageTitle) + }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { + verifyTopSiteContextMenuItems() + } + + // Dismiss context menu popup + mDevice.pressBack() + } + + @Test + fun verifyOpenTopSitePrivateTab() { + val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val defaultWebPageTitle = "Test_Page_1" + + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyPageContent(defaultWebPage.content) + }.openThreeDotMenu { + verifyAddFirefoxHome() + }.addToFirefoxHome { + verifySnackBarText("Added to top sites!") + }.openHomeScreen { + verifyExistingTabList() + verifyExistingTopSitesList() + verifyExistingTopSitesTabs(defaultWebPageTitle) + }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { + verifyTopSiteContextMenuItems() + }.openTopSiteInPrivateTab { + verifyCurrentPrivateSession(activityIntentTestRule.activity.applicationContext) + } + } + + @Test + fun verifyRemoveTopSite() { + val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val defaultWebPageTitle = "Test_Page_1" + + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyPageContent(defaultWebPage.content) + }.openThreeDotMenu { + verifyAddFirefoxHome() + }.addToFirefoxHome { + verifySnackBarText("Added to top sites!") + }.openHomeScreen { + verifyExistingTabList() + verifyExistingTopSitesList() + verifyExistingTopSitesTabs(defaultWebPageTitle) + }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { + verifyTopSiteContextMenuItems() + }.removeTopSite { + verifyNotExistingTopSitesList(defaultWebPageTitle) + } + } + + @Test + fun verifyDefaultTopSitesLocale_EN() { + // en-US defaults + val defaultTopSites = arrayOf( + "Top Articles", + "Wikipedia", + "YouTube" + ) + + homeScreen { }.dismissOnboarding() + + homeScreen { + verifyExistingTopSitesList() + defaultTopSites.forEach { item -> + verifyExistingTopSitesTabs(item) + } + } + } +} 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 d8fc2d44e..8f4519663 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,6 +6,7 @@ package org.mozilla.fenix.ui.robots +import android.content.Context import android.content.Intent import android.net.Uri import androidx.test.espresso.Espresso.onView @@ -15,6 +16,7 @@ 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.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText @@ -25,11 +27,13 @@ import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString +import org.junit.Assert.assertTrue import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.helpers.Constants.LongClickDuration 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 { @@ -38,6 +42,11 @@ class BrowserRobot { .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } + fun verifyCurrentPrivateSession(context: Context) { + val session = context.components.core.sessionManager.selectedSession + assertTrue("Current session is private", session?.private!!) + } + fun verifyUrl(url: String) { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) mDevice.waitNotNull( @@ -76,6 +85,10 @@ class BrowserRobot { fun verifySnackBarText(expectedText: String) { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime) + + onView(withText(expectedText)).check( + matches(isCompletelyDisplayed()) + ) } fun verifyLinkContextMenuItems(containsURL: Uri) { @@ -324,4 +337,4 @@ fun dismissTrackingOnboarding() { fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view)) -private fun tabsCounter() = onView(withId(R.id.counter_box)) +private fun tabsCounter() = onView(withId(R.id.mozac_browser_toolbar_browser_actions)) 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 ecf982021..a1b34902f 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,10 +14,12 @@ 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.contrib.RecyclerViewActions.actionOnItem import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.Visibility +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility @@ -27,9 +29,9 @@ 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.hamcrest.CoreMatchers.containsString +import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.CoreMatchers.not import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime @@ -37,6 +39,7 @@ import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull import org.mozilla.fenix.components.Search import org.mozilla.fenix.helpers.withBitmapDrawable +import org.mozilla.fenix.helpers.matchers.hasItem /** * Implementation of Robot Pattern for the home screen menu. @@ -96,6 +99,11 @@ class HomeScreenRobot { fun verifyExistingTabList() = assertExistingTabList() fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title) + fun verifyExistingTopSitesList() = assertExistingTopSitesList() + fun verifyNotExistingTopSitesList(title: String) = assertNotExistingTopSitesList(title) + fun verifyExistingTopSitesTabs(title: String) = assertExistingTopSitesTabs(title) + fun verifyTopSiteContextMenuItems() = assertTopSiteContextMenuItems() + // Collections element fun clickCollectionThreeDotButton() { collectionThreeDotButton().click() @@ -160,7 +168,7 @@ class HomeScreenRobot { } fun snackBarButtonClick(expectedText: String) { - onView(CoreMatchers.allOf(withId(R.id.snackbar_btn), withText(expectedText))).check( + onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check( matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) ).perform(ViewActions.click()) } @@ -217,6 +225,42 @@ class HomeScreenRobot { NavigationToolbarRobot().interact() return NavigationToolbarRobot.Transition() } + + fun openContextMenuOnTopSitesWithTitle(title: String, interact: HomeScreenRobot.() -> Unit): Transition { + onView(withId(R.id.top_sites_list)).perform( + actionOnItem(hasDescendant(withText(title)), ViewActions.longClick()) + ) + + HomeScreenRobot().interact() + return Transition() + } + + fun openTopSiteTabWithTitle(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + onView(withId(R.id.top_sites_list)).perform( + actionOnItem(hasDescendant(withText(title)), click()) + ) + + BrowserRobot().interact() + return BrowserRobot.Transition() + } + + fun removeTopSite(interact: HomeScreenRobot.() -> Unit): Transition { + onView(withText("Remove")) + .check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) + .perform(click()) + + HomeScreenRobot().interact() + return Transition() + } + + fun openTopSiteInPrivateTab(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + onView(withText("Open in private tab")) + .check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) + .perform(click()) + + BrowserRobot().interact() + return BrowserRobot.Transition() + } } } @@ -229,12 +273,12 @@ val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val appContext = InstrumentationRegistry.getInstrumentation().targetContext private fun navigationToolbar() = - onView(CoreMatchers.allOf(withText("Search or enter address"))) + onView(allOf(withText("Search or enter address"))) private fun closeTabButton() = onView(withId(R.id.close_tab_button)) private fun assertNavigationToolbar() = - onView(CoreMatchers.allOf(withText("Search or enter address"))) + onView(allOf(withText("Search or enter address"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout")) @@ -254,33 +298,33 @@ private fun assertHomeToolbar() = onView(ViewMatchers.withResourceName("toolbar" .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertOpenTabsHeader() = - onView(CoreMatchers.allOf(withText("Open tabs"))) + onView(allOf(withText("Open tabs"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertAddTabButton() = - onView(CoreMatchers.allOf(withId(R.id.add_tab_button), isDisplayed())) + onView(allOf(withId(R.id.add_tab_button), isDisplayed())) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertNoTabsOpenedHeader() = - onView(CoreMatchers.allOf(withText("No open tabs"))) + onView(allOf(withText("No open tabs"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertNoTabsOpenedText() { - onView(CoreMatchers.allOf(withText("Your open tabs will be shown here."))) + onView(allOf(withText("Your open tabs will be shown here."))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun assertCollectionsHeader() = - onView(CoreMatchers.allOf(withText("Collections"))) + onView(allOf(withText("Collections"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertNoCollectionsHeader() = - onView(CoreMatchers.allOf(withText("No collections"))) + onView(allOf(withText("No collections"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertNoCollectionsText() = onView( - CoreMatchers.allOf( + allOf( withText("Collect the things that matter to you. To start, save open tabs to a new collection.") ) ) @@ -306,11 +350,11 @@ private fun verifySearchEngineIcon(searchEngineName: String) { // First Run elements private fun assertWelcomeHeader() = - onView(CoreMatchers.allOf(withText("Welcome to Firefox Preview!"))) + onView(allOf(withText("Welcome to Firefox Preview!"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertGetTheMostHeader() = - onView(CoreMatchers.allOf(withText("Get the most out of Firefox Preview."))) + onView(allOf(withText("Get the most out of Firefox Preview."))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertAccountsSignInButton() = @@ -318,15 +362,15 @@ private fun assertAccountsSignInButton() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertGetToKnowHeader() = - onView(CoreMatchers.allOf(withText("Get to know Firefox Preview"))) + onView(allOf(withText("Get to know Firefox Preview"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertChooseThemeHeader() = - onView(CoreMatchers.allOf(withText("Choose your theme"))) + onView(allOf(withText("Choose your theme"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertChooseThemeText() = - onView(CoreMatchers.allOf(withText("Try dark theme: easier on your battery and your eyes."))) + onView(allOf(withText("Try dark theme: easier on your battery and your eyes."))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertLightThemeToggle() = @@ -334,7 +378,7 @@ private fun assertLightThemeToggle() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertLightThemeDescription() = - onView(CoreMatchers.allOf(withText("Light theme"))) + onView(allOf(withText("Light theme"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertDarkThemeToggle() = @@ -342,7 +386,7 @@ private fun assertDarkThemeToggle() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertDarkThemeDescription() = - onView(CoreMatchers.allOf(withText("Dark theme"))) + onView(allOf(withText("Dark theme"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertAutomaticThemeToggle() = @@ -350,21 +394,21 @@ private fun assertAutomaticThemeToggle() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertAutomaticThemeDescription() = - onView(CoreMatchers.allOf(withText("Automatic"))) + onView(allOf(withText("Automatic"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertProtectYourselfHeader() = - onView(CoreMatchers.allOf(withText("Protect yourself"))) + onView(allOf(withText("Protect yourself"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertTrackingProtectionToggle() = onView( - CoreMatchers.allOf(ViewMatchers.withResourceName("tracking_protection_toggle")) + allOf(ViewMatchers.withResourceName("tracking_protection_toggle")) ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertProtectYourselfText() { onView( - CoreMatchers.allOf( + allOf( withText( "Firefox Preview blocks ad trackers that follow you around the web." ) @@ -374,20 +418,20 @@ private fun assertProtectYourselfText() { } private fun assertBrowsePrivatelyHeader() = - onView(CoreMatchers.allOf(withText("Browse privately"))) + onView(allOf(withText("Browse privately"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertBrowsePrivatelyText() = - onView(CoreMatchers.allOf(withText(containsString("private browsing is just a tap away.")))) + onView(allOf(withText(containsString("private browsing is just a tap away.")))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertYourPrivacyHeader() = - onView(CoreMatchers.allOf(withText("Your privacy"))) + onView(allOf(withText("Your privacy"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertYourPrivacyText() = onView( - CoreMatchers.allOf( + allOf( withText( "We’ve designed Firefox Preview to give you control over what you share online and what you share with us." ) @@ -396,16 +440,16 @@ private fun assertYourPrivacyText() = .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertPrivacyNoticeButton() = - onView(CoreMatchers.allOf(withText("Read our privacy notice"))) + onView(allOf(withText("Read our privacy notice"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertStartBrowsingButton() = - onView(CoreMatchers.allOf(withText("Start browsing"))) + onView(allOf(withText("Start browsing"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) // Private mode elements private fun assertPrivateSessionHeader() = - onView(CoreMatchers.allOf(withText("Private tabs"))) + onView(allOf(withText("Private tabs"))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " + @@ -414,13 +458,13 @@ const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and brow "who uses this device." private fun assertPrivateSessionMessage(visible: Boolean) = - onView(CoreMatchers.allOf(withText(PRIVATE_SESSION_MESSAGE))) + onView(allOf(withText(PRIVATE_SESSION_MESSAGE))) .check( if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else doesNotExist() ) private fun assertShareTabsButton(visible: Boolean) = - onView(CoreMatchers.allOf(withId(R.id.share_tabs_button), isDisplayed())) + onView(allOf(withId(R.id.share_tabs_button), isDisplayed())) .check(matches(withEffectiveVisibility(visibleOrGone(visible)))) private fun assertCloseTabsButton(title: String) = @@ -431,7 +475,7 @@ private fun visibleOrGone(visibility: Boolean) = if (visibility) Visibility.VISIBLE else Visibility.GONE private fun assertExistingTabList() = - onView(CoreMatchers.allOf(withId(R.id.item_tab))) + onView(allOf(withId(R.id.item_tab))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertExistingOpenTabs(title: String) = @@ -460,3 +504,29 @@ private fun closeTabViaX(title: String) { } private fun assertPrivateTabsCloseTabsButton() = onView(allOf(withId(R.id.close_tabs_button))) + +private fun assertExistingTopSitesList() = + onView(allOf(withId(R.id.top_sites_list))) + .check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) + +private fun assertExistingTopSitesTabs(title: String) = + onView(allOf(withId(R.id.top_sites_list))) + .check(matches(hasItem(hasDescendant(withText(title))))) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + +private fun assertNotExistingTopSitesList(title: String) = + onView(allOf(withId(R.id.top_sites_list))) + .check(matches(not(hasItem(hasDescendant(withText(title)))))) + +private fun assertTopSiteContextMenuItems() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + mDevice.waitNotNull( + Until.findObject(By.text("Open in private tab")), + waitingTime + ) + mDevice.waitNotNull( + Until.findObject(By.text("Remove")), + waitingTime + ) +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt index 474d27c2d..ff4ec3725 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt @@ -6,17 +6,21 @@ package org.mozilla.fenix.ui.robots +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.hasFocus import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withResourceName import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice @@ -75,6 +79,7 @@ class ThreeDotMenuMainRobot { fun verifySendToDeviceTitle() = assertSendToDeviceTitle() fun verifyShareALinkTitle() = assertShareALinkTitle() fun verifyWhatsNewButton() = assertWhatsNewButton() + fun verifyAddFirefoxHome() = assertAddToFirefoxHome() class Transition { @@ -185,11 +190,20 @@ class ThreeDotMenuMainRobot { return BrowserRobot.Transition() } - fun typeCollectionName(name: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { - mDevice.wait(Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")), waitingTime) + fun typeCollectionName( + name: String, + interact: BrowserRobot.() -> Unit + ): BrowserRobot.Transition { + mDevice.wait( + Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")), + waitingTime + ) collectionNameTextField().check(matches(hasFocus())) - collectionNameTextField().perform(ViewActions.replaceText(name), ViewActions.pressImeActionButton()) + collectionNameTextField().perform( + ViewActions.replaceText(name), + ViewActions.pressImeActionButton() + ) BrowserRobot().interact() return BrowserRobot.Transition() @@ -208,6 +222,13 @@ class ThreeDotMenuMainRobot { ReaderViewRobot().interact() return ReaderViewRobot.Transition() } + + fun addToFirefoxHome(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + addToFirefoxHomeButton().click() + + BrowserRobot().interact() + return BrowserRobot.Transition() + } } } @@ -215,8 +236,13 @@ private fun threeDotMenuRecyclerViewExists() { onView(withId(R.id.mozac_browser_menu_recyclerView)).check(matches(isDisplayed())) } -private fun settingsButton() = onView(allOf(withText(R.string.settings), - withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) +private fun settingsButton() = onView( + allOf( + withText(R.string.settings), + withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + ) +) + private fun assertSettingsButton() = settingsButton() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) @@ -264,8 +290,12 @@ private fun shareButton() = onView(ViewMatchers.withContentDescription("Share")) private fun assertShareButton() = shareButton() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) -private fun browserViewSaveCollectionButton() = onView(allOf(withText("Save to Collection"), - withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) +private fun browserViewSaveCollectionButton() = onView( + allOf( + withText("Save to Collection"), + withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + ) +) private fun saveCollectionButton() = onView(allOf(withText("Save to collection"))) private fun assertSaveCollectionButton() = saveCollectionButton() @@ -300,7 +330,10 @@ private fun assertShareALinkTitle() = ShareALinkTitle() private fun whatsNewButton() = onView( allOf( withText("What’s New"), - withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + ) +) + private fun assertWhatsNewButton() = whatsNewButton() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) @@ -309,8 +342,22 @@ private fun assertReaderViewToggle(visible: Boolean) = readerViewToggle() .check( if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) else ViewAssertions.doesNotExist() ) -private fun readerViewAppearanceToggle() = onView(allOf(withText(R.string.browser_menu_read_appearance))) + +private fun readerViewAppearanceToggle() = + onView(allOf(withText(R.string.browser_menu_read_appearance))) + private fun assertReaderViewAppearanceButton(visible: Boolean) = readerViewAppearanceToggle() .check( if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) else ViewAssertions.doesNotExist() ) + +private fun addToFirefoxHomeButton() = + onView(allOf(withText(R.string.browser_menu_add_to_top_sites))) +private fun assertAddToFirefoxHome() { + onView(withId(R.id.mozac_browser_menu_recyclerView)) + .perform( + RecyclerViewActions.scrollTo( + hasDescendant(withText(R.string.browser_menu_add_to_top_sites)) + ) + ).check(matches(withEffectiveVisibility(Visibility.VISIBLE))) +}