1
0
Fork 0

Adding UI tests for the Basic Menu settings. 🎲 !!!! (#6346)

* Added UI tests for the basic settings menu

* Disabled toggleSearchSuggestions test
master
Kadeem M 2020-02-06 15:58:47 -05:00 committed by GitHub
parent 6019adafac
commit 095477e0ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 599 additions and 159 deletions

View File

@ -4,9 +4,11 @@
package org.mozilla.fenix.helpers
import android.graphics.Bitmap
import android.view.View
import org.hamcrest.CoreMatchers.not
import org.hamcrest.Matcher
import org.mozilla.fenix.helpers.matchers.BitmapDrawableMatcher
import androidx.test.espresso.matcher.ViewMatchers.isChecked as espressoIsChecked
import androidx.test.espresso.matcher.ViewMatchers.isEnabled as espressoIsEnabled
import androidx.test.espresso.matcher.ViewMatchers.isSelected as espressoIsSelected
@ -30,3 +32,5 @@ private fun maybeInvertMatcher(matcher: Matcher<View>, useUnmodifiedMatcher: Boo
useUnmodifiedMatcher -> matcher
else -> not(matcher)
}
fun withBitmapDrawable(bitmap: Bitmap, name: String): Matcher<View>? = BitmapDrawableMatcher(bitmap, name)

View File

@ -0,0 +1,36 @@
package org.mozilla.fenix.helpers.assertions
import android.view.View
import androidx.test.espresso.ViewAssertion
import mozilla.components.browser.awesomebar.BrowserAwesomeBar
class AwesomeBarAssertion {
companion object {
fun suggestionsAreGreaterThan(minimumSuggestions: Int): ViewAssertion {
return ViewAssertion { view, noViewFoundException ->
if (noViewFoundException != null) throw noViewFoundException
val suggestionsCount = getSuggestionCountFromView(view)
if (suggestionsCount <= minimumSuggestions)
throw AssertionError("The suggestion count is less than or equal to the minimum suggestions")
}
}
fun suggestionsAreEqualTo(expectedItemCount: Int): ViewAssertion {
return ViewAssertion { view, noViewFoundException ->
if (noViewFoundException != null) throw noViewFoundException
val suggestionsCount = getSuggestionCountFromView(view)
if (suggestionsCount != expectedItemCount)
throw AssertionError("The expected item count is $expectedItemCount, and the suggestions count within the AwesomeBar is $suggestionsCount")
}
}
private fun getSuggestionCountFromView(view: View): Int {
return (view as BrowserAwesomeBar).adapter?.itemCount
?: throw AssertionError("This view is not of type BrowserAwesomeBar")
}
}
}

View File

@ -0,0 +1,35 @@
package org.mozilla.fenix.helpers.matchers
import android.graphics.Bitmap
import android.view.View
import android.widget.ImageView
import androidx.test.espresso.matcher.BoundedMatcher
import org.hamcrest.Description
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.Drawable
class BitmapDrawableMatcher(private val bitmap: Bitmap, private val name: String) :
BoundedMatcher<View, ImageView>(ImageView::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("has image drawable resource $name")
}
override fun matchesSafely(item: ImageView): Boolean {
return sameBitmap(item.drawable, bitmap)
}
private fun sameBitmap(drawable: Drawable?, otherBitmap: Bitmap): Boolean {
var currentDrawable = drawable ?: return false
if (currentDrawable is StateListDrawable) {
currentDrawable = currentDrawable.current
}
if (currentDrawable is BitmapDrawable) {
val bitmap = currentDrawable.bitmap
return bitmap.sameAs(otherBitmap)
}
return false
}
}

View File

@ -52,11 +52,11 @@ class SearchTest {
homeScreen {
}.openSearch {
verifySearchWithText()
clickDuckDuckGoEngineButton()
clickSearchEngineButton("DuckDuckGo")
typeSearch("mozilla")
verifyDuckDuckGoResults()
clickDuckDuckGoResult()
verifyDuckDuckGoURL()
verifySearchEngineResults("DuckDuckGo")
clickSearchEngineResult("DuckDuckGo")
verifySearchEngineURL("DuckDuckGo")
}
}

View File

