diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/Matchers.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/Matchers.kt new file mode 100644 index 000000000..5368e8d4c --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/Matchers.kt @@ -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 = maybeInvertMatcher(espressoIsEnabled(), isEnabled) + +/** + * The [espressoIsChecked] function that can also handle unchecked state through the boolean argument. + */ +fun isChecked(isChecked: Boolean): Matcher = maybeInvertMatcher(espressoIsChecked(), isChecked) + +/** + * The [espressoIsSelected] function that can also handle not selected state through the boolean argument. + */ +fun isSelected(isSelected: Boolean): Matcher = maybeInvertMatcher(espressoIsSelected(), isSelected) + +private fun maybeInvertMatcher(matcher: Matcher, useUnmodifiedMatcher: Boolean): Matcher = when { + useUnmodifiedMatcher -> matcher + else -> not(matcher) +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/ViewInteraction.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/ViewInteraction.kt new file mode 100644 index 000000000..b9db09365 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/ViewInteraction.kt @@ -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)))!! +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuTest.kt new file mode 100644 index 000000000..98e035bfb --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuTest.kt @@ -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() + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt new file mode 100644 index 000000000..c9714bf5b --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -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()) + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt index 6256104d7..e867a23b8 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt @@ -7,33 +7,58 @@ package org.mozilla.fenix.ui.robots 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 -import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility 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 { - fun verifyHomeScreen() = homeScreen() - fun verifyHomePrivateBrowsingButton() = homePrivateBrowsingButton() - fun verifyHomeMenu() = homeMenu() - fun verifyHomeWordmark() = homeWordmark() - fun verifyHomeToolbar() = homeToolbar() - fun verifyHomeComponent() = homeComponent() + fun verifyHomeScreen() = assertHomeScreen() + fun verifyHomePrivateBrowsingButton() = assertHomePrivateBrowsingButton() + fun verifyHomeMenu() = assertHomeMenu() + fun verifyHomeWordmark() = assertHomeWordmark() + fun verifyHomeToolbar() = assertHomeToolbar() + 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() + return HomeScreenRobot.Transition() } -private fun homeScreen() = onView(ViewMatchers.withResourceName("homeLayout")) +private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun homePrivateBrowsingButton() = onView(ViewMatchers.withResourceName("privateBrowsingButton")) +private fun assertHomeMenu() = onView(ViewMatchers.withResourceName("menuButton")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun homeMenu() = onView(ViewMatchers.withResourceName("menuButton")) +private fun assertHomePrivateBrowsingButton() = onView(ViewMatchers.withResourceName("privateBrowsingButton")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun homeWordmark() = onView(ViewMatchers.withResourceName("wordmark")) +private fun assertHomeWordmark() = onView(ViewMatchers.withResourceName("wordmark")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun homeToolbar() = onView(ViewMatchers.withResourceName("toolbar")) +private fun assertHomeToolbar() = onView(ViewMatchers.withResourceName("toolbar")) .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))) + +private fun threeDotButton() = onView(allOf(ViewMatchers.withId(R.id.menuButton))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibraryRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibraryRobot.kt new file mode 100644 index 000000000..d0d72c2f5 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibraryRobot.kt @@ -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"))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt new file mode 100644 index 000000000..8dc009b55 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt @@ -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"))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuRobot.kt new file mode 100644 index 000000000..1a79d83b1 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuRobot.kt @@ -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)))