diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/AppAndSystemHelper.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/AppAndSystemHelper.kt index ea2ef99b87740..2baf9ff5c3eb3 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/AppAndSystemHelper.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/AppAndSystemHelper.kt @@ -46,6 +46,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import mozilla.appservices.places.BookmarkRoot +import mozilla.components.browser.state.search.RegionState import mozilla.components.browser.storage.sync.PlacesBookmarksStorage import mozilla.components.browser.storage.sync.PlacesHistoryStorage import mozilla.components.feature.top.sites.TopSite @@ -53,6 +54,7 @@ import mozilla.components.support.locale.LocaleManager.resetToSystemDefault import mozilla.components.support.locale.LocaleManager.setNewLocale import org.junit.Assert import org.junit.Assert.assertEquals +import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.components.PermissionStorage import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity @@ -956,4 +958,19 @@ object AppAndSystemHelper { Log.i(TAG, "setScreenOrientation: Waited for the compose test rule to be idle.") Log.i(TAG, "setScreenOrientation: Orientation set to $orientation.") } + + /** + * Whether the default pinned shortcuts are expected on the homepage. + * + * Mirrors the condition in `DefaultPinnedSitesBinding`. + */ + fun isDefaultPinnedShortcutsOnHomepage(): Boolean { + val region = appContext.components.core.store.state.search.region + val expected = region != null && (!Config.channel.isReleased || region != RegionState.Default) + Log.i( + TAG, + "isDefaultPinnedShortcutsOnHomepage: region=$region channel=${Config.channel} expected=$expected", + ) + return expected + } } diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt index 3d94625f39585..e9f2c1fc7dc85 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/Constants.kt @@ -41,18 +41,17 @@ object Constants { "DuckDuckGo" to "t=fpas", ) - val firstSponsoredShortcutTitle by lazy { getSponsoredShortcutTitle(2) } - val secondSponsoredShortcutTitle by lazy { getSponsoredShortcutTitle(3) } + private val firstSponsoredShortcutTitle by lazy { getSponsoredShortcutTitle(1) } + private val secondSponsoredShortcutTitle by lazy { getSponsoredShortcutTitle(2) } - // Expected for en-us defaults - val defaultTopSitesList by lazy { - mapOf( - "Google" to "Google", - "First sponsored shortcut" to firstSponsoredShortcutTitle, - "Second sponsored shortcut" to secondSponsoredShortcutTitle, - "Wikipedia" to "Wikipedia", - ) - } + /** + * The shortcut titles, which are only present when [AppAndSystemHelper.isDefaultPinnedShortcutsOnHomepage] holds. + * Expected for en-us defaults. + */ + val defaultPinnedShortcutTitles by lazy { listOf("Google", "Wikipedia") } + + /** The sponsored shortcuts, which are present on every build type. */ + val sponsoredShortcutTitles by lazy { listOf(firstSponsoredShortcutTitle, secondSponsoredShortcutTitle) } // Notes: // "Ghostery" - not included in the list because of the name discrepancy between the recommended list and the diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/DataGenerationHelper.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/DataGenerationHelper.kt index 5a69e5093e075..ec5b319a67739 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/DataGenerationHelper.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/DataGenerationHelper.kt @@ -20,17 +20,20 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.core.graphics.createBitmap import androidx.core.net.toUri import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiSelector +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiObject2 import java.time.LocalDate import java.time.LocalTime import mozilla.components.browser.state.search.SearchEngine import mozilla.components.browser.state.state.availableSearchEngines import org.junit.Assert +import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.Constants.TAG import org.mozilla.fenix.helpers.Constants.recommendedAddons import org.mozilla.fenix.helpers.TestHelper.mDevice import org.mozilla.fenix.helpers.TestHelper.waitForAppWindowToBeUpdated +import org.mozilla.fenix.home.topsites.TopSitesTestTag import org.mozilla.fenix.utils.IntentUtils object DataGenerationHelper { @@ -126,24 +129,36 @@ object DataGenerationHelper { return currentYear + currentMonth + currentDay + currentHour } - /** Returns sponsored shortcut title based on the index. */ - fun getSponsoredShortcutTitle(position: Int): String { - Log.i( - TAG, - "getSponsoredShortcutTitle: Trying to get the title of the sponsored shortcut at position: ${position - 1}", - ) + /** + * Returns the title of the [ordinal]th sponsored shortcut, counting only sponsored shortcuts. + * + * The shortcuts are located by their sponsored label rather than by their absolute position in the top sites list, + * because the number of default pinned shortcuts preceding them varies by release channel and region. See + * [sponsoredShortcuts]. + * + * @param ordinal One-based index among the sponsored shortcuts only. + */ + fun getSponsoredShortcutTitle(ordinal: Int): String { + Log.i(TAG, "getSponsoredShortcutTitle: Trying to get the title of sponsored shortcut #$ordinal") val sponsoredShortcut = - mDevice - .findObject(UiSelector().resourceId("top_sites_list.top_site_item").index(position - 1)) - .getChild(UiSelector().resourceId("top_sites_list.top_site_item.top_site_title")) - .text - Log.i( - TAG, - "getSponsoredShortcutTitle: The sponsored shortcut at position: ${position - 1} has title: $sponsoredShortcut", - ) + sponsoredShortcuts()[ordinal - 1].findObject(By.res(TopSitesTestTag.TOP_SITE_TITLE)).text + Log.i(TAG, "getSponsoredShortcutTitle: Sponsored shortcut #$ordinal has title: $sponsoredShortcut") return sponsoredShortcut } + /** + * Returns the top sites that are labelled as sponsored, in the order they appear on the homepage. + * + * Every shortcut lays out a sponsored label, but it only carries text when the shortcut is sponsored, so matching + * on that text distinguishes them. + */ + fun sponsoredShortcuts(): List { + val sponsoredLabel = getStringResource(R.string.top_sites_sponsored_label) + return mDevice.findObjects(By.res(TopSitesTestTag.TOP_SITE_ITEM_ROOT)).filter { + it.findObject(By.text(sponsoredLabel)) != null + } + } + /** Returns the title of the first matching extension. */ fun getRecommendedExtensionTitle(composeTestRule: ComposeTestRule): String { var verifiedCount = 0 diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/SponsoredShortcutsTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/SponsoredShortcutsTest.kt index 4a30412213197..373ec85c575aa 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/SponsoredShortcutsTest.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/SponsoredShortcutsTest.kt @@ -10,9 +10,11 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.mozilla.fenix.customannotations.SmokeTest +import org.mozilla.fenix.helpers.AppAndSystemHelper.isDefaultPinnedShortcutsOnHomepage import org.mozilla.fenix.helpers.Constants.RETRY_COUNT import org.mozilla.fenix.helpers.Constants.TAG -import org.mozilla.fenix.helpers.Constants.defaultTopSitesList +import org.mozilla.fenix.helpers.Constants.defaultPinnedShortcutTitles +import org.mozilla.fenix.helpers.Constants.sponsoredShortcutTitles import org.mozilla.fenix.helpers.DataGenerationHelper.getSponsoredShortcutTitle import org.mozilla.fenix.helpers.FenixTestRule import org.mozilla.fenix.helpers.HomeActivityIntentTestRule @@ -71,8 +73,9 @@ class SponsoredShortcutsTest { fun verifySponsoredShortcutsListTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - defaultTopSitesList.values.forEach { value -> - verifyExistingTopSitesTabs(value) + sponsoredShortcutTitles.forEach { title -> verifyExistingTopSitesTabs(title) } + if (isDefaultPinnedShortcutsOnHomepage()) { + defaultPinnedShortcutTitles.forEach { title -> verifyExistingTopSitesTabs(title) } } verifyAddShortcutButtonExists() } @@ -86,7 +89,9 @@ class SponsoredShortcutsTest { .goBack {} .goBack(composeTestRule) { verifyNotExistingSponsoredTopSitesList() - verifyAddShortcutButtonExists() + if (isDefaultPinnedShortcutsOnHomepage()) { + verifyAddShortcutButtonExists() + } } } @@ -95,7 +100,7 @@ class SponsoredShortcutsTest { fun openSponsoredShortcutTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) } .openTopSiteTabWithTitle(sponsoredShortcutTitle) { verifyUrl(sponsoredShortcutTitle) @@ -107,7 +112,7 @@ class SponsoredShortcutsTest { fun openSponsoredShortcutInPrivateTabTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) } .openContextMenuOnTopSitesWithTitle(sponsoredShortcutTitle) {} .openTopSiteInPrivateTab { @@ -120,7 +125,7 @@ class SponsoredShortcutsTest { fun openSponsorsAndYourPrivacyOptionTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) } .openContextMenuOnTopSitesWithTitle(sponsoredShortcutTitle) {} .clickSponsorsAndPrivacyButton { @@ -133,7 +138,7 @@ class SponsoredShortcutsTest { fun openSponsoredShortcutsSettingsOptionTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) } .openContextMenuOnTopSitesWithTitle(sponsoredShortcutTitle) {} .clickSponsoredShortcutsSettingsButton { @@ -146,11 +151,11 @@ class SponsoredShortcutsTest { fun verifySponsoredShortcutsDetailsTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) - sponsoredShortcutTitle2 = getSponsoredShortcutTitle(3) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) + sponsoredShortcutTitle2 = getSponsoredShortcutTitle(2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle, 2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 3) + verifySponsoredShortcutDetails(sponsoredShortcutTitle, 1) + verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 2) } } @@ -165,11 +170,11 @@ class SponsoredShortcutsTest { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) - sponsoredShortcutTitle2 = getSponsoredShortcutTitle(3) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) + sponsoredShortcutTitle2 = getSponsoredShortcutTitle(2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle, 2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 3) + verifySponsoredShortcutDetails(sponsoredShortcutTitle, 1) + verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 2) } navigationToolbar(composeTestRule) {} .enterURLAndEnterToBrowser(firstWebPage.url) { @@ -213,7 +218,7 @@ class SponsoredShortcutsTest { } .clickAddToShortcutsButton {} .goToHomescreen { - verifySponsoredShortcutDetails(sponsoredShortcutTitle, 2) + verifySponsoredShortcutDetails(sponsoredShortcutTitle, 1) verifySponsoredShortcutDoesNotExist(sponsoredShortcutTitle2, 3) } } @@ -234,11 +239,11 @@ class SponsoredShortcutsTest { homeScreen(composeTestRule) { verifyExistingTopSitesList() - sponsoredShortcutTitle = getSponsoredShortcutTitle(2) - sponsoredShortcutTitle2 = getSponsoredShortcutTitle(3) + sponsoredShortcutTitle = getSponsoredShortcutTitle(1) + sponsoredShortcutTitle2 = getSponsoredShortcutTitle(2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle, 2) - verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 3) + verifySponsoredShortcutDetails(sponsoredShortcutTitle, 1) + verifySponsoredShortcutDetails(sponsoredShortcutTitle2, 2) MockBrowserDataHelper.addPinnedSite( Pair(pagesList[0].title, pagesList[0].url.toString()), diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt index 753ef59abe903..2efd86079c437 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -12,9 +12,11 @@ import org.junit.Test import org.mozilla.fenix.R import org.mozilla.fenix.customannotations.Converted import org.mozilla.fenix.customannotations.SmokeTest +import org.mozilla.fenix.helpers.AppAndSystemHelper.isDefaultPinnedShortcutsOnHomepage import org.mozilla.fenix.helpers.Constants.RETRY_COUNT import org.mozilla.fenix.helpers.Constants.TAG -import org.mozilla.fenix.helpers.Constants.defaultTopSitesList +import org.mozilla.fenix.helpers.Constants.defaultPinnedShortcutTitles +import org.mozilla.fenix.helpers.Constants.sponsoredShortcutTitles import org.mozilla.fenix.helpers.DataGenerationHelper.generateRandomString import org.mozilla.fenix.helpers.DataGenerationHelper.getStringResource import org.mozilla.fenix.helpers.FenixTestRule @@ -59,11 +61,7 @@ class TopSitesTest { homeScreen(composeTestRule) {} .openThreeDotMenu {} .clickSettingsButton {} - .goBack(composeTestRule) { - defaultTopSitesList.values.forEach { value -> - verifyExistingTopSitesTabs(value) - } - } + .goBack(composeTestRule) { verifyExistingTopSitesList() } break } catch (e: Throwable) { @@ -261,8 +259,9 @@ class TopSitesTest { fun verifyENLocalesDefaultTopSitesListTest() { homeScreen(composeTestRule) { verifyExistingTopSitesList() - defaultTopSitesList.values.forEach { value -> - verifyExistingTopSitesTabs(value) + sponsoredShortcutTitles.forEach { title -> verifyExistingTopSitesTabs(title) } + if (isDefaultPinnedShortcutsOnHomepage()) { + defaultPinnedShortcutTitles.forEach { title -> verifyExistingTopSitesTabs(title) } } verifyAddShortcutButtonExists() } diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt index c550427ef007c..745ed23d10035 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt @@ -48,6 +48,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObjectNotFoundException import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector +import kotlin.test.assertNotNull import mozilla.components.browser.menu.R as menuR import mozilla.components.compose.base.R as composeBaseR import mozilla.components.compose.browser.toolbar.concept.BrowserToolbarTestTags.ADDRESSBAR_URL_BOX @@ -58,6 +59,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.helpers.Constants.RETRY_COUNT import org.mozilla.fenix.helpers.Constants.TAG import org.mozilla.fenix.helpers.DataGenerationHelper.getStringResource +import org.mozilla.fenix.helpers.DataGenerationHelper.sponsoredShortcuts import org.mozilla.fenix.helpers.MatcherHelper.assertItemIsChecked import org.mozilla.fenix.helpers.MatcherHelper.assertUIObjectExists import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText @@ -256,18 +258,22 @@ class HomeScreenRobot(private val composeTestRule: ComposeTestRule) { Log.i(TAG, "verifyAddShortcutButtonExists: Verified that the \"Add shortcut\" button exists") } - fun verifySponsoredShortcutDetails(sponsoredShortcutTitle: String, position: Int) { - assertUIObjectExists( - itemWithResIdAndIndex(resourceId = "top_sites_list.top_site_item", index = position - 1) - .getChild(UiSelector().resourceId(TOP_SITE_CARD_FAVICON)) - ) - assertUIObjectExists( - itemWithResIdAndIndex(resourceId = "top_sites_list.top_site_item", index = position - 1) - .getChild(UiSelector().textContains(sponsoredShortcutTitle)) + /** + * Asserts the [ordinal]th sponsored shortcut has a favicon and the expected title. + * + * @param sponsoredShortcutTitle The shortcut title which is expected. + * @param ordinal One-based index among the sponsored shortcuts only, matching + * [org.mozilla.fenix.helpers.DataGenerationHelper.getSponsoredShortcutTitle]. + */ + fun verifySponsoredShortcutDetails(sponsoredShortcutTitle: String, ordinal: Int) { + val sponsoredShortcut = sponsoredShortcuts()[ordinal - 1] + assertNotNull( + sponsoredShortcut.findObject(By.res(TOP_SITE_CARD_FAVICON)), + "Sponsored shortcut #$ordinal has no favicon", ) - assertUIObjectExists( - itemWithResIdAndIndex(resourceId = "top_sites_list.top_site_item", index = position - 1) - .getChild(UiSelector().textContains(getStringResource(R.string.top_sites_sponsored_label))) + assertNotNull( + sponsoredShortcut.findObject(By.textContains(sponsoredShortcutTitle)), + "Sponsored shortcut #$ordinal is not titled $sponsoredShortcutTitle", ) }