@ -10,12 +10,17 @@ import androidx.test.uiautomator.UiDevice
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.Ignore
import org.mozilla.fenix.FenixApplication
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
import org.mozilla.fenix.helpers.TestAssetHelper.getGenericAsset
import org.mozilla.fenix.helpers.TestAssetHelper.getLoremIpsumAsset
import org.mozilla.fenix.ui.robots.checkTextSizeOnWebsite
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
/**
* Tests for verifying the main three dot menu options
@ -29,7 +34,7 @@ class SettingsBasicsTest {
private lateinit var mockWebServer: MockWebServer
@get:Rule
val activityTestRule = HomeActivityTestRule()
val activityIntentTestRule = HomeActivityIntentTestRule()
@Before
fun setUp() {
@ -45,7 +50,8 @@ class SettingsBasicsTest {
}
private fun getUiTheme(): Boolean {
val mode = activityTestRule.activity.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK)
val mode =
activityIntentTestRule.activity.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK)
return when (mode) {
Configuration.UI_MODE_NIGHT_YES -> true // dark theme is set
@ -68,6 +74,7 @@ class SettingsBasicsTest {
verifySearchEngineList()
verifyShowSearchSuggestions()
verifyShowSearchShortcuts()
verifyShowClipboardSuggestions()
verifySearchBrowsingHistory()
verifySearchBookmarks()
@ -76,7 +83,7 @@ class SettingsBasicsTest {
verifyThemes()
}.goBack {
}.openAccessibilitySubMenu {
verifyAutomaticFontSizing()
verifyAutomaticFontSizingMenuItems()
}.goBack {
// drill down to submenu
}.openDefaultBrowserSubMenu {
@ -86,49 +93,75 @@ class SettingsBasicsTest {
}
}
@Ignore("This is a stub test, ignore for now")
@Test
fun selectNewDefaultSearchEngine() {
// Open 3dot (main) menu
// Select settings
// Select "Search engine"
// Choose: DuckDuckGo
// Back arrow to Home
// Verify DuckDuckGo icon in Navigation bar
// Goes through the settings and changes the default search engine, then verifies it has changed.
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openSearchSubMenu {
changeDefaultSearchEngine("DuckDuckGo")
}.goBack {
}.goBack {
verifyDefaultSearchEngine("DuckDuckGo")
}
}
@Ignore("This is a stub test, ignore for now")
@Ignore("This test works locally, fails on firebase. https://github.com/mozilla-mobile/fenix/issues/8174")
@Test
fun toggleSearchSuggestions() {
// Enter: "mozilla" in navigation bar
// Verify more than one suggesion provided
// Open 3dot (main) menu
// Select settings
// Select "Search engine"
// Toggle 'Show search suggestions' to 'off'
// Back arrow twice to home screen
// Enter: "mozilla" in navigation bar
// Verify no suggestions provided
// Goes through the settings and changes the search suggestion toggle, then verifies it changes.
homeScreen {
}.openNavigationToolbar {
verifySearchSuggestionsAreMoreThan(1, "mozilla")
}.goBack {
}.openThreeDotMenu {
}.openSettings {
}.openSearchSubMenu {
disableShowSearchSuggestions()
}.goBack {
}.goBack {
}.openNavigationToolbar {
verifySearchSuggestionsAreEqualTo(0, "mozilla")
}
}
@Ignore("This is a stub test, ignore for now")
@Test
fun toggleShowVisitedSitesAndBookmarks() {
// Visit 3 static sites
// Bookmark 2 of them
// Open 3dot (main) menu
// Enter navigation bar and verify visited sites appear
// Verify bookmarks exist
// Open 3dot (main) menu
// Select settings
// Select "Search engine"
// Toggle off "Show visited sites and bookmarks"
// Back arrow twice to home screen
// Verify history and bookmarks are gone
// Bookmarks a few websites, toggles the history and bookmarks setting to off, then verifies if the visited and bookmarked websites do not show in the suggestions.
val page1 = getGenericAsset(mockWebServer, 1)
val page2 = getGenericAsset(mockWebServer, 2)
val page3 = getGenericAsset(mockWebServer, 3)
homeScreen {
}.openNavigationToolbar {
}.enterURLAndEnterToBrowser(page1.url) {
verifyPageContent(page1.content)
}.openThreeDotMenu {
clickAddBookmarkButton()
}
navigationToolbar {
}.enterURLAndEnterToBrowser(page2.url) {
verifyPageContent(page2.content)
}.openThreeDotMenu {
clickAddBookmarkButton()
}
navigationToolbar {
}.enterURLAndEnterToBrowser(page3.url) {
verifyPageContent(page3.content)
}
navigationToolbar {
verifyNoHistoryBookmarks()
}
}
@Test
fun changeThemeSetting() {
// Goes through the settings and changes the default search engine, then verifies it changes.
homeScreen {
}.openThreeDotMenu {
}.openSettings {
@ -141,40 +174,48 @@ class SettingsBasicsTest {
}
}
@Ignore("This is a stub test, ignore for now")
@Test
fun changeAccessibiltySettings() {
// Open 3dot (main) menu
// Select settings
// Select Accessibility
// Verify header: "Automatic Font Sizing"
// Verify description: "Font size will match your Android settings. Disable to manage font size here"
// Verify toggle is set to 'on' by default
// Toggle font size to off
// Verify that new sub-menu items appear....
// Verify header: "Font Size"
// Verify description: "Make text on websites larger or smaller"
// Verify slider bar exists
// Verify slider bar default value set to 100%
// Verify sample text "The quick brown fox..." appears 4 times
// Move slider bar to 180%
// Verify that text grows to 180%
// Back error twice to home screen
// Open static website in navigation bar
// Verify that text is now at 180%
// Select settings
// Select Accessibility
// Toggle font size back to 'off'
// Verify that "Font Size" header, description, slider bar and sample text all disappear
// Goes through the settings and changes the default text on a webpage, then verifies if the text has changed.
val fenixApp = activityIntentTestRule.activity.applicationContext as FenixApplication
val webpage = getLoremIpsumAsset(mockWebServer).url
// This value will represent the text size percentage the webpage will scale to. The default value is 100%.
val textSizePercentage = 180
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openAccessibilitySubMenu {
clickFontSizingSwitch()
verifyNewMenuItems()
changeTextSizeSlider(textSizePercentage)
verifyTextSizePercentage(textSizePercentage)
}.goBack {
}.goBack {
}.openNavigationToolbar {
}.enterURLAndEnterToBrowser(webpage) {
checkTextSizeOnWebsite(textSizePercentage, fenixApp.components)
}.openHomeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openAccessibilitySubMenu {
clickFontSizingSwitch()
verifyNewMenuItemsAreGone()
}
}
@Ignore("This is a stub test, ignore for now")
@Test
fun changeDefaultBrowserSetting() {
// Open 3dot (main) menu
// Select settings
// Verify that "Set as default browser toggle is set to 'off' (default)
// Turn default browser toggle 'on'
// Verify that Andrdoid "Default Apps" menu appears
// Opens settings and toggles the default browser setting to on. The device settings open and allows the user to set a default browser.
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openDefaultBrowserSubMenu {
verifyDefaultBrowserIsDisabled()
clickDefaultBrowserSwitch()
verifyAndroidDefaultAppsMenuAppears()
}
}
}

View File

@ -7,6 +7,7 @@
package org.mozilla.fenix.ui.robots
import androidx.recyclerview.widget.RecyclerView
import android.graphics.Bitmap
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
@ -14,12 +15,12 @@ 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.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
@ -34,6 +35,8 @@ import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.components.Search
import org.mozilla.fenix.helpers.withBitmapDrawable
/**
* Implementation of Robot Pattern for the home screen menu.
@ -53,6 +56,7 @@ class HomeScreenRobot {
fun verifyHomeWordmark() = assertHomeWordmark()
fun verifyHomeToolbar() = assertHomeToolbar()
fun verifyHomeComponent() = assertHomeComponent()
fun verifyDefaultSearchEngine(searchEngine: String) = verifySearchEngineIcon(searchEngine)
// First Run elements
fun verifyWelcomeHeader() = assertWelcomeHeader()
@ -81,6 +85,7 @@ class HomeScreenRobot {
// Private mode elements
fun verifyPrivateSessionHeader() = assertPrivateSessionHeader()
fun verifyPrivateSessionMessage(visible: Boolean = true) = assertPrivateSessionMessage(visible)
fun verifyPrivateTabsCloseTabsButton() = assertPrivateTabsCloseTabsButton()
@ -114,7 +119,6 @@ class HomeScreenRobot {
fun typeCollectionName(name: String) {
mDevice.wait(Until.findObject(By.res("name_collection_edittext")), waitingTime)
collectionNameTextField().perform(ViewActions.replaceText(name))
collectionNameTextField().perform(ViewActions.pressImeActionButton())
}
@ -206,6 +210,13 @@ class HomeScreenRobot {
HomeScreenRobot().interact()
return Transition()
}
fun openNavigationToolbar(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition {
assertNavigationToolbar().perform(click())
NavigationToolbarRobot().interact()
return NavigationToolbarRobot.Transition()
}
}
}
@ -215,6 +226,7 @@ fun homeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition
}
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
private fun navigationToolbar() =
onView(CoreMatchers.allOf(withText("Search or enter address")))
@ -279,6 +291,19 @@ private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("sessio
private fun threeDotButton() = onView(allOf(withId(R.id.menuButton)))
private fun verifySearchEngineIcon(searchEngineIcon: Bitmap, searchEngineName: String) {
onView(withId(R.id.search_engine_icon))
.check(matches(withBitmapDrawable(searchEngineIcon, searchEngineName)))
}
private fun getSearchEngine(searchEngineName: String) =
Search(appContext).searchEngineManager.getDefaultSearchEngine(appContext, searchEngineName)
private fun verifySearchEngineIcon(searchEngineName: String) {
val ddgSearchEngine = getSearchEngine(searchEngineName)
verifySearchEngineIcon(ddgSearchEngine.icon, ddgSearchEngine.name)
}
// First Run elements
private fun assertWelcomeHeader() =
onView(CoreMatchers.allOf(withText("Welcome to Firefox Preview!")))
@ -418,7 +443,8 @@ private fun assertExistingOpenTabs(title: String) =
private fun tabsListThreeDotButton() = onView(allOf(withId(R.id.tabs_overflow_button)))
private fun collectionThreeDotButton() = onView(allOf(withId(R.id.collection_overflow_button)))
private fun collectionThreeDotButton() =
onView(allOf(withId(R.id.collection_overflow_button)))
private fun collectionNameTextField() =
onView(allOf(ViewMatchers.withResourceName("name_collection_edittext")))

View File

@ -10,26 +10,49 @@ import android.net.Uri
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.pressImeActionButton
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.not
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.helpers.assertions.AwesomeBarAssertion.Companion.suggestionsAreEqualTo
import org.mozilla.fenix.helpers.assertions.AwesomeBarAssertion.Companion.suggestionsAreGreaterThan
/**
* Implementation of Robot Pattern for the URL toolbar.
*/
class NavigationToolbarRobot {
fun verifySearchSuggestionsAreMoreThan(suggestionSize: Int, searchTerm: String) =
assertSuggestionsAreMoreThan(suggestionSize, searchTerm)
fun verifySearchSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) =
assertSuggestionsAreEqualTo(suggestionSize, searchTerm)
fun verifyNoHistoryBookmarks() = assertNoHistoryBookmarks()
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun enterURLAndEnterToBrowser(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitNotNull(Until.findObject(By.res("org.mozilla.fenix.debug:id/toolbar")), waitingTime)
fun enterURLAndEnterToBrowser(
url: Uri,
interact: BrowserRobot.() -> Unit
): BrowserRobot.Transition {
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/toolbar")),
waitingTime
)
urlBar().click()
awesomeBar().perform(replaceText(url.toString()), pressImeActionButton())
@ -45,7 +68,10 @@ class NavigationToolbarRobot {
return ThreeDotMenuMainRobot.Transition()
}
fun openNewTabAndEnterToBrowser(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
fun openNewTabAndEnterToBrowser(
url: Uri,
interact: BrowserRobot.() -> Unit
): BrowserRobot.Transition {
mDevice.waitNotNull(Until.findObject(By.descContains("Add tab")), waitingTime)
newTab().click()
awesomeBar().perform(replaceText(url.toString()), pressImeActionButton())
@ -75,6 +101,13 @@ class NavigationToolbarRobot {
BrowserRobot().interact()
return BrowserRobot.Transition()
}
fun goBack(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
goBackButton()
HomeScreenRobot().interact()
return HomeScreenRobot.Transition()
}
}
}
@ -83,10 +116,34 @@ fun navigationToolbar(interact: NavigationToolbarRobot.() -> Unit): NavigationTo
return NavigationToolbarRobot.Transition()
}
private fun dismissOnboardingButton() = onView(ViewMatchers.withId(R.id.close_onboarding))
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.clipboard_url))
private fun clearAddressBar() = onView(ViewMatchers.withId(R.id.mozac_browser_toolbar_clear_view))
private fun assertSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) {
mDevice.waitForIdle()
awesomeBar().perform(typeText(searchTerm))
mDevice.waitForIdle()
onView(withId(R.id.awesomeBar)).check(suggestionsAreEqualTo(suggestionSize))
}
private fun assertSuggestionsAreMoreThan(suggestionSize: Int, searchTerm: String) {
mDevice.waitForIdle()
awesomeBar().perform(typeText(searchTerm))
mDevice.waitForIdle()
onView(withId(R.id.awesomeBar)).check(suggestionsAreGreaterThan(suggestionSize))
}
private fun assertNoHistoryBookmarks() {
onView(withId(R.id.container))
.check(matches(not(hasDescendant(withText("Test_Page_1")))))
.check(matches(not(hasDescendant(withText("Test_Page_2")))))
.check(matches(not(hasDescendant(withText("Test_Page_3")))))
}
private fun dismissOnboardingButton() = onView(withId(R.id.close_onboarding))
private fun urlBar() = onView(withId(R.id.toolbar))
private fun awesomeBar() = onView(withId(R.id.mozac_browser_toolbar_edit_url_view))
private fun threeDotButton() = onView(withContentDescription("Menu"))
private fun newTab() = onView(withContentDescription("Add tab"))
private fun fillLinkButton() = onView(withId(R.id.fill_link_from_clipboard))
private fun clearAddressBar() = onView(withId(R.id.mozac_browser_toolbar_clear_view))
private fun goBackButton() = mDevice.pressBack()

