[fenix] Created UI tests for Addon Settings (https://github.com/mozilla-mobile/fenix/pull/9258)
parent
76a7b37440
commit
a8f29e65c0
@ -0,0 +1,26 @@
|
||||
package org.mozilla.fenix.helpers
|
||||
|
||||
import androidx.test.espresso.IdlingRegistry
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.helpers.idlingresource.AddonsInstallingIdlingResource
|
||||
|
||||
object IdlingResourceHelper {
|
||||
|
||||
// Idling Resource to manage installing an addon
|
||||
fun registerAddonInstallingIdlingResource(activityTestRule: ActivityTestRule<HomeActivity>) {
|
||||
IdlingRegistry.getInstance().register(
|
||||
AddonsInstallingIdlingResource(
|
||||
activityTestRule.activity.supportFragmentManager
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun unregisterAddonInstallingIdlingResource(activityTestRule: ActivityTestRule<HomeActivity>) {
|
||||
IdlingRegistry.getInstance().unregister(
|
||||
AddonsInstallingIdlingResource(
|
||||
activityTestRule.activity.supportFragmentManager
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package org.mozilla.fenix.helpers.idlingresource
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.test.espresso.IdlingResource
|
||||
import mozilla.components.feature.addons.ui.AddonInstallationDialogFragment
|
||||
|
||||
class AddonsInstallingIdlingResource(
|
||||
val fragmentManager: FragmentManager
|
||||
) :
|
||||
IdlingResource {
|
||||
private var resourceCallback: IdlingResource.ResourceCallback? = null
|
||||
private var isAddonInstalled = false
|
||||
|
||||
override fun getName(): String {
|
||||
return this::javaClass.name
|
||||
}
|
||||
|
||||
override fun isIdleNow(): Boolean {
|
||||
return isInstalledAddonDialogShown()
|
||||
}
|
||||
|
||||
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
|
||||
if (callback != null)
|
||||
resourceCallback = callback
|
||||
}
|
||||
|
||||
private fun isInstalledAddonDialogShown(): Boolean {
|
||||
val activityChildFragments =
|
||||
(fragmentManager.fragments.first() as NavHostFragment)
|
||||
.childFragmentManager.fragments
|
||||
|
||||
for (childFragment in activityChildFragments.indices) {
|
||||
if (activityChildFragments[childFragment] is AddonInstallationDialogFragment) {
|
||||
resourceCallback?.onTransitionToIdle()
|
||||
isAddonInstalled = true
|
||||
return isAddonInstalled
|
||||
}
|
||||
}
|
||||
return isAddonInstalled
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package org.mozilla.fenix.helpers.idlingresource
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.test.espresso.IdlingResource
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.addons.AddonsManagementFragment
|
||||
|
||||
class AddonsLoadingIdlingResource(val fragmentManager: FragmentManager) : IdlingResource {
|
||||
private var resourceCallback: IdlingResource.ResourceCallback? = null
|
||||
|
||||
override fun getName(): String {
|
||||
return this::javaClass.name
|
||||
}
|
||||
|
||||
override fun isIdleNow(): Boolean {
|
||||
val idle = addonsFinishedLoading()
|
||||
if (idle)
|
||||
resourceCallback?.onTransitionToIdle()
|
||||
return idle
|
||||
}
|
||||
|
||||
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
|
||||
if (callback != null)
|
||||
resourceCallback = callback
|
||||
}
|
||||
|
||||
private fun addonsFinishedLoading(): Boolean {
|
||||
val progressbar = fragmentManager.findFragmentById(R.id.container)?.let {
|
||||
val addonsManagementFragment =
|
||||
it.childFragmentManager.fragments.first { it is AddonsManagementFragment }
|
||||
addonsManagementFragment.view?.findViewById<View>(R.id.add_ons_progress_bar)
|
||||
} ?: return true
|
||||
|
||||
if (progressbar.visibility == VISIBLE)
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
|
||||
/* 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/. */
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Rule
|
||||
import org.junit.Before
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying the functionality of installing or removing addons
|
||||
*
|
||||
*/
|
||||
|
||||
class SettingsAddonsTest {
|
||||
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityTestRule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
setDispatcher(AndroidAssetDispatcher())
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
// Walks through settings add-ons menu to ensure all items are present
|
||||
@Test
|
||||
fun settingsAddonsItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
verifyAdvancedHeading()
|
||||
verifyAddons()
|
||||
}.openAddonsManagerMenu {
|
||||
verifyAddonsItems()
|
||||
}
|
||||
}
|
||||
|
||||
// Opens a webpage and installs an add-on from the three-dot menu
|
||||
@Test
|
||||
fun installAddonFromThreeDotMenu() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val addonName = "uBlock Origin"
|
||||
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openAddonsManagerMenu {
|
||||
clickInstallAddon(addonName)
|
||||
verifyAddonPrompt(addonName)
|
||||
cancelInstallAddon()
|
||||
clickInstallAddon(addonName)
|
||||
acceptInstallAddon()
|
||||
verifyDownloadAddonPrompt(addonName, activityTestRule)
|
||||
}
|
||||
}
|
||||
|
||||
// Opens the addons settings menu, installs an addon, then uninstalls
|
||||
@Test
|
||||
fun verifyAddonsCanBeUninstalled() {
|
||||
val addonName = "uBlock Origin"
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
verifyAdvancedHeading()
|
||||
verifyAddons()
|
||||
}.openAddonsManagerMenu {
|
||||
clickInstallAddon(addonName)
|
||||
acceptInstallAddon()
|
||||
verifyDownloadAddonPrompt(addonName, activityTestRule)
|
||||
}.openDetailedMenuForAddon(addonName) {
|
||||
verifyCurrentAddonMenu()
|
||||
}.removeAddon {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
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.isCompletelyDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.click
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for a single Addon inside of the Addons Management Settings.
|
||||
*/
|
||||
|
||||
class SettingsSubMenuAddonsManagerAddonDetailedMenuRobot {
|
||||
|
||||
fun verifyCurrentAddonMenu() = assertAddonMenuItems()
|
||||
|
||||
class Transition {
|
||||
fun goBack(interact: SettingsSubMenuAddonsManagerRobot.() -> Unit): SettingsSubMenuAddonsManagerRobot.Transition {
|
||||
fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
|
||||
goBackButton().click()
|
||||
|
||||
SettingsSubMenuAddonsManagerRobot().interact()
|
||||
return SettingsSubMenuAddonsManagerRobot.Transition()
|
||||
}
|
||||
|
||||
fun removeAddon(interact: SettingsSubMenuAddonsManagerRobot.() -> Unit): SettingsSubMenuAddonsManagerRobot.Transition {
|
||||
removeAddonButton().click()
|
||||
|
||||
SettingsSubMenuAddonsManagerRobot().interact()
|
||||
return SettingsSubMenuAddonsManagerRobot.Transition()
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertAddonMenuItems() {
|
||||
enableSwitchButton().check(matches(isCompletelyDisplayed()))
|
||||
settingsButton().check(matches(isCompletelyDisplayed()))
|
||||
detailsButton().check(matches(isCompletelyDisplayed()))
|
||||
permissionsButton().check(matches(isCompletelyDisplayed()))
|
||||
removeAddonButton().check(matches(isCompletelyDisplayed()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableSwitchButton() =
|
||||
onView(withId(R.id.enable_switch))
|
||||
|
||||
private fun settingsButton() =
|
||||
onView(withId(R.id.settings))
|
||||
|
||||
private fun detailsButton() =
|
||||
onView(withId(R.id.details))
|
||||
|
||||
private fun permissionsButton() =
|
||||
onView(withId(R.id.permissions))
|
||||
|
||||
private fun removeAddonButton() =
|
||||
onView(withId(R.id.remove_add_on))
|
@ -0,0 +1,228 @@
|
||||
package org.mozilla.fenix.ui.robots
|
||||
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
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.withParent
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.hamcrest.CoreMatchers.containsString
|
||||
import org.hamcrest.CoreMatchers.instanceOf
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.IdlingResourceHelper.registerAddonInstallingIdlingResource
|
||||
import org.mozilla.fenix.helpers.IdlingResourceHelper.unregisterAddonInstallingIdlingResource
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.click
|
||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for the Addons Management Settings.
|
||||
*/
|
||||
|
||||
class SettingsSubMenuAddonsManagerRobot {
|
||||
fun verifyAddonPrompt(addonName: String) = assertAddonPrompt(addonName)
|
||||
fun clickInstallAddon(addonName: String) = selectInstallAddon(addonName)
|
||||
fun verifyDownloadAddonPrompt(addonName: String, activityTestRule: HomeActivityTestRule) =
|
||||
assertDownloadingAddonPrompt(addonName, activityTestRule)
|
||||
|
||||
fun cancelInstallAddon() = cancelInstall()
|
||||
fun acceptInstallAddon() = allowInstall()
|
||||
fun verifyAddonsItems() = assertAddonsItems()
|
||||
fun verifyAddonCanBeInstalled(addonName: String) = assertAddonCanBeInstalled(addonName)
|
||||
|
||||
class Transition {
|
||||
fun goBack(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
||||
fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
|
||||
goBackButton().click()
|
||||
|
||||
HomeScreenRobot().interact()
|
||||
return HomeScreenRobot.Transition()
|
||||
}
|
||||
|
||||
fun openDetailedMenuForAddon(
|
||||
addonName: String,
|
||||
interact: SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.() -> Unit
|
||||
): SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.Transition {
|
||||
addonName.chars()
|
||||
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.add_on_item),
|
||||
hasDescendant(
|
||||
allOf(
|
||||
withId(R.id.add_on_name),
|
||||
withText(addonName)
|
||||
)
|
||||
)
|
||||
)
|
||||
).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
.perform(click())
|
||||
|
||||
SettingsSubMenuAddonsManagerAddonDetailedMenuRobot().interact()
|
||||
return SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.Transition()
|
||||
}
|
||||
}
|
||||
|
||||
private fun installButtonForAddon(addonName: String) =
|
||||
onView(
|
||||
allOf(
|
||||
withContentDescription(R.string.mozac_feature_addons_install_addon_content_description),
|
||||
isDescendantOfA(withId(R.id.add_on_item)),
|
||||
hasSibling(hasDescendant(withText(addonName)))
|
||||
)
|
||||
)
|
||||
|
||||
private fun selectInstallAddon(addonName: String) {
|
||||
mDevice.waitNotNull(
|
||||
Until.findObject(By.textContains(addonName)),
|
||||
waitingTime
|
||||
)
|
||||
|
||||
installButtonForAddon(addonName)
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
.perform(click())
|
||||
}
|
||||
|
||||
private fun assertAddonIsEnabled(addonName: String) {
|
||||
installButtonForAddon(addonName)
|
||||
.check(matches(not(isCompletelyDisplayed())))
|
||||
}
|
||||
|
||||
private fun assertAddonPrompt(addonName: String) {
|
||||
onView(allOf(withId(R.id.title), withText("Add $addonName?")))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.permissions),
|
||||
withText(containsString("It requires your permission to:"))
|
||||
)
|
||||
)
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
|
||||
onView(allOf(withId(R.id.allow_button), withText("Add")))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
|
||||
onView(allOf(withId(R.id.deny_button), withText("Cancel")))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
}
|
||||
|
||||
private fun assertDownloadingAddonPrompt(
|
||||
addonName: String,
|
||||
activityTestRule: HomeActivityTestRule
|
||||
) {
|
||||
registerAddonInstallingIdlingResource(activityTestRule)
|
||||
|
||||
onView(
|
||||
allOf(
|
||||
withText("Okay, Got it"),
|
||||
withParent(instanceOf(RelativeLayout::class.java)),
|
||||
hasSibling(withText("$addonName has been added to Firefox Preview")),
|
||||
hasSibling(withText("Open it in the menu")),
|
||||
hasSibling(withText("Allow in private browsing"))
|
||||
)
|
||||
)
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
.perform(click())
|
||||
|
||||
unregisterAddonInstallingIdlingResource(activityTestRule)
|
||||
|
||||
TestHelper.scrollToElementByText(addonName)
|
||||
|
||||
assertAddonIsInstalled(addonName)
|
||||
}
|
||||
|
||||
private fun assertAddonIsInstalled(addonName: String) {
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.add_button),
|
||||
isDescendantOfA(withId(R.id.add_on_item)),
|
||||
hasSibling(hasDescendant(withText(addonName)))
|
||||
)
|
||||
).check(matches(withEffectiveVisibility(Visibility.GONE)))
|
||||
}
|
||||
|
||||
private fun cancelInstall() {
|
||||
onView(allOf(withId(R.id.deny_button), withText("Cancel")))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
.perform(click())
|
||||
}
|
||||
|
||||
private fun allowInstall() {
|
||||
onView(allOf(withId(R.id.allow_button), withText("Add")))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
.perform(click())
|
||||
}
|
||||
|
||||
private fun assertAddonsItems() {
|
||||
assertRecommendedTitleDisplayed()
|
||||
assertAddons()
|
||||
}
|
||||
|
||||
private fun assertRecommendedTitleDisplayed() {
|
||||
onView(allOf(withId(R.id.title), withText("Recommended")))
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
|
||||
private fun assertEnabledTitleDisplayed() {
|
||||
onView(withText("Enabled"))
|
||||
.check(matches(isCompletelyDisplayed()))
|
||||
}
|
||||
|
||||
private fun assertAddons() {
|
||||
assertAddonUblock()
|
||||
}
|
||||
|
||||
private fun assertAddonUblock() {
|
||||
onView(
|
||||
allOf(
|
||||
isAssignableFrom(RelativeLayout::class.java),
|
||||
withId(R.id.add_on_item),
|
||||
hasDescendant(allOf(withId(R.id.add_on_icon), isCompletelyDisplayed())),
|
||||
hasDescendant(
|
||||
allOf(
|
||||
withId(R.id.details_container),
|
||||
hasDescendant(withText("uBlock Origin")),
|
||||
hasDescendant(withText("Finally, an efficient wide-spectrum content blocker. Easy on CPU and memory.")),
|
||||
hasDescendant(withId(R.id.rating)),
|
||||
hasDescendant(withId(R.id.users_count))
|
||||
)
|
||||
),
|
||||
hasDescendant(withId(R.id.add_button))
|
||||
)
|
||||
).check(matches(isCompletelyDisplayed()))
|
||||
}
|
||||
|
||||
private fun assertAddonCanBeInstalled(addonName: String) {
|
||||
device.waitNotNull(Until.findObject(By.text(addonName)), waitingTime)
|
||||
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.add_button),
|
||||
hasSibling(
|
||||
hasDescendant(
|
||||
allOf(
|
||||
withId(R.id.add_on_name),
|
||||
withText(addonName)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue