Issue #26286: adds UI test for the Language menu

pull/543/head
sv-ohorvath 2 years ago committed by mergify[bot]
parent 67d8a2c749
commit 0a2d1c63f1

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@file:Suppress("DEPRECATION")
package org.mozilla.fenix.helpers package org.mozilla.fenix.helpers
import android.app.ActivityManager import android.app.ActivityManager
@ -10,6 +12,7 @@ import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
@ -31,6 +34,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject import androidx.test.uiautomator.UiObject
@ -38,6 +42,8 @@ import androidx.test.uiautomator.UiObjectNotFoundException
import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until
import java.util.Locale
import java.util.regex.Pattern
import junit.framework.AssertionFailedError import junit.framework.AssertionFailedError
import mozilla.components.browser.state.search.SearchEngine import mozilla.components.browser.state.search.SearchEngine
import mozilla.components.support.ktx.android.content.appName import mozilla.components.support.ktx.android.content.appName
@ -46,6 +52,7 @@ import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.junit.Assert import org.junit.Assert
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
@ -56,7 +63,7 @@ import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.helpers.idlingresource.NetworkConnectionIdlingResource import org.mozilla.fenix.helpers.idlingresource.NetworkConnectionIdlingResource
import org.mozilla.fenix.ui.robots.BrowserRobot import org.mozilla.fenix.ui.robots.BrowserRobot
import org.mozilla.fenix.utils.IntentUtils import org.mozilla.fenix.utils.IntentUtils
import java.util.regex.Pattern import org.mozilla.gecko.util.ThreadUtils
object TestHelper { object TestHelper {
@ -329,4 +336,39 @@ object TestHelper {
.map { kotlin.random.Random.nextInt(0, charPool.size) } .map { kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get) .map(charPool::get)
.joinToString("") .joinToString("")
/**
* Changes the default language of the entire device, not just the app.
* Runs the test in its testBlock.
* Cleans up and sets the default locale after it's are done.
*/
fun runWithSystemLocaleChanged(locale: Locale, testRule: ActivityTestRule<HomeActivity>, testBlock: () -> Unit) {
val defaultLocale = Locale.getDefault()
try {
setSystemLocale(locale)
testBlock()
ThreadUtils.runOnUiThread { testRule.activity.recreate() }
} catch (e: Exception) {
e.printStackTrace()
} finally {
setSystemLocale(defaultLocale)
}
}
/**
* Changes the default language of the entire device, not just the app.
*/
private fun setSystemLocale(locale: Locale) {
val activityManagerNative = Class.forName("android.app.ActivityManagerNative")
val am = activityManagerNative.getMethod("getDefault", *arrayOfNulls(0))
.invoke(activityManagerNative, *arrayOfNulls(0))
val config = InstrumentationRegistry.getInstrumentation().context.resources.configuration
config.javaClass.getDeclaredField("locale")[config] = locale
config.javaClass.getDeclaredField("userSetLocale").setBoolean(config, true)
am.javaClass.getMethod(
"updateConfiguration",
Configuration::class.java
).invoke(am, config)
}
} }

@ -5,27 +5,40 @@
package org.mozilla.fenix.ui package org.mozilla.fenix.ui
import android.content.res.Configuration import android.content.res.Configuration
import androidx.test.espresso.IdlingRegistry
import java.time.LocalDate
import java.util.Locale
import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockWebServer
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.FenixApplication import org.mozilla.fenix.FenixApplication
import org.mozilla.fenix.R
import org.mozilla.fenix.customannotations.SmokeTest import org.mozilla.fenix.customannotations.SmokeTest
import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.FeatureSettingsHelper import org.mozilla.fenix.helpers.FeatureSettingsHelper
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.getLoremIpsumAsset import org.mozilla.fenix.helpers.TestAssetHelper.getLoremIpsumAsset
import org.mozilla.fenix.ui.SettingsBasicsTest.creditCard.MOCK_CREDIT_CARD_NUMBER import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeLong
import org.mozilla.fenix.ui.SettingsBasicsTest.creditCard.MOCK_EXPIRATION_MONTH import org.mozilla.fenix.helpers.TestHelper.runWithSystemLocaleChanged
import org.mozilla.fenix.ui.SettingsBasicsTest.creditCard.MOCK_EXPIRATION_YEAR import org.mozilla.fenix.helpers.TestHelper.getStringResource
import org.mozilla.fenix.ui.SettingsBasicsTest.creditCard.MOCK_LAST_CARD_DIGITS import org.mozilla.fenix.helpers.TestHelper.mDevice
import org.mozilla.fenix.ui.SettingsBasicsTest.creditCard.MOCK_NAME_ON_CARD import org.mozilla.fenix.ui.SettingsBasicsTest.CreditCard.MOCK_CREDIT_CARD_NUMBER
import org.mozilla.fenix.ui.SettingsBasicsTest.CreditCard.MOCK_EXPIRATION_MONTH
import org.mozilla.fenix.ui.SettingsBasicsTest.CreditCard.MOCK_EXPIRATION_YEAR
import org.mozilla.fenix.ui.SettingsBasicsTest.CreditCard.MOCK_LAST_CARD_DIGITS
import org.mozilla.fenix.ui.SettingsBasicsTest.CreditCard.MOCK_NAME_ON_CARD
import org.mozilla.fenix.ui.robots.checkTextSizeOnWebsite import org.mozilla.fenix.ui.robots.checkTextSizeOnWebsite
import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar import org.mozilla.fenix.ui.robots.navigationToolbar
import java.time.LocalDate import org.mozilla.fenix.ui.util.FRENCH_LANGUAGE_HEADER
import org.mozilla.fenix.ui.util.FRENCH_SYSTEM_LOCALE_OPTION
import org.mozilla.fenix.ui.util.FR_SETTINGS
import org.mozilla.fenix.ui.util.ROMANIAN_LANGUAGE_HEADER
/** /**
* Tests for verifying the General section of the Settings menu * Tests for verifying the General section of the Settings menu
@ -35,8 +48,9 @@ class SettingsBasicsTest {
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping. /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
private lateinit var mockWebServer: MockWebServer private lateinit var mockWebServer: MockWebServer
private val featureSettingsHelper = FeatureSettingsHelper() private val featureSettingsHelper = FeatureSettingsHelper()
private var localeListIdlingResource: RecyclerViewIdlingResource? = null
object creditCard { object CreditCard {
const val MOCK_CREDIT_CARD_NUMBER = "5555555555554444" const val MOCK_CREDIT_CARD_NUMBER = "5555555555554444"
const val MOCK_LAST_CARD_DIGITS = "4444" const val MOCK_LAST_CARD_DIGITS = "4444"
const val MOCK_NAME_ON_CARD = "Mastercard" const val MOCK_NAME_ON_CARD = "Mastercard"
@ -64,6 +78,10 @@ class SettingsBasicsTest {
// resetting modified features enabled setting to default // resetting modified features enabled setting to default
featureSettingsHelper.resetAllFeatureFlags() featureSettingsHelper.resetAllFeatureFlags()
if (localeListIdlingResource != null) {
IdlingRegistry.getInstance().unregister(localeListIdlingResource)
}
} }
private fun getUiTheme(): Boolean { private fun getUiTheme(): Boolean {
@ -250,4 +268,68 @@ class SettingsBasicsTest {
verifyAddCreditCardsButton() verifyAddCreditCardsButton()
} }
} }
@SmokeTest
@Test
fun switchLanguageTest() {
val enLanguageHeaderText = getStringResource(R.string.preferences_language)
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openLanguageSubMenu {
localeListIdlingResource =
RecyclerViewIdlingResource(
activityIntentTestRule.activity.findViewById(R.id.locale_list),
2
)
IdlingRegistry.getInstance().register(localeListIdlingResource)
selectLanguage("Romanian")
verifyLanguageHeaderIsTranslated(ROMANIAN_LANGUAGE_HEADER)
selectLanguage("Français")
verifyLanguageHeaderIsTranslated(FRENCH_LANGUAGE_HEADER)
selectLanguage(FRENCH_SYSTEM_LOCALE_OPTION)
verifyLanguageHeaderIsTranslated(enLanguageHeaderText)
IdlingRegistry.getInstance().unregister(localeListIdlingResource)
}
}
@Test
fun searchInLanguagesListTest() {
val systemLocaleDefault = getStringResource(R.string.default_locale_text)
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openLanguageSubMenu {
verifyLanguageListIsDisplayed()
openSearchBar()
typeInSearchBar("French")
verifySearchResultsContains(systemLocaleDefault)
clearSearchBar()
typeInSearchBar("French")
selectLanguageSearchResult("Français")
verifyLanguageHeaderIsTranslated(FRENCH_LANGUAGE_HEADER)
// Add this step when https://github.com/mozilla-mobile/fenix/issues/26733 is fixed
// verifyLanguageListIsDisplayed()
}
}
@Ignore("Failing due to app translation bug, see: https://github.com/mozilla-mobile/fenix/issues/26729")
@Test
fun frenchSystemLocaleTest() {
val frenchLocale = Locale("fr", "FR")
runWithSystemLocaleChanged(frenchLocale, activityIntentTestRule) {
mDevice.waitForIdle(waitingTimeLong)
homeScreen {
}.openThreeDotMenu {
}.openSettings(localizedText = FR_SETTINGS) {
}.openLanguageSubMenu(localizedText = FRENCH_LANGUAGE_HEADER) {
verifyLanguageHeaderIsTranslated(FRENCH_LANGUAGE_HEADER)
verifySelectedLanguage(FRENCH_SYSTEM_LOCALE_OPTION)
}
}
}
} }

@ -47,9 +47,6 @@ import org.mozilla.fenix.ui.robots.navigationToolbar
import org.mozilla.fenix.ui.robots.notificationShade import org.mozilla.fenix.ui.robots.notificationShade
import org.mozilla.fenix.ui.robots.openEditURLView import org.mozilla.fenix.ui.robots.openEditURLView
import org.mozilla.fenix.ui.robots.searchScreen import org.mozilla.fenix.ui.robots.searchScreen
import org.mozilla.fenix.ui.util.FRENCH_LANGUAGE_HEADER
import org.mozilla.fenix.ui.util.FRENCH_SYSTEM_LOCALE_OPTION
import org.mozilla.fenix.ui.util.ROMANIAN_LANGUAGE_HEADER
import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER
/** /**
@ -68,7 +65,6 @@ class SmokeTest {
private var recentlyClosedTabsListIdlingResource: RecyclerViewIdlingResource? = null private var recentlyClosedTabsListIdlingResource: RecyclerViewIdlingResource? = null
private var readerViewNotification: ViewVisibilityIdlingResource? = null private var readerViewNotification: ViewVisibilityIdlingResource? = null
private var bookmarksListIdlingResource: RecyclerViewIdlingResource? = null private var bookmarksListIdlingResource: RecyclerViewIdlingResource? = null
private var localeListIdlingResource: RecyclerViewIdlingResource? = null
private val customMenuItem = "TestMenuItem" private val customMenuItem = "TestMenuItem"
private lateinit var browserStore: BrowserStore private lateinit var browserStore: BrowserStore
private val featureSettingsHelper = FeatureSettingsHelper() private val featureSettingsHelper = FeatureSettingsHelper()
@ -129,10 +125,6 @@ class SmokeTest {
IdlingRegistry.getInstance().unregister(readerViewNotification) IdlingRegistry.getInstance().unregister(readerViewNotification)
} }
if (localeListIdlingResource != null) {
IdlingRegistry.getInstance().unregister(localeListIdlingResource)
}
// resetting modified features enabled setting to default // resetting modified features enabled setting to default
featureSettingsHelper.resetAllFeatureFlags() featureSettingsHelper.resetAllFeatureFlags()
} }
@ -953,28 +945,6 @@ class SmokeTest {
} }
} }
@Test
fun switchLanguageTest() {
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openLanguageSubMenu {
localeListIdlingResource =
RecyclerViewIdlingResource(
activityTestRule.activity.findViewById(R.id.locale_list),
2
)
IdlingRegistry.getInstance().register(localeListIdlingResource)
selectLanguage("Romanian")
verifyLanguageHeaderIsTranslated(ROMANIAN_LANGUAGE_HEADER)
selectLanguage("Français")
verifyLanguageHeaderIsTranslated(FRENCH_LANGUAGE_HEADER)
selectLanguage(FRENCH_SYSTEM_LOCALE_OPTION)
verifyLanguageHeaderIsTranslated("Language")
IdlingRegistry.getInstance().unregister(localeListIdlingResource)
}
}
@Test @Test
fun goToHomeScreenBottomToolbarTest() { fun goToHomeScreenBottomToolbarTest() {
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)

@ -192,12 +192,15 @@ class SettingsRobot {
return SettingsSubMenuAccessibilityRobot.Transition() return SettingsSubMenuAccessibilityRobot.Transition()
} }
fun openLanguageSubMenu(interact: SettingsSubMenuLanguageRobot.() -> Unit): SettingsSubMenuLanguageRobot.Transition { fun openLanguageSubMenu(
localizedText: String = getStringResource(R.string.preferences_language),
interact: SettingsSubMenuLanguageRobot.() -> Unit
): SettingsSubMenuLanguageRobot.Transition {
onView(withId(R.id.recycler_view)) onView(withId(R.id.recycler_view))
.perform( .perform(
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant( hasDescendant(
withText(R.string.preferences_language) withText(localizedText)
), ),
ViewActions.click() ViewActions.click()
) )

@ -7,13 +7,16 @@ package org.mozilla.fenix.ui.robots
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import junit.framework.TestCase.assertTrue import junit.framework.TestCase.assertTrue
import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.TestHelper.mDevice import org.mozilla.fenix.helpers.TestHelper.mDevice
import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.TestHelper.packageName
import org.mozilla.fenix.helpers.click
class SettingsSubMenuLanguageRobot { class SettingsSubMenuLanguageRobot {
fun selectLanguage(language: String) { fun selectLanguage(language: String) {
@ -23,9 +26,45 @@ class SettingsSubMenuLanguageRobot {
.click() .click()
} }
fun selectLanguageSearchResult(languageName: String) {
language(languageName).waitForExists(waitingTime)
language(languageName).click()
}
fun verifyLanguageHeaderIsTranslated(translation: String) = fun verifyLanguageHeaderIsTranslated(translation: String) =
assertTrue(mDevice.findObject(UiSelector().text(translation)).waitForExists(waitingTime)) assertTrue(mDevice.findObject(UiSelector().text(translation)).waitForExists(waitingTime))
fun verifySelectedLanguage(language: String) {
languagesList.waitForExists(waitingTime)
assertTrue(
languagesList
.getChildByText(UiSelector().text(language), language, true)
.getFromParent(UiSelector().resourceId("$packageName:id/locale_selected_icon"))
.waitForExists(waitingTime)
)
}
fun openSearchBar() {
onView(withId(R.id.search)).click()
}
fun typeInSearchBar(text: String) {
searchBar.waitForExists(waitingTime)
searchBar.text = text
}
fun verifySearchResultsContains(languageName: String) {
assertTrue(language(languageName).waitForExists(waitingTime))
}
fun clearSearchBar() {
onView(withId(R.id.search_close_btn)).click()
}
fun verifyLanguageListIsDisplayed() {
assertTrue(languagesList.waitForExists(waitingTime))
}
class Transition { class Transition {
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
@ -47,3 +86,8 @@ private val languagesList =
.resourceId("$packageName:id/locale_list") .resourceId("$packageName:id/locale_list")
.scrollable(true) .scrollable(true)
) )
private fun language(name: String) = mDevice.findObject(UiSelector().text(name))
private val searchBar =
mDevice.findObject(UiSelector().resourceId("$packageName:id/search_src_text"))

@ -33,6 +33,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.Constants.RETRY_COUNT import org.mozilla.fenix.helpers.Constants.RETRY_COUNT
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeLong import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeLong
import org.mozilla.fenix.helpers.TestHelper.getStringResource
import org.mozilla.fenix.helpers.TestHelper.mDevice import org.mozilla.fenix.helpers.TestHelper.mDevice
import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.TestHelper.packageName
import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.click
@ -145,13 +146,16 @@ class ThreeDotMenuMainRobot {
} }
class Transition { class Transition {
fun openSettings(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { fun openSettings(
localizedText: String = getStringResource(R.string.browser_menu_settings),
interact: SettingsRobot.() -> Unit
): SettingsRobot.Transition {
// We require one swipe to display the full size 3-dot menu. On smaller devices // We require one swipe to display the full size 3-dot menu. On smaller devices
// such as the Pixel 2, we require two swipes to display the "Settings" menu item // such as the Pixel 2, we require two swipes to display the "Settings" menu item
// at the bottom. On larger devices, the second swipe is a no-op. // at the bottom. On larger devices, the second swipe is a no-op.
threeDotMenuRecyclerView().perform(swipeUp()) threeDotMenuRecyclerView().perform(swipeUp())
threeDotMenuRecyclerView().perform(swipeUp()) threeDotMenuRecyclerView().perform(swipeUp())
settingsButton().click() settingsButton(localizedText).click()
SettingsRobot().interact() SettingsRobot().interact()
return SettingsRobot.Transition() return SettingsRobot.Transition()
@ -405,7 +409,9 @@ private fun threeDotMenuRecyclerViewExists() {
threeDotMenuRecyclerView().check(matches(isDisplayed())) threeDotMenuRecyclerView().check(matches(isDisplayed()))
} }
private fun settingsButton() = mDevice.findObject(UiSelector().text("Settings")) private fun settingsButton(localizedText: String = getStringResource(R.string.browser_menu_settings)) =
mDevice.findObject(UiSelector().text(localizedText))
private fun assertSettingsButton() = assertTrue(settingsButton().waitForExists(waitingTime)) private fun assertSettingsButton() = assertTrue(settingsButton().waitForExists(waitingTime))
private fun customizeHomeButton() = private fun customizeHomeButton() =

@ -8,5 +8,6 @@ const val STRING_ONBOARDING_ACCOUNT_SIGN_IN_HEADER = "Sync Firefox between devic
const val STRING_ONBOARDING_TRACKING_PROTECTION_HEADER = "Always-on privacy" const val STRING_ONBOARDING_TRACKING_PROTECTION_HEADER = "Always-on privacy"
const val STRING_ONBOARDING_TOOLBAR_PLACEMENT_HEADER = "Pick your toolbar placement" const val STRING_ONBOARDING_TOOLBAR_PLACEMENT_HEADER = "Pick your toolbar placement"
const val FRENCH_LANGUAGE_HEADER = "Langues" const val FRENCH_LANGUAGE_HEADER = "Langues"
const val ROMANIAN_LANGUAGE_HEADER = "Limbă" const val FR_SETTINGS = "Paramètres"
const val FRENCH_SYSTEM_LOCALE_OPTION = "Utiliser la langue de lappareil" const val FRENCH_SYSTEM_LOCALE_OPTION = "Utiliser la langue de lappareil"
const val ROMANIAN_LANGUAGE_HEADER = "Limbă"

Loading…
Cancel
Save