View File

@ -39,8 +39,10 @@ class SearchRobot {
fun verifyBrowserToolbar() = assertBrowserToolbarEditView()
fun verifyScanButton() = assertScanButton()
fun verifySearchWithText() = assertSearchWithText()
fun verifyDuckDuckGoResults() = assertDuckDuckGoResults()
fun verifyDuckDuckGoURL() = assertDuckDuckGoURL()
fun verifySearchEngineResults(searchEngineName: String) =
assertSearchEngineResults(searchEngineName)
fun verifySearchEngineURL(searchEngineName: String) = assertSearchEngineURL(searchEngineName)
fun verifySearchSettings() = assertSearchSettings()
fun verifySearchBarEmpty() = assertSearchBarEmpty()
@ -60,13 +62,21 @@ class SearchRobot {
browserToolbarEditView().perform(typeText(searchTerm))
}
fun clickDuckDuckGoEngineButton() {
duckDuckGoEngineButton().perform(click())
fun clickSearchEngineButton(searchEngineName: String) {
searchEngineButton(searchEngineName).perform(click())
}
fun clickDuckDuckGoResult() {
mDevice.waitNotNull(Until.findObjects(By.text("DuckDuckGo")), TestAssetHelper.waitingTime)
awesomeBar().perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
fun clickSearchEngineResult(searchEngineName: String) {
mDevice.waitNotNull(
Until.findObjects(By.text(searchEngineName)),
TestAssetHelper.waitingTime
)
awesomeBar().perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
0,
click()
)
)
}
fun scrollToSearchEngineSettings(): UiScrollable {
@ -88,7 +98,7 @@ class SearchRobot {
fun openBrowser(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitForIdle()
browserToolbarEditView().perform(typeText("Mozilla\n"))
browserToolbarEditView().perform(typeText("mozilla\n"))
BrowserRobot().interact()
return BrowserRobot.Transition()
@ -98,11 +108,12 @@ class SearchRobot {
private fun awesomeBar() = onView(withId(R.id.awesomeBar))
private fun browserToolbarEditView() = onView(Matchers.allOf(withId(R.id.mozac_browser_toolbar_edit_url_view)))
private fun browserToolbarEditView() =
onView(Matchers.allOf(withId(R.id.mozac_browser_toolbar_edit_url_view)))
private fun duckDuckGoEngineButton(): ViewInteraction {
mDevice.waitNotNull(Until.findObject(By.text("DuckDuckGo")), TestAssetHelper.waitingTime)
return onView(Matchers.allOf(withText("DuckDuckGo")))
private fun searchEngineButton(searchEngineName: String): ViewInteraction {
mDevice.waitNotNull(Until.findObject(By.text(searchEngineName)), TestAssetHelper.waitingTime)
return onView(Matchers.allOf(withText(searchEngineName)))
}
private fun denyPermissionButton(): UiObject {
@ -122,14 +133,18 @@ private fun scanButton(): ViewInteraction {
private fun clearButton() = onView(withId(R.id.mozac_browser_toolbar_clear_view))
private fun assertDuckDuckGoURL() {
mDevice.waitNotNull(Until.findObject(By.textContains("https://duckduckgo.com/?q=mozilla")), TestAssetHelper.waitingTime)
onView(allOf(withText(startsWith("https://duckduckgo.com"))))
private fun assertSearchEngineURL(searchEngineName: String) {
mDevice.waitNotNull(
Until.findObject(By.textContains("https://${searchEngineName.toLowerCase()}.com/?q=mozilla")),
TestAssetHelper.waitingTime
)
onView(allOf(withText(startsWith("https://${searchEngineName.toLowerCase()}.com"))))
.check(matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
private fun assertDuckDuckGoResults() {
val count = mDevice.wait(Until.findObjects(By.text(("DuckDuckGo"))), TestAssetHelper.waitingTime)
private fun assertSearchEngineResults(searchEngineName: String) {
val count =
mDevice.wait(Until.findObjects(By.text((searchEngineName))), TestAssetHelper.waitingTime)
assert(count.size > 1)
}

View File

@ -6,27 +6,67 @@
package org.mozilla.fenix.ui.robots
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers
import android.view.KeyEvent
import android.view.KeyEvent.KEYCODE_DPAD_RIGHT
import android.view.KeyEvent.KEYCODE_DPAD_LEFT
import android.view.KeyEvent.ACTION_DOWN
import android.view.View
import android.widget.SeekBar
import android.widget.TextView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.ViewAssertion
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import org.hamcrest.CoreMatchers.allOf
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import org.hamcrest.Matcher
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.ui.robots.SettingsSubMenuAccessibilityRobot.Companion.DECIMAL_CONVERSION
import org.mozilla.fenix.ui.robots.SettingsSubMenuAccessibilityRobot.Companion.MIN_VALUE
import org.mozilla.fenix.ui.robots.SettingsSubMenuAccessibilityRobot.Companion.STEP_SIZE
import org.mozilla.fenix.ui.robots.SettingsSubMenuAccessibilityRobot.Companion.TEXT_SIZE
import kotlin.math.roundToInt
/**
* Implementation of Robot Pattern for the settings Accessibility sub menu.
*/
class SettingsSubMenuAccessibilityRobot {
fun verifyAutomaticFontSizing() = assertAutomaticFontSizing()
companion object {
const val STEP_SIZE = 5
const val MIN_VALUE = 50
const val DECIMAL_CONVERSION = 100f
const val TEXT_SIZE = 16f
}
fun verifyAutomaticFontSizingMenuItems() = assertAutomaticFontSizingMenuItems()
fun clickFontSizingSwitch() = toggleFontSizingSwitch()
fun verifyNewMenuItems() = assertNewMenuItems()
fun verifyNewMenuItemsAreGone() = assertNewMenuItemsAreGone()
fun changeTextSizeSlider(seekBarPercentage: Int) = adjustTextSizeSlider(seekBarPercentage)
fun verifyTextSizePercentage(textSize: Int) = assertTextSizePercentage(textSize)
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
mDevice.waitForIdle()
goBackButton().perform(ViewActions.click())
goBackButton().perform(click())
SettingsRobot().interact()
return SettingsRobot.Transition()
@ -34,13 +74,127 @@ class SettingsSubMenuAccessibilityRobot {
}
}
private fun assertAutomaticFontSizing() {
Espresso.onView(ViewMatchers.withText("Automatic Font Sizing"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private fun assertAutomaticFontSizingMenuItems() {
onView(withText("Automatic Font Sizing"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
val strFont = "Font size will match your Android settings. Disable to manage font size here."
Espresso.onView(ViewMatchers.withText(strFont))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText(strFont))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun toggleFontSizingSwitch() {
// Toggle font size to off
onView(withText("Automatic Font Sizing"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.perform(click())
}
private fun assertNewMenuItems() {
assertFontSize()
assertSliderBar()
}
private fun assertFontSize() {
val view = onView(withText("Font Size"))
view.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
val strFont = "Make text on websites larger or smaller"
onView(withText(strFont))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertSliderBar() {
onView(withId(org.mozilla.fenix.R.id.sampleText))
.check(matches(withText("This is sample text. It is here to show how text will appear when you increase or decrease the size with this setting.")))
onView(withId(org.mozilla.fenix.R.id.seekbar_value))
.check(matches(withText("100%")))
onView(withId(org.mozilla.fenix.R.id.seekbar))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun adjustTextSizeSlider(seekBarPercentage: Int) {
onView(withId(org.mozilla.fenix.R.id.seekbar))
.perform(SeekBarChangeProgressViewAction(seekBarPercentage))
}
private fun assertTextSizePercentage(textSize: Int) {
onView(withId(org.mozilla.fenix.R.id.sampleText))
.check(textSizePercentageEquals(textSize))
}
private fun assertNewMenuItemsAreGone() {
onView(withText("Font Size")).check(doesNotExist())
val strFont = "Make text on websites larger or smaller"
onView(withText(strFont))
.check(doesNotExist())
onView(withId(org.mozilla.fenix.R.id.sampleText))
.check(doesNotExist())
onView(withId(org.mozilla.fenix.R.id.seekbar_value))
.check(doesNotExist())
onView(withId(org.mozilla.fenix.R.id.seekbar))
.check(doesNotExist())
}
private fun goBackButton() =
Espresso.onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
onView(allOf(withContentDescription("Navigate up")))
class SeekBarChangeProgressViewAction(val seekBarPercentage: Int) : ViewAction {
override fun getConstraints(): Matcher<View> {
return isAssignableFrom(SeekBar::class.java)
}
override fun perform(uiController: UiController?, view: View?) {
val targetStepSize = calculateStepSizeFromPercentage(seekBarPercentage)
val seekbar = view as SeekBar
var progress = seekbar.progress
if (targetStepSize > progress) {
for (i in progress until targetStepSize) {
seekbar.onKeyDown(KEYCODE_DPAD_RIGHT, KeyEvent(ACTION_DOWN, KEYCODE_DPAD_RIGHT))
}
} else if (progress > targetStepSize) {
for (i in progress downTo targetStepSize) {
seekbar.onKeyDown(KEYCODE_DPAD_LEFT, KeyEvent(ACTION_DOWN, KEYCODE_DPAD_LEFT))
}
}
}
override fun getDescription(): String {
return "Changes the progress on a SeekBar, based on the percentage value."
}
}
fun textSizePercentageEquals(textSizePercentage: Int): ViewAssertion {
return ViewAssertion { view, noViewFoundException ->
if (noViewFoundException != null) throw noViewFoundException
val textView = view as TextView
val scaledPixels =
textView.textSize / InstrumentationRegistry.getInstrumentation().context.resources.displayMetrics.scaledDensity
val currentTextSizePercentage = calculateTextPercentageFromTextSize(scaledPixels)
if (currentTextSizePercentage != textSizePercentage) throw AssertionError("The textview has a text size percentage of $currentTextSizePercentage, and does not match $textSizePercentage")
}
}
fun calculateTextPercentageFromTextSize(textSize: Float): Int {
val decimal = textSize / TEXT_SIZE
return (decimal * DECIMAL_CONVERSION).roundToInt()
}
fun calculateStepSizeFromPercentage(textSizePercentage: Int): Int {
return ((textSizePercentage - MIN_VALUE) / STEP_SIZE)
}
fun checkTextSizeOnWebsite(textSizePercentage: Int, components: Components): Boolean {
// Checks the Gecko engine settings for the font size
val textSize = calculateStepSizeFromPercentage(textSizePercentage)
val newTextScale = ((textSize * STEP_SIZE) + MIN_VALUE).toFloat() / DECIMAL_CONVERSION
return components.core.engine.settings.fontSizeFactor == newTextScale
}

View File

@ -8,18 +8,37 @@ package org.mozilla.fenix.ui.robots
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.mozilla.fenix.R
import org.mozilla.fenix.ui.robots.SettingsSubMenuDefaultBrowserRobot.Companion.DEFAULT_APPS_SETTINGS_ACTION
/**
* Implementation of Robot Pattern for the settings DefaultBrowser sub menu.
*/
class SettingsSubMenuDefaultBrowserRobot {
companion object {
const val DEFAULT_APPS_SETTINGS_ACTION = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"
}
fun verifyOpenLinksInPrivateTab() = assertOpenLinksInPrivateTab()
fun verifyDefaultBrowserIsDisabled() = assertDefaultBrowserIsDisabled()
fun clickDefaultBrowserSwitch() = toggleDefaultBrowserSwitch()
fun verifyAndroidDefaultAppsMenuAppears() = assertAndroidDefaultAppsMenuAppears()
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@ -34,10 +53,29 @@ class SettingsSubMenuDefaultBrowserRobot {
}
}
fun assertDefaultBrowserIsDisabled() {
onView(withId(R.id.switch_widget))
.check(matches(isNotChecked()))
}
fun toggleDefaultBrowserSwitch() {
onView(
allOf(
withParent(not(withId(R.id.navigationToolbar))),
withText("Set as default browser")
)
)
.perform(click())
}
private fun assertAndroidDefaultAppsMenuAppears() {
intended(hasAction(DEFAULT_APPS_SETTINGS_ACTION))
}
private fun assertOpenLinksInPrivateTab() {
onView(ViewMatchers.withText("Open links in private tab"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Open links in private tab"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun goBackButton() =
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
onView(allOf(withContentDescription("Navigate up")))

View File

@ -8,14 +8,15 @@ 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.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.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers
@ -31,13 +32,17 @@ class SettingsSubMenuSearchRobot {
fun verifyShowClipboardSuggestions() = assertShowClipboardSuggestions()
fun verifySearchBrowsingHistory() = assertSearchBrowsingHistory()
fun verifySearchBookmarks() = assertSearchBookmarks()
fun changeDefaultSearchEngine(searchEngineName: String) =
selectDefaultSearchEngine(searchEngineName)
fun disableShowSearchSuggestions() = toggleShowSearchSuggestions()
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
mDevice.waitForIdle()
goBackButton().perform(ViewActions.click())
goBackButton().perform(click())
SettingsRobot().interact()
return SettingsRobot.Transition()
@ -46,24 +51,24 @@ class SettingsSubMenuSearchRobot {
}
private fun assertDefaultSearchEngineHeader() =
onView(ViewMatchers.withText("Default search engine"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Default search engine"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertSearchEngineList() {
onView(ViewMatchers.withText("Google"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Amazon.com"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Bing"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("DuckDuckGo"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Twitter"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Wikipedia"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Add search engine"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Google"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("Amazon.com"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("Bing"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("DuckDuckGo"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("Twitter"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("Wikipedia"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(withText("Add search engine"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertShowSearchSuggestions() {
@ -72,8 +77,8 @@ private fun assertShowSearchSuggestions() {
hasDescendant(withText("Show search suggestions"))
)
)
onView(ViewMatchers.withText("Show search suggestions"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Show search suggestions"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertShowSearchShortcuts() {
@ -82,8 +87,8 @@ private fun assertShowSearchShortcuts() {
hasDescendant(withText("Show search shortcuts"))
)
)
onView(ViewMatchers.withText("Show search shortcuts"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Show search shortcuts"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertShowClipboardSuggestions() {
@ -92,8 +97,8 @@ private fun assertShowClipboardSuggestions() {
hasDescendant(withText("Show clipboard suggestions"))
)
)
onView(ViewMatchers.withText("Show clipboard suggestions"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Show clipboard suggestions"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertSearchBrowsingHistory() {
@ -102,8 +107,8 @@ private fun assertSearchBrowsingHistory() {
hasDescendant(withText("Search browsing history"))
)
)
onView(ViewMatchers.withText("Search browsing history"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Search browsing history"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertSearchBookmarks() {
@ -112,9 +117,30 @@ private fun assertSearchBookmarks() {
hasDescendant(withText("Search bookmarks"))
)
)
onView(ViewMatchers.withText("Search bookmarks"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Search bookmarks"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun selectDefaultSearchEngine(searchEngine: String) {
onView(withText(searchEngine))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.perform(click())
}
private fun selectDuckDuckGoAsSearchEngine() {
selectDefaultSearchEngine("DuckDuckGo")
}
private fun toggleShowSearchSuggestions() {
onView(withId(androidx.preference.R.id.recycler_view)).perform(
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
hasDescendant(withText("Show search suggestions"))
)
)
onView(withText("Show search suggestions"))
.perform(click())
}
private fun goBackButton() =
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
onView(CoreMatchers.allOf(withContentDescription("Navigate up")))

View File

@ -6,7 +6,9 @@
package org.mozilla.fenix.ui.robots
import android.os.Build
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
@ -25,7 +27,8 @@ class SettingsSubMenuThemeRobot {
fun verifyThemes() = assertThemes()
fun verifyLightThemeApplied(expected: Boolean) = assertFalse("Light theme not selected", expected)
fun verifyLightThemeApplied(expected: Boolean) =
assertFalse("Light theme not selected", expected)
fun verifyDarkThemeApplied(expected: Boolean) = assertTrue("Dark theme not selected", expected)
@ -47,18 +50,23 @@ class SettingsSubMenuThemeRobot {
}
private fun assertThemes() {
onView(withText("Light"))
lightModeToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(withText("Dark"))
darkModeToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
deviceModeToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
// Conditionally unavailable on API 25
// onView(ViewMatchers.withText("Follow device theme"))
// .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
private fun goBackButton() =
onView(allOf(ViewMatchers.withContentDescription("Navigate up")))
private fun darkModeToggle() = onView(withText("Dark"))
private fun lightModeToggle() = onView(withText("Light"))
private fun deviceModeToggle(): ViewInteraction {
val followDeviceThemeText =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "Follow device theme" else "Set by Battery Saver"
return onView(withText(followDeviceThemeText))
}
private fun goBackButton() =
onView(allOf(ViewMatchers.withContentDescription("Navigate up")))