Add automated UI test for main three dot menu (#2540)
parent
b3a3c94169
commit
ba3edbcf92
|
@ -0,0 +1,32 @@
|
||||||
|
/* 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.helpers
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import org.hamcrest.CoreMatchers.not
|
||||||
|
import org.hamcrest.Matcher
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [espressoIsEnabled] function that can also handle disabled state through the boolean argument.
|
||||||
|
*/
|
||||||
|
fun isEnabled(isEnabled: Boolean): Matcher<View> = maybeInvertMatcher(espressoIsEnabled(), isEnabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [espressoIsChecked] function that can also handle unchecked state through the boolean argument.
|
||||||
|
*/
|
||||||
|
fun isChecked(isChecked: Boolean): Matcher<View> = maybeInvertMatcher(espressoIsChecked(), isChecked)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [espressoIsSelected] function that can also handle not selected state through the boolean argument.
|
||||||
|
*/
|
||||||
|
fun isSelected(isSelected: Boolean): Matcher<View> = maybeInvertMatcher(espressoIsSelected(), isSelected)
|
||||||
|
|
||||||
|
private fun maybeInvertMatcher(matcher: Matcher<View>, useUnmodifiedMatcher: Boolean): Matcher<View> = when {
|
||||||
|
useUnmodifiedMatcher -> matcher
|
||||||
|
else -> not(matcher)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* 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.helpers
|
||||||
|
|
||||||
|
import androidx.test.espresso.ViewInteraction
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
|
||||||
|
fun ViewInteraction.click(): ViewInteraction = this.perform(ViewActions.click())!!
|
||||||
|
|
||||||
|
fun ViewInteraction.assertIsEnabled(isEnabled: Boolean): ViewInteraction {
|
||||||
|
return this.check(matches(isEnabled(isEnabled)))!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ViewInteraction.assertIsChecked(isChecked: Boolean): ViewInteraction {
|
||||||
|
return this.check(matches(isChecked(isChecked)))!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ViewInteraction.assertIsSelected(isSelected: Boolean): ViewInteraction {
|
||||||
|
return this.check(matches(isSelected(isSelected)))!!
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* 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 org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||||
|
import org.mozilla.fenix.ui.robots.homeScreen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for verifying the main three dot menu options
|
||||||
|
*
|
||||||
|
* Including:
|
||||||
|
* - Verify all menu items present
|
||||||
|
* - Open library button opens library menu
|
||||||
|
* - Open settings button opens settings menu
|
||||||
|
* - Open Help button opens support page in browser
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ThreeDotMenuTest {
|
||||||
|
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||||
|
@get:Rule
|
||||||
|
val activityTestRule = HomeActivityTestRule()
|
||||||
|
@Test
|
||||||
|
fun threeDotMenuItemsTest() {
|
||||||
|
homeScreen {
|
||||||
|
}.openThreeDotMenu {
|
||||||
|
verifySettingsButton()
|
||||||
|
verifyLibraryButton()
|
||||||
|
verifyHelpButton()
|
||||||
|
}.openSettings {
|
||||||
|
verifySettingsView()
|
||||||
|
}.goBack {
|
||||||
|
}.openThreeDotMenu {
|
||||||
|
}.openLibrary {
|
||||||
|
verifyLibraryView()
|
||||||
|
}.goBack {
|
||||||
|
}.openThreeDotMenu {
|
||||||
|
}.openHelp {
|
||||||
|
verifyHelpUrl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@file:Suppress("TooManyFunctions")
|
||||||
|
|
||||||
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import org.hamcrest.CoreMatchers.containsString
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
class BrowserRobot {
|
||||||
|
|
||||||
|
fun verifyHelpUrl() {
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
val redirectUrl = "https://support.mozilla.org/"
|
||||||
|
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
onView(withId(R.id.mozac_browser_toolbar_url_view))
|
||||||
|
.check(matches(withText(containsString(redirectUrl))))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,33 +7,58 @@
|
||||||
package org.mozilla.fenix.ui.robots
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import org.hamcrest.Matchers.allOf
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Robot Pattern for the home screen menu.
|
||||||
|
*/
|
||||||
class HomeScreenRobot {
|
class HomeScreenRobot {
|
||||||
fun verifyHomeScreen() = homeScreen()
|
fun verifyHomeScreen() = assertHomeScreen()
|
||||||
fun verifyHomePrivateBrowsingButton() = homePrivateBrowsingButton()
|
fun verifyHomePrivateBrowsingButton() = assertHomePrivateBrowsingButton()
|
||||||
fun verifyHomeMenu() = homeMenu()
|
fun verifyHomeMenu() = assertHomeMenu()
|
||||||
fun verifyHomeWordmark() = homeWordmark()
|
fun verifyHomeWordmark() = assertHomeWordmark()
|
||||||
fun verifyHomeToolbar() = homeToolbar()
|
fun verifyHomeToolbar() = assertHomeToolbar()
|
||||||
fun verifyHomeComponent() = homeComponent()
|
fun verifyHomeComponent() = assertHomeComponent()
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
|
||||||
|
fun openThreeDotMenu(interact: ThreeDotMenuRobot.() -> Unit): ThreeDotMenuRobot.Transition {
|
||||||
|
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
threeDotButton().perform(click())
|
||||||
|
|
||||||
|
ThreeDotMenuRobot().interact()
|
||||||
|
return ThreeDotMenuRobot.Transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun homeScreen(interact: HomeScreenRobot.() -> Unit) {
|
fun homeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
||||||
HomeScreenRobot().interact()
|
HomeScreenRobot().interact()
|
||||||
|
return HomeScreenRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun homeScreen() = onView(ViewMatchers.withResourceName("homeLayout"))
|
private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
private fun homePrivateBrowsingButton() = onView(ViewMatchers.withResourceName("privateBrowsingButton"))
|
private fun assertHomeMenu() = onView(ViewMatchers.withResourceName("menuButton"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
private fun homeMenu() = onView(ViewMatchers.withResourceName("menuButton"))
|
private fun assertHomePrivateBrowsingButton() = onView(ViewMatchers.withResourceName("privateBrowsingButton"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
private fun homeWordmark() = onView(ViewMatchers.withResourceName("wordmark"))
|
private fun assertHomeWordmark() = onView(ViewMatchers.withResourceName("wordmark"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
private fun homeToolbar() = onView(ViewMatchers.withResourceName("toolbar"))
|
private fun assertHomeToolbar() = onView(ViewMatchers.withResourceName("toolbar"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
private fun homeComponent() = onView(ViewMatchers.withResourceName("home_component"))
|
private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("home_component"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
|
private fun threeDotButton() = onView(allOf(ViewMatchers.withId(R.id.menuButton)))
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@file:Suppress("TooManyFunctions")
|
||||||
|
|
||||||
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Robot Pattern for the your library menu.
|
||||||
|
*/
|
||||||
|
class LibraryRobot {
|
||||||
|
fun verifyLibraryView() = assertLibraryView()
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
|
||||||
|
fun goBack(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
||||||
|
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
goBackButton().perform(click())
|
||||||
|
|
||||||
|
HomeScreenRobot().interact()
|
||||||
|
return HomeScreenRobot.Transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertLibraryView() {
|
||||||
|
// verify that we are in the correct library view
|
||||||
|
onView(allOf(withId(R.id.libraryItemTitle)))
|
||||||
|
onView(allOf(withText("Bookmarks")))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@file:Suppress("TooManyFunctions")
|
||||||
|
|
||||||
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||||
|
import org.hamcrest.CoreMatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Robot Pattern for the settings menu.
|
||||||
|
*/
|
||||||
|
class SettingsRobot {
|
||||||
|
|
||||||
|
fun verifySettingsView() = assertSettingsView()
|
||||||
|
class Transition {
|
||||||
|
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
|
||||||
|
fun goBack(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
||||||
|
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
goBackButton().perform(ViewActions.click())
|
||||||
|
|
||||||
|
HomeScreenRobot().interact()
|
||||||
|
return HomeScreenRobot.Transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertSettingsView() {
|
||||||
|
// verify that we are in the correct library view
|
||||||
|
assertBasicsHeading()
|
||||||
|
assertAdvancedHeading()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertBasicsHeading() = onView(ViewMatchers.withText("Basics"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
private fun assertAdvancedHeading() = onView(ViewMatchers.withText("Advanced"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
|
private fun goBackButton() = onView(CoreMatchers.allOf(withContentDescription("Navigate up")))
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@file:Suppress("TooManyFunctions")
|
||||||
|
|
||||||
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import org.hamcrest.Matchers.allOf
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.helpers.click
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Robot Pattern for the three dot (main) menu.
|
||||||
|
*/
|
||||||
|
class ThreeDotMenuRobot {
|
||||||
|
fun verifySettingsButton() = assertSettingsButton()
|
||||||
|
fun verifyLibraryButton() = assertLibraryButton()
|
||||||
|
fun verifyHelpButton() = assertHelpButton()
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
|
||||||
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
|
||||||
|
fun openSettings(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
settingsButton().click()
|
||||||
|
|
||||||
|
SettingsRobot().interact()
|
||||||
|
return SettingsRobot.Transition()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openLibrary(interact: LibraryRobot.() -> Unit): LibraryRobot.Transition {
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
libraryButton().click()
|
||||||
|
|
||||||
|
LibraryRobot().interact()
|
||||||
|
return LibraryRobot.Transition()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openHelp(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
helpButton().click()
|
||||||
|
|
||||||
|
BrowserRobot().interact()
|
||||||
|
return BrowserRobot.Transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun settingsButton() = onView(allOf(withText("Settings")))
|
||||||
|
private fun assertSettingsButton() = settingsButton()
|
||||||
|
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||||
|
|
||||||
|
private fun libraryButton() = onView(allOf(withText(R.string.browser_menu_your_library)))
|
||||||
|
private fun assertLibraryButton() = libraryButton()
|
||||||
|
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||||
|
|
||||||
|
private fun helpButton() = onView(allOf(withText(R.string.browser_menu_help)))
|
||||||
|
private fun assertHelpButton() = helpButton()
|
||||||
|
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
Loading…
Reference in New Issue