1
0
Fork 0
blallo 2020-02-22 13:54:50 +01:00
commit 007979a8e2
Signed by: blallo
GPG Key ID: 0CBE577C9B72DC3F
462 changed files with 31257 additions and 3257 deletions

View File

@ -12,11 +12,11 @@ jobs:
when:
- {hour: 6, minute: 0}
- {hour: 18, minute: 0}
- name: fennec-nightly
- name: fennec-beta
job:
type: decision-task
treeherder-symbol: N-fennec
target-tasks-method: fennec-nightly
treeherder-symbol: fennec-beta
target-tasks-method: fennec-beta
when: [] # Force hook only
- name: raptor
job:

1
.gitignore vendored
View File

@ -84,6 +84,7 @@ gen-external-apklibs
.adjust_token
.sentry_token
.digital_asset_links_token
.mls_token
# Python Byte-compiled / optimized / DLL files

View File

@ -8,7 +8,7 @@ tasks:
- $let:
taskgraph:
branch: taskgraph
revision: bb532a33ad32213f3a396346b26abd28fb87a975
revision: 7a51e874018f13aa236f5dc64ae7bbcaf942fcf3
trustDomain: mobile
in:
$let:
@ -63,11 +63,8 @@ tasks:
$if: 'tasks_for == "github-release"'
then: '${event.release.target_commitish}'
else:
$if: 'tasks_for == "cron"'
$if: 'tasks_for in ["action", "cron"]'
then: '${push.branch}'
else:
$if: 'tasks_for == "action"'
then: ${parameters.head_ref}
head_sha:
$if: 'tasks_for == "github-push"'
then: '${event.after}'
@ -78,11 +75,9 @@ tasks:
$if: 'tasks_for == "github-release"'
then: '${event.release.tag_name}'
else:
$if: 'tasks_for == "cron"'
$if: 'tasks_for in ["action", "cron"]'
then: '${push.revision}'
else:
$if: 'tasks_for == "action"'
then: ${parameters.head_rev}
head_tag:
$if: 'tasks_for == "github-release"'

View File

@ -5,6 +5,7 @@ plugins {
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco'
apply from: "$project.rootDir/automation/gradle/versionCode.gradle"
apply plugin: 'androidx.navigation.safeargs.kotlin'
@ -150,6 +151,7 @@ android {
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests.includeAndroidResources = true
animationsDisabled = true
}
flavorDimensions "engine"
@ -355,6 +357,21 @@ android.applicationVariants.all { variant ->
buildConfigField 'String', 'DIGITAL_ASSET_LINKS_TOKEN', 'null'
println("X_X")
}
// -------------------------------------------------------------------------------------------------
// MLS: Read token from local file if it exists
// -------------------------------------------------------------------------------------------------
print("MLS token: ")
try {
def token = new File("${rootDir}/.mls_token").text.trim()
buildConfigField 'String', 'MLS_TOKEN', '"' + token + '"'
println "(Added from .mls_token file)"
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'MLS_TOKEN', '""'
println("X_X")
}
}
androidExtensions {
@ -374,9 +391,11 @@ dependencies {
implementation Deps.androidx_coordinatorlayout
implementation Deps.sentry
implementation Deps.leanplum
implementation Deps.osslicenses_library
implementation Deps.leanplum_core
implementation Deps.leanplum_fcm
implementation Deps.mozilla_concept_engine
implementation Deps.mozilla_concept_push
implementation Deps.mozilla_concept_storage
@ -394,6 +413,9 @@ dependencies {
implementation Deps.mozilla_browser_storage_sync
implementation Deps.mozilla_browser_toolbar
implementation Deps.mozilla_support_extensions
implementation Deps.mozilla_feature_addons
implementation Deps.mozilla_feature_accounts
implementation Deps.mozilla_feature_app_links
implementation Deps.mozilla_feature_awesomebar
@ -415,6 +437,7 @@ dependencies {
implementation Deps.mozilla_feature_readerview
implementation Deps.mozilla_feature_tab_collections
implementation Deps.mozilla_feature_top_sites
implementation Deps.mozilla_feature_share
implementation Deps.mozilla_feature_accounts_push
implementation Deps.mozilla_feature_webcompat
implementation Deps.mozilla_feature_webnotifications
@ -423,6 +446,8 @@ dependencies {
implementation Deps.mozilla_service_firefox_accounts
implementation Deps.mozilla_service_glean
implementation Deps.mozilla_service_experiments
implementation Deps.mozilla_service_location
implementation Deps.mozilla_support_base
implementation Deps.mozilla_support_ktx
@ -494,6 +519,8 @@ dependencies {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestImplementation Deps.androidx_junit
androidTestImplementation Deps.androidx_work_testing
androidTestImplementation Deps.mockwebserver
testImplementation Deps.mozilla_support_test
testImplementation Deps.androidx_junit

View File

@ -82,7 +82,7 @@ events:
A string containing the name of the item the user tapped. These items include:
Settings, Library, Help, Desktop Site toggle on/off, Find in Page, New Tab,
Private Tab, Share, Report Site Issue, Back/Forward button, Reload Button, Quit,
Reader Mode On, Reader Mode Off, Open In App, Add to Firefox Home
Reader Mode On, Reader Mode Off, Open In App, Add To Top Sites, Add-ons Manager
bugs:
- https://github.com/mozilla-mobile/fenix/issues/1024
data_reviews:
@ -98,10 +98,13 @@ events:
A counter of URIs visited by the user in the current session, including page reloads. This does not include background page requests and URIs from embedded pages or private browsing.
send_in_pings:
- baseline
- metrics
bugs:
- https://github.com/mozilla-mobile/fenix/issues/1301
- https://github.com/mozilla-mobile/fenix/issues/4456
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1785
- https://github.com/mozilla-mobile/fenix/pull/8314
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
@ -362,20 +365,6 @@ metrics:
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
total_uri_count:
type: string
lifetime: application
description: >
A counter of URIs visited by the user in the current session, including page reloads. This does not include background page requests and URIs from embedded pages or private browsing.
send_in_pings:
- metrics
bugs:
- https://github.com/mozilla-mobile/fenix/issues/4456
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6003
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
toolbar_position:
type: string
lifetime: application
@ -1703,3 +1692,167 @@ top_sites:
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
about_page:
support_tapped:
type: event
description: >
A user tapped on "Support" item from About page
bugs:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
privacy_notice_tapped:
type: event
description: >
A user tapped on "Privacy notice" item from About page
bugs:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
rights_tapped:
type: event
description: >
A user tapped on "Know your rights" item from About page
bugs:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
licensing_tapped:
type: event
description: >
A user tapped on "Licensing information" item from About page
bugs:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
libraries_tapped:
type: event
description: >
A user tapped on "Libraries that we use" item from About page
bugs:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
app_theme:
dark_theme_selected:
type: event
description: >
A user selected Dark Theme
extra_keys:
source:
description: "The source from where dark theme was selected. The source can be 'SETTINGS' or 'ONBOARDING'"
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7289
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/7968
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
pocket:
pocket_top_site_clicked:
type: event
description: >
A user clicked on the trending Pocket top site
bugs:
- https://github.com/mozilla-mobile/fenix/issues/8126
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8098
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
pocket_top_site_removed:
type: event
description: >
A user removed the trending Pocket top site
bugs:
- https://github.com/mozilla-mobile/fenix/issues/8126
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8098
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
installation:
campaign:
type: string
send_in_pings:
- installation
description: >
The name of the campaign that is responsible for this installation.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7295
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
network:
type: string
send_in_pings:
- installation
description: >
The name of the Network that sourced this installation.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7295
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
adgroup:
type: string
send_in_pings:
- installation
description: >
The name of the AdGroup that was used to source this installation.
bugs:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586480836
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
creative:
send_in_pings:
- installation
type: string
description: >
The identifier of the creative material that the user interacted with.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7295
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"
timestamp:
send_in_pings:
- installation
type: datetime
description: >
The date and time of the installation.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7295
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
notification_emails:
- fenix-core@mozilla.com
expires: "2020-09-01"

View File

@ -18,3 +18,14 @@ activation:
- https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209
notification_emails:
- fenix-core@mozilla.com
installation:
description: >
This ping is intended to capture the source of the installation
include_client_id: false
bugs:
- https://github.com/mozilla-mobile/fenix/issues/7295
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202
notification_emails:
- fenix-core@mozilla.com

View File

@ -1,16 +1,36 @@
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt</title>
<meta content="width=device-width, initial-scale=1"
name="viewport"/>
</head>
<body>
<h1>
Lorem ipsum dolor sit amet, consectetur adipiscing elit,<br>
sed do eiusmod tempor incididunt ut labore et dolore magna <br>
aliqua. Ut enim ad minim veniam, quis nostrud exercitation <br>
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis <br>
aute irure dolor in reprehenderit in voluptate velit esse cillum <br>
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat <br>
non proident, sunt in culpa qui officia deserunt mollit anim id est <br>
laborum.
</h1>
<p id="testContent">Page content: lorem ipsum</p>
<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor
sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor
sit amet.
</p>
</body>
</html>

View File

@ -0,0 +1,22 @@
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body aria-label="body">
<form method="GET" action="passwordsubmit.html">
<p>Username: <input id="username" type="text" value="test@example.com"></p>
<p>Password: <input id="password" type="password" value="verysecret"></p>
<p><input type="submit" id="submit" value="Login" aria-label="submit"/></p>
</form>
</body>
<script>
document.getElementById("password").value = Math.random().toString();
</script>
</html>

View File

@ -0,0 +1,9 @@
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body aria-label="body">
<p>Password submitted. Nope just a test.</p>
</body>
</html>

View File

@ -0,0 +1,107 @@
/* 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.glean
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import androidx.test.uiautomator.UiDevice
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import mozilla.components.service.glean.Glean
import mozilla.components.service.glean.config.Configuration
import mozilla.components.service.glean.testing.GleanTestLocalServer
import org.json.JSONObject
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.BeforeClass
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.MockWebServerHelper
import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4::class)
class BaselinePingTest {
private val server = MockWebServerHelper.createAlwaysOkMockWebServer()
@get:Rule
val activityRule: ActivityTestRule<HomeActivity> = HomeActivityTestRule()
@get:Rule
val gleanRule = GleanTestLocalServer(ApplicationProvider.getApplicationContext(), server.port)
companion object {
@BeforeClass
@JvmStatic
fun setupOnce() {
// Fenix does not initialize the Glean SDK in tests/debug builds, but this test
// requires Glean to be initialized so we need to do it manually. Additionally,
// we need to do this on the main thread, as the Glean SDK requires it.
GlobalScope.launch(Dispatchers.Main.immediate) {
Glean.initialize(
ApplicationProvider.getApplicationContext(),
true,
Configuration()
)
}
}
}
private fun waitForPingContent(
pingName: String,
maxAttempts: Int = 3
): JSONObject? {
var attempts = 0
do {
attempts += 1
val request = server.takeRequest(20L, TimeUnit.SECONDS)
val docType = request.path.split("/")[3]
if (pingName == docType) {
return JSONObject(request.body.readUtf8())
}
} while (attempts < maxAttempts)
return null
}
@Test
fun validateBaselinePing() {
// Wait for the app to be idle/ready.
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.waitForIdle()
// Wait for 1 second: this should guarantee we have some valid duration in the
// ping.
Thread.sleep(1000)
// Move it to background.
device.pressHome()
// Validate the received data.
val baselinePing = waitForPingContent("baseline")!!
assertEquals("baseline", baselinePing.getJSONObject("ping_info")["ping_type"])
val metrics = baselinePing.getJSONObject("metrics")
// Make sure we have a 'duration' field with a reasonable value: it should be >= 1, since
// we slept for 1000ms.
val timespans = metrics.getJSONObject("timespan")
assertTrue(timespans.getJSONObject("glean.baseline.duration").getLong("value") >= 1L)
// Make sure there's no errors.
val errors = metrics.optJSONObject("labeled_counter")?.keys()
errors?.forEach {
assertFalse(it.startsWith("glean.error."))
}
}
}

View File

@ -4,9 +4,14 @@
package org.mozilla.fenix.helpers
import android.graphics.Bitmap
import android.view.View
import android.view.ViewGroup
import org.hamcrest.CoreMatchers.not
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
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 +35,24 @@ private fun maybeInvertMatcher(matcher: Matcher<View>, useUnmodifiedMatcher: Boo
useUnmodifiedMatcher -> matcher
else -> not(matcher)
}
fun withBitmapDrawable(bitmap: Bitmap, name: String): Matcher<View>? = BitmapDrawableMatcher(bitmap, name)
fun nthChildOf(
parentMatcher: Matcher<View>,
childPosition: Int
): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("Position is $childPosition")
}
public override fun matchesSafely(view: View): Boolean {
if (view.parent !is ViewGroup) {
return parentMatcher.matches(view.parent)
}
val group = view.parent as ViewGroup
return parentMatcher.matches(view.parent) && group.getChildAt(childPosition) == view
}
}
}

View File

@ -32,6 +32,20 @@ object MockWebServerHelper {
}
return uris
}
/**
* Create a mock webserver that accepts all requests and replies with "OK".
* @return a [MockWebServer] instance
*/
fun createAlwaysOkMockWebServer(): MockWebServer {
return MockWebServer().apply {
setDispatcher(object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
return MockResponse().setBody("OK")
}
})
}
}
}
/**

View File

@ -83,4 +83,10 @@ object TestAssetHelper {
return TestAsset(url, "")
}
fun getSaveLoginAsset(server: MockWebServer): TestAsset {
val url = server.url("pages/password.html").toString().toUri()!!
return TestAsset(url, "")
}
}

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

@ -0,0 +1,29 @@
package org.mozilla.fenix.helpers.matchers
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.matcher.BoundedMatcher
import org.hamcrest.Description
import org.hamcrest.Matcher
fun hasItem(matcher: Matcher<View?>): Matcher<View?>? {
return object : BoundedMatcher<View?, RecyclerView>(RecyclerView::class.java) {
override fun describeTo(description: Description) {
description.appendText("has item: ")
matcher.describeTo(description)
}
override fun matchesSafely(view: RecyclerView): Boolean {
val adapter = view.adapter
for (position in 0 until adapter!!.itemCount) {
val type = adapter.getItemViewType(position)
val holder = adapter.createViewHolder(view, type)
adapter.onBindViewHolder(holder, position)
if (matcher.matches(holder.itemView)) {
return true
}
}
return false
}
}
}

View File

@ -206,7 +206,7 @@ fun settingsAccountPreferences() = onView(withText(R.string.preferences_sync)).c
fun settingsSearch() = onView(withText(R.string.preferences_search)).click()
fun settingsTheme() = onView(withText(R.string.preferences_theme)).click()
fun settingsTheme() = onView(withText(R.string.preferences_customize)).click()
fun settingsAccessibility() = onView(withText(R.string.preferences_accessibility)).click()

View File

@ -9,35 +9,54 @@ import android.widget.EditText
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.pressImeActionButton
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.accountSettings
import org.mozilla.fenix.ui.robots.settingsSubMenuLoginsAndPassword
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import okhttp3.mockwebserver.MockWebServer
import org.hamcrest.Matchers.allOf
import org.junit.After
import org.junit.Before
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.ext.toUri
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.accountSettings
import org.mozilla.fenix.ui.robots.settingsSubMenuLoginsAndPassword
import org.mozilla.fenix.ui.robots.navigationToolbar
import org.mozilla.fenix.ui.robots.browserScreen
@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class SyncIntegrationTest {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
@get:Rule
val activityTestRule = HomeActivityTestRule()
@Before
fun setUp() {
mockWebServer = MockWebServer().apply {
setDispatcher(AndroidAssetDispatcher())
start()
}
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
// History item Desktop -> Fenix
@Test
fun checkHistoryFromDesktopTest() {
@ -82,6 +101,7 @@ class SyncIntegrationTest {
}
}
// Login item Desktop -> Fenix
@Test
fun checkLoginsFromDesktopTest() {
homeScreen {
@ -113,30 +133,46 @@ class SyncIntegrationTest {
}
}
/* These tests will be running in the future
// once the test above runs successfully and
// the environment is stable
// Bookmark item Fenix -> Desktop
@Test
fun checkBookmarkFromDeviceTest() {
val defaultWebPage = "example.com".toUri()!!
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage) {
}.openThreeDotMenu {
verifyAddBookmarkButton()
clickAddBookmarkButton()
}
browserScreen {
}.openThreeDotMenu {
}.openSettings {
}.openTurnOnSyncMenu {
useEmailInsteadButton()
typeEmail()
tapOnContinueButton()
typePassword()
sleep(TestAssetHelper.waitingTimeShort)
tapOnSignIn()
}
}
// History item Fenix -> Desktop
@Test
fun checkBookmarkFromDeviceTest() {
tapInToolBar()
typeInToolBar()
seeBookmark()
mDevice.pressBack()
signInFxSync()
}
// Bookmark item Fenix -> Desktop
@Test
fun checkHistoryFromDeviceTest() {
tapInToolBar()
typeInToolBar()
sleep(TestAssetHelper.waitingTime)
mDevice.pressBack()
signInFxSync()
val defaultWebPage = "example.com".toUri()!!
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage) {
}.openThreeDotMenu {
}.openSettings {
}.openTurnOnSyncMenu {
useEmailInsteadButton()
typeEmail()
tapOnContinueButton()
typePassword()
sleep(TestAssetHelper.waitingTimeShort)
tapOnSignIn()
}
}
*/
// Useful functions for the tests
fun typeEmail() {
@ -170,11 +206,6 @@ class SyncIntegrationTest {
mDevice.pressEnter()
}
fun typeInToolBar() {
awesomeBar().perform(replaceText("example.com"),
pressImeActionButton())
}
fun historyAfterSyncIsShown() {
val historyEntry = mDevice.findObject(By.text("http://www.example.com/"))
historyEntry.isEnabled()
@ -185,12 +216,6 @@ class SyncIntegrationTest {
bookmarkEntry.isEnabled()
}
fun seeBookmark() {
mDevice.waitNotNull(Until.findObjects(By.text("Bookmark")), TestAssetHelper.waitingTime)
val bookmarkButton = mDevice.findObject(By.text("Bookmark"))
bookmarkButton.click()
}
fun tapReturnToPreviousApp() {
mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)
mDevice.pressBack()
@ -218,7 +243,5 @@ class SyncIntegrationTest {
}
fun settingsAccount() = onView(allOf(withText("Turn on Sync"))).perform(click())
fun tapInToolBar() = onView(withId(R.id.toolbar_wrapper))
fun awesomeBar() = onView(withId(R.id.mozac_browser_toolbar_edit_url_view))
fun useEmailInsteadButton() = onView(withId(R.id.signInEmailButton)).perform(click())
fun enterAccountSettings() = onView(withId(R.id.email)).perform(click())

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in strict JSON format, as it will get parsed by the Python
* testrunner (no single quotes, extra comma's, etc).
*/
EnableEngines(["bookmarks"]);
var phases = { "phase1": "profile1" };
// expected bookmark state
var bookmarksExpected = {
"mobile": [{
uri: "http://www.example.com/",
title: "Example Domain"}]
};
// sync and verify bookmarks
Phase("phase1", [
[Sync],
[Bookmarks.verify, bookmarksExpected],
]);

View File

@ -0,0 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in strict JSON format, as it will get parsed by the Python
* testrunner (no single quotes, extra comma's, etc).
*/
EnableEngines(["history"]);
var phases = { "phase1": "profile1" };
// expected history state
var historyExpected = [
{ uri: "http://www.example.com/",
visits: [
{ type: 1 },
{ type: 2 }
]
}
];
// sync and verify history
Phase("phase1", [
[Sync],
[History.verify, historyExpected]
]);

View File

@ -10,12 +10,14 @@ def test_sync_history_from_desktop(tps, gradlewbuild):
tps.run('test_history.js')
gradlewbuild.test('checkHistoryFromDesktopTest')
def test_sync_bookmark_from_device(tps, gradlewbuild):
os.chdir('app/src/androidTest/java/org/mozilla/fenix/syncintegration/')
def test_sync_bookmark_from_desktop(tps, gradlewbuild):
tps.run('test_bookmark.js')
gradlewbuild.test('checkBookmarkFromDesktopTest')
def test_sync_logins_from_device(tps, gradlewbuild):
os.chdir('app/src/androidTest/java/org/mozilla/fenix/syncintegration/')
tps.run('test_logins.js')
gradlewbuild.test('checkLoginsFromDesktopTest')
def test_sync_bookmark_from_device(tps, gradlewbuild):
gradlewbuild.test('checkBookmarkFromDeviceTest')
tps.run('test_bookmark_desktop.js')

View File

@ -11,6 +11,7 @@ import mozilla.appservices.places.BookmarkRoot
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.mozilla.fenix.ext.bookmarkStorage
@ -103,6 +104,7 @@ class BookmarksTest {
verifyKeyboardVisible()
addNewFolderName(bookmarksFolderName)
saveNewFolder()
getInstrumentation().waitForIdleSync()
verifyFolderTitle(bookmarksFolderName)
verifyKeyboardHidden()
}
@ -268,6 +270,7 @@ class BookmarksTest {
}
}
@Ignore("Temp disable: Nexus 6 failures - issue: https://github.com/mozilla-mobile/fenix/issues/7417")
@Test
fun deleteMultipleSelectionTest() {
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
@ -314,6 +317,7 @@ class BookmarksTest {
}
}
@Ignore("Temp disable: Nexus 6 failures - issue: https://github.com/mozilla-mobile/fenix/issues/7417")
@Test
fun multipleBookmarkDeletions() {
homeScreen {

View File

@ -53,6 +53,7 @@ class ContextMenusTest {
}
@Test
@Ignore("Disabling because of intermittent failures https://github.com/mozilla-mobile/fenix/issues/8663")
fun verifyContextOpenLinkNewTab() {
val pageLinks =
TestAssetHelper.getGenericAsset(mockWebServer, 4)
@ -112,7 +113,7 @@ class ContextMenusTest {
clickContextCopyLink()
verifySnackBarText("Link copied to clipboard")
}.openNavigationToolbar {
}.visitLinkFromClipboard(genericURL.url) {
}.visitLinkFromClipboard {
verifyUrl(genericURL.url.toString())
}
}
@ -169,7 +170,7 @@ class ContextMenusTest {
clickContextCopyImageLocation()
verifySnackBarText("Link copied to clipboard")
}.openNavigationToolbar {
}.visitLinkFromClipboard(imageResource.url) {
}.visitLinkFromClipboard {
verifyUrl(imageResource.url.toString())
}
}

View File

@ -110,7 +110,6 @@ class HomeScreenTest {
verifyHomeWordmark()
verifyAddTabButton()
verifyShareTabsButton(visible = false)
verifyCloseTabsButton(visible = false)
verifyPrivateSessionHeader()
verifyPrivateSessionMessage(visible = true)
verifyHomeToolbar()
@ -130,7 +129,6 @@ class HomeScreenTest {
verifyHomeWordmark()
verifyAddTabButton()
verifyShareTabsButton(visible = true)
verifyCloseTabsButton(visible = true)
verifyPrivateSessionHeader()
verifyPrivateSessionMessage(visible = false)
verifyHomeToolbar()

View File

@ -0,0 +1,271 @@
/* 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 androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.ui.robots.navigationToolbar
import org.mozilla.fenix.ui.robots.readerViewRobot
/**
* Tests for verifying basic functionality of content context menus
*
* - Verifies Reader View entry and detection when available UI and functionality
* - Verifies Reader View exit UI and functionality
* - Verifies Reader View appearance controls UI and functionality
*
*/
class ReaderViewTest {
private lateinit var mockWebServer: MockWebServer
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@get:Rule
val activityIntentTestRule = HomeActivityIntentTestRule()
@Before
fun setUp() {
mockWebServer = MockWebServer().apply {
setDispatcher(AndroidAssetDispatcher())
start()
}
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
/**
* Verify that Reader View capable pages
*
* - Show blue notification in the three dot menu
* - Show the toggle button in the three dot menu
*
*/
@Test
fun verifyReaderViewPageMenuDetection() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.closeBrowserMenuToBrowser { }
}
/**
* Verify that non Reader View capable pages
*
* - Do not show a blue notification in the three dot menu
* - Reader View toggle should not be visible in the three dot menu
*
*/
@Test
fun verifyNonReaderViewPageMenuNoDetection() {
var genericPage =
TestAssetHelper.getGenericAsset(mockWebServer, 1)
navigationToolbar {
}.enterURLAndEnterToBrowser(genericPage.url) {
verifyPageContent(genericPage.content)
}
readerViewRobot {
verifyReaderViewDetected(false)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(false)
verifyReaderViewAppearance(false)
}.closeBrowserMenuToBrowser { }
}
@Test
fun verifyReaderViewToggle() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(false)
}.close { }
readerViewRobot {
verifyReaderViewDetected(true)
}
}
@Test
fun verifyReaderViewAppearanceUI() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(true)
}.openReaderViewAppearance {
verifyAppearanceFontGroup(true)
verifyAppearanceFontSansSerif(true)
verifyAppearanceFontSerif(true)
verifyAppearanceFontIncrease(true)
verifyAppearanceFontDecrease(true)
verifyAppearanceColorGroup(true)
verifyAppearanceColorDark(true)
verifyAppearanceColorLight(true)
verifyAppearanceColorSepia(true)
}
}
@Test
fun verifyReaderViewAppearanceFontToggle() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(true)
}.openReaderViewAppearance {
verifyAppearanceFontGroup(true)
verifyAppearanceFontSansSerif(true)
verifyAppearanceFontSerif(true)
verifyAppearanceFontIncrease(true)
verifyAppearanceFontDecrease(true)
}.toggleSansSerif {
verifyAppearanceFontIsActive("SANSSERIF")
}.toggleSerif {
verifyAppearanceFontIsActive("SERIF")
}
}
@Test
fun verifyReaderViewAppearanceFontSizeToggle() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(true)
}.openReaderViewAppearance {
verifyAppearanceFontIncrease(true)
verifyAppearanceFontDecrease(true)
verifyAppearanceFontSize(3)
}.toggleFontSizeIncrease {
verifyAppearanceFontSize(4)
}.toggleFontSizeIncrease {
verifyAppearanceFontSize(5)
}.toggleFontSizeIncrease {
verifyAppearanceFontSize(6)
}.toggleFontSizeDecrease {
verifyAppearanceFontSize(5)
}.toggleFontSizeDecrease {
verifyAppearanceFontSize(4)
}.toggleFontSizeDecrease {
verifyAppearanceFontSize(3)
}
}
@Test
fun verifyReaderViewAppearanceColorSchemeChange() {
val readerViewPage =
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(readerViewPage.url) {
verifyPageContent(readerViewPage.content)
}
readerViewRobot {
verifyReaderViewDetected(true)
}
navigationToolbar {
}.openThreeDotMenu {
verifyReaderViewToggle(true)
}.toggleReaderView {
}.openThreeDotMenu {
verifyReaderViewAppearance(true)
}.openReaderViewAppearance {
verifyAppearanceColorDark(true)
verifyAppearanceColorLight(true)
verifyAppearanceColorSepia(true)
}.toggleColorSchemeChangeDark {
verifyAppearanceColorSchemeChange("DARK")
}.toggleColorSchemeChangeSepia {
verifyAppearanceColorSchemeChange("SEPIA")
}.toggleColorSchemeChangeLight {
verifyAppearanceColorSchemeChange("LIGHT")
}
}
}

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,15 +74,16 @@ class SettingsBasicsTest {
verifySearchEngineList()
verifyShowSearchSuggestions()
verifyShowSearchShortcuts()
verifyShowClipboardSuggestions()
verifySearchBrowsingHistory()
verifySearchBookmarks()
}.goBack {
}.openThemeSubMenu {
}.openCustomizeSubMenu {
verifyThemes()
}.goBack {
}.openAccessibilitySubMenu {
verifyAutomaticFontSizing()
verifyAutomaticFontSizingMenuItems()
}.goBack {
// drill down to submenu
}.openDefaultBrowserSubMenu {
@ -86,53 +93,79 @@ 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 {
}.openThemeSubMenu {
}.openCustomizeSubMenu {
verifyThemes()
selectDarkMode()
verifyDarkThemeApplied(getUiTheme())
@ -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

@ -14,8 +14,10 @@ import org.junit.Ignore
import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestHelper
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
/**
* Tests for verifying the main three dot menu options
@ -133,6 +135,65 @@ class SettingsPrivacyTest {
}
}
@Test
fun saveLoginFromPromptTest() {
val saveLoginTest =
TestAssetHelper.getSaveLoginAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
verifySaveLoginPromptIsShown()
// Click save to save the login
saveLoginFromPrompt("Save")
}.openHomeScreen {
}.openThreeDotMenu {
}.openSettings {
TestHelper.scrollToElementByText("Logins and passwords")
}.openLoginsAndPasswordSubMenu {
verifyDefaultView()
verifyDefaultValueSyncLogins()
}.openSavedLogins {
verifySavedLoginsView()
tapSetupLater()
// Verify that the login appears correctly
verifySavedLoginFromPrompt()
}
}
@Test
fun doNotSaveLoginFromPromptTest() {
val saveLoginTest = TestAssetHelper.getSaveLoginAsset(mockWebServer)
navigationToolbar {
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
verifySaveLoginPromptIsShown()
// Don't save the login
saveLoginFromPrompt("Dont save")
}.openHomeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openLoginsAndPasswordSubMenu {
verifyDefaultView()
verifyDefaultValueSyncLogins()
}.openSavedLogins {
verifySavedLoginsView()
tapSetupLater()
// Verify that the login list is empty
verifyNotSavedLoginFromPromt()
}
}
@Test
fun saveLoginsAndPasswordsOptions() {
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openLoginsAndPasswordSubMenu {
}.saveLoginsAndPasswordsOptions {
verifySaveLoginsOptionsView()
}
}
@Ignore("This is a stub test, ignore for now")
@Test
fun toggleTrackingProtection() {

View File

@ -28,8 +28,10 @@ import org.mozilla.fenix.ui.robots.navigationToolbar
* - Opening a private tab
* - Verifying tab list
* - Closing all tabs
* - Close tab
* - Swipe to close tab
* - Undo close tab
*
* TODO: Tab Collections
*/
class TabbedBrowsingTest {
@ -76,7 +78,10 @@ class TabbedBrowsingTest {
homeScreen {
// Timing issue on slow devices on Firebase
mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime)
mDevice.waitNotNull(
Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")),
TestAssetHelper.waitingTime
)
verifyExistingTabList()
}.openTabsListThreeDotMenu {
@ -106,10 +111,13 @@ class TabbedBrowsingTest {
verifyTabCounter("1")
}.openHomeScreen {
// Timing issue on slow devices on Firebase
mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime)
mDevice.waitNotNull(
Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")),
TestAssetHelper.waitingTime
)
verifyExistingTabList()
verifyShareTabsButton(true)
verifyCloseTabsButton(true)
verifyCloseTabsButton("Test_Page_1")
}.togglePrivateBrowsingMode()
// Verify private tabs remain in private browsing mode
@ -135,7 +143,10 @@ class TabbedBrowsingTest {
homeScreen {
// Timing issue on slow devices on Firebase
mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")), TestAssetHelper.waitingTime)
mDevice.waitNotNull(
Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")),
TestAssetHelper.waitingTime
)
verifyExistingTabList()
}.openTabsListThreeDotMenu {
verifyCloseAllTabsButton()
@ -147,5 +158,59 @@ class TabbedBrowsingTest {
verifyNoTabsOpenedHeader()
verifyNoTabsOpenedText()
}
// Repeat for Private Tabs
homeScreen {
}.togglePrivateBrowsingMode()
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
verifyPageContent(defaultWebPage.content)
}.openHomeScreen { }
homeScreen {
// Timing issue on slow devices on Firebase
mDevice.waitNotNull(
Until.findObjects(By.res("org.mozilla.fenix.debug:id/item_tab")),
TestAssetHelper.waitingTime
)
verifyExistingTabList()
verifyPrivateTabsCloseTabsButton()
}.closeAllPrivateTabs {
verifyPrivateSessionHeader()
verifyPrivateSessionMessage(true)
}
}
@Test
fun closeTabTest() {
var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer)
genericURLS.forEachIndexed { index, element ->
navigationToolbar {
}.openNewTabAndEnterToBrowser(element.url) {
verifyPageContent(element.content)
}.openHomeScreen { }
homeScreen {
verifyExistingOpenTabs("Test_Page_${index + 1}")
verifyCloseTabsButton("Test_Page_${index + 1}")
closeTabViaXButton("Test_Page_${index + 1}")
verifySnackBarText("Tab closed")
snackBarButtonClick("UNDO")
verifyExistingOpenTabs("Test_Page_${index + 1}")
verifyCloseTabsButton("Test_Page_${index + 1}")
swipeTabRight("Test_Page_${index + 1}")
verifySnackBarText("Tab closed")
snackBarButtonClick("UNDO")
verifyExistingOpenTabs("Test_Page_${index + 1}")
verifyCloseTabsButton("Test_Page_${index + 1}")
swipeTabLeft("Test_Page_${index + 1}")
verifySnackBarText("Tab closed")
snackBarButtonClick("UNDO")
verifyExistingOpenTabs("Test_Page_${index + 1}")
verifyCloseTabsButton("Test_Page_${index + 1}")
}
}
}
}

View File

@ -1,122 +0,0 @@
/* 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 androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import okhttp3.mockwebserver.MockWebServer
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.Before
import org.junit.After
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
/**
* Tests for verifying basic functionality of tabs
*
*/
class TabsTest {
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
@get:Rule
val activityTestRule = HomeActivityTestRule()
@Before
fun setUp() {
mockWebServer = MockWebServer().apply {
setDispatcher(AndroidAssetDispatcher())
start()
}
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
@Ignore("This is a stub test, ignore for now")
@Test
fun tabsItemsTest() {
homeScreen { }.dismissOnboarding()
// Setup browser so that tabs are visible in UI
// Verify all tabs elements are visible:
// "open tabs header, + button, etc.
// Verify tabs 3-dot menu elements
}
@Ignore("This is a stub test, ignore for now")
@Test
fun noTabsInCacheTest() {
// Verify open tabs header and text exists (when no previous browsing)
// Verify + button redirects to navigation bar UI
// Verify "Collections" header exists
// Verify "No collections" text (when no previous browsing)
}
@Ignore("This is a stub test, ignore for now")
@Test
fun browsingWithTabsTest() {
// Setup:
// - Verify + button redirects to navigation bar UI
// - Enter mock website via navigation bar
// Verify "Open tabs" header exits
// Verify Collections header exits
// Verify that tabs counter is augmented by 1 count
// Click on tabs counter
// Verify that new page is listed in "Open tabs"
// Repeat for several sites
}
@Ignore("This is a stub test, ignore for now")
@Test
fun tabsThreeDotMenuTest() {
// short 3-dot menu setup:
// - create multiple tabs (using mock web server) for the following...
// Verify tabs 3-dot menu functions:
// 1. "Close all tabs"
// 2. "Share tabs" - opens share sub-menu
// 3. "Save to collection" - verify saved to collection
// NOTE: extended 3 dot menu test is verified in a separate class
}
@Ignore("This is a stub test, ignore for now")
@Test
fun collectionsTest() {
// Setup:
// - create multiple tabs (using mock web server) for the following...
// Verify collections header exits
// Verify multiple collections can be saved, named
// Verify "Select tabs to save"
// Verify collections dropdown toggle
// Verify send and share button works - opens share menu
// Verify collections 3-dot menu functions:
// 1. Delete collection
// 2. Rename collection
// 3. Open tabs
}
@Ignore("This is a sample test, ignore")
@Test
fun sampleTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
}
}
}

View File

@ -0,0 +1,162 @@
/* 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 androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
/**
* Tests Top Sites functionality
*
* - Verifies 'Add to Firefox Home' UI functionality
* - Verifies 'Top Sites' context menu UI functionality
* - Verifies 'Top Site' usage UI functionality
* - Verifies existence of default top sites available on the home-screen
*/
class TopSitesTest {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
@get:Rule
val activityIntentTestRule = HomeActivityIntentTestRule()
@Before
fun setUp() {
mockWebServer = MockWebServer().apply {
setDispatcher(AndroidAssetDispatcher())
start()
}
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
@Test
fun verifyAddToFirefoxHome() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val defaultWebPageTitle = "Test_Page_1"
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
verifyPageContent(defaultWebPage.content)
}.openThreeDotMenu {
verifyAddFirefoxHome()
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openHomeScreen {
verifyExistingTabList()
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}
}
@Test
fun verifyOpenTopSiteNormalTab() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val defaultWebPageTitle = "Test_Page_1"
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
verifyPageContent(defaultWebPage.content)
}.openThreeDotMenu {
verifyAddFirefoxHome()
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openHomeScreen {
verifyExistingTabList()
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openTopSiteTabWithTitle(title = defaultWebPageTitle) {
verifyPageContent(defaultWebPage.content)
verifyUrl(defaultWebPage.url.toString())
}.openHomeScreen {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
verifyTopSiteContextMenuItems()
}
// Dismiss context menu popup
mDevice.pressBack()
}
@Test
fun verifyOpenTopSitePrivateTab() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val defaultWebPageTitle = "Test_Page_1"
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
verifyPageContent(defaultWebPage.content)
}.openThreeDotMenu {
verifyAddFirefoxHome()
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openHomeScreen {
verifyExistingTabList()
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
verifyTopSiteContextMenuItems()
}.openTopSiteInPrivateTab {
verifyCurrentPrivateSession(activityIntentTestRule.activity.applicationContext)
}
}
@Test
fun verifyRemoveTopSite() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val defaultWebPageTitle = "Test_Page_1"
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
verifyPageContent(defaultWebPage.content)
}.openThreeDotMenu {
verifyAddFirefoxHome()
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openHomeScreen {
verifyExistingTabList()
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
verifyTopSiteContextMenuItems()
}.removeTopSite {
verifyNotExistingTopSitesList(defaultWebPageTitle)
}
}
@Test
fun verifyDefaultTopSitesLocale_EN() {
// en-US defaults
val defaultTopSites = arrayOf(
"Top Articles",
"Wikipedia",
"YouTube"
)
homeScreen { }.dismissOnboarding()
homeScreen {
verifyExistingTopSitesList()
defaultTopSites.forEach { item ->
verifyExistingTopSitesTabs(item)
}
}
}
}

View File

@ -6,6 +6,7 @@
package org.mozilla.fenix.ui.robots
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.test.espresso.Espresso.onView
@ -15,6 +16,7 @@ import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.BundleMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
@ -25,11 +27,13 @@ import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.junit.Assert.assertTrue
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.helpers.Constants.LongClickDuration
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.helpers.Constants.LongClickDuration
class BrowserRobot {
@ -38,14 +42,19 @@ class BrowserRobot {
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
fun verifyUrl(redirectUrl: String) {
fun verifyCurrentPrivateSession(context: Context) {
val session = context.components.core.sessionManager.selectedSession
assertTrue("Current session is private", session?.private!!)
}
fun verifyUrl(url: String) {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view")),
TestAssetHelper.waitingTime
)
onView(withId(R.id.mozac_browser_toolbar_url_view))
.check(matches(withText(containsString(redirectUrl))))
.check(matches(withText(containsString(url))))
}
fun verifyHelpUrl() {
@ -76,6 +85,10 @@ class BrowserRobot {
fun verifySnackBarText(expectedText: String) {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime)
onView(withText(expectedText)).check(
matches(isCompletelyDisplayed())
)
}
fun verifyLinkContextMenuItems(containsURL: Uri) {
@ -256,6 +269,18 @@ class BrowserRobot {
).perform(ViewActions.click())
}
fun verifySaveLoginPromptIsShown() {
mDevice.waitNotNull(Until.findObjects(By.text("test@example.com")), TestAssetHelper.waitingTime)
val submitButton = mDevice.findObject(By.res("submit"))
submitButton.clickAndWait(Until.newWindow(), TestAssetHelper.waitingTime)
// Click save to save the login
mDevice.waitNotNull(Until.findObjects(By.text("Save")))
}
fun saveLoginFromPrompt(optionToSaveLogin: String) {
mDevice.findObject(By.text(optionToSaveLogin)).click()
}
class Transition {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private fun threeDotButton() = onView(
@ -312,4 +337,4 @@ fun dismissTrackingOnboarding() {
fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view))
private fun tabsCounter() = onView(withId(R.id.counter_box))
private fun tabsCounter() = onView(withId(R.id.mozac_browser_toolbar_browser_actions))

View File

@ -7,31 +7,39 @@
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
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withText
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.hasDescendant
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.Until
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector
import org.hamcrest.CoreMatchers
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.containsString
import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.click
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
import org.mozilla.fenix.helpers.matchers.hasItem
/**
* Implementation of Robot Pattern for the home screen menu.
@ -51,6 +59,7 @@ class HomeScreenRobot {
fun verifyHomeWordmark() = assertHomeWordmark()
fun verifyHomeToolbar() = assertHomeToolbar()
fun verifyHomeComponent() = assertHomeComponent()
fun verifyDefaultSearchEngine(searchEngine: String) = verifySearchEngineIcon(searchEngine)
// First Run elements
fun verifyWelcomeHeader() = assertWelcomeHeader()
@ -79,41 +88,55 @@ class HomeScreenRobot {
// Private mode elements
fun verifyPrivateSessionHeader() = assertPrivateSessionHeader()
fun verifyPrivateSessionMessage(visible: Boolean = true) = assertPrivateSessionMessage(visible)
fun verifyPrivateTabsCloseTabsButton() = assertPrivateTabsCloseTabsButton()
fun verifyShareTabsButton(visible: Boolean = true) = assertShareTabsButton(visible)
fun verifyCloseTabsButton(visible: Boolean = true) = assertCloseTabsButton(visible)
fun verifyCloseTabsButton(title: String) =
assertCloseTabsButton(title)
fun verifyExistingTabList() = assertExistingTabList()
fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title)
fun verifyExistingTopSitesList() = assertExistingTopSitesList()
fun verifyNotExistingTopSitesList(title: String) = assertNotExistingTopSitesList(title)
fun verifyExistingTopSitesTabs(title: String) = assertExistingTopSitesTabs(title)
fun verifyTopSiteContextMenuItems() = assertTopSiteContextMenuItems()
// Collections element
fun clickCollectionThreeDotButton() {
collectionThreeDotButton().click()
mDevice.waitNotNull(Until.findObject(By.text("Delete collection")), waitingTime)
}
fun selectRenameCollection() {
onView(allOf(ViewMatchers.withText("Rename collection"))).click()
mDevice.waitNotNull(Until.findObject(By.res("name_collection_edittext")))
}
fun selectDeleteCollection() {
onView(allOf(ViewMatchers.withText("Delete collection"))).click()
mDevice.waitNotNull(Until.findObject(By.res("message")), waitingTime)
}
fun confirmDeleteCollection() {
onView(allOf(ViewMatchers.withText("DELETE"))).click()
mDevice.waitNotNull(Until.findObject(By.res("collections_header")), waitingTime)
}
fun typeCollectionName(name: String) {
mDevice.wait(Until.findObject(By.res("name_collection_edittext")), waitingTime)
collectionNameTextField().perform(ViewActions.replaceText(name))
collectionNameTextField().perform(ViewActions.pressImeActionButton())
}
fun scrollToElementByText(text: String): UiScrollable {
val appView = UiScrollable(UiSelector().scrollable(true))
appView.scrollTextIntoView(text)
return appView
}
fun swipeUpToDismissFirstRun() {
scrollToElementByText("Start browsing")
}
@ -124,12 +147,31 @@ class HomeScreenRobot {
fun togglePrivateBrowsingModeOnOff() {
onView(ViewMatchers.withResourceName("privateBrowsingButton"))
.perform(click())
.perform(click())
}
fun swipeToBottom() = onView(ViewMatchers.withId(R.id.home_component)).perform(ViewActions.swipeUp())
fun swipeToBottom() = onView(withId(R.id.sessionControlRecyclerView)).perform(ViewActions.swipeUp())
fun swipeToTop() = onView(ViewMatchers.withId(R.id.home_component)).perform(ViewActions.swipeDown())
fun swipeToTop() = onView(withId(R.id.sessionControlRecyclerView)).perform(ViewActions.swipeDown())
fun swipeTabRight(title: String) =
onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeRight())
fun swipeTabLeft(title: String) =
onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeLeft())
fun closeTabViaXButton(title: String) = closeTabViaX(title)
fun verifySnackBarText(expectedText: String) {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime)
}
fun snackBarButtonClick(expectedText: String) {
onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check(
matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))
).perform(ViewActions.click())
}
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@ -167,6 +209,58 @@ class HomeScreenRobot {
ThreeDotMenuMainRobot().interact()
return ThreeDotMenuMainRobot.Transition()
}
fun closeAllPrivateTabs(interact: HomeScreenRobot.() -> Unit): Transition {
onView(withId(R.id.close_tabs_button))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.perform(click())
HomeScreenRobot().interact()
return Transition()
}
fun openNavigationToolbar(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition {
assertNavigationToolbar().perform(click())
NavigationToolbarRobot().interact()
return NavigationToolbarRobot.Transition()
}
fun openContextMenuOnTopSitesWithTitle(title: String, interact: HomeScreenRobot.() -> Unit): Transition {
onView(withId(R.id.top_sites_list)).perform(
actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText(title)), ViewActions.longClick())
)
HomeScreenRobot().interact()
return Transition()
}
fun openTopSiteTabWithTitle(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
onView(withId(R.id.top_sites_list)).perform(
actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText(title)), click())
)
BrowserRobot().interact()
return BrowserRobot.Transition()
}
fun removeTopSite(interact: HomeScreenRobot.() -> Unit): Transition {
onView(withText("Remove"))
.check((matches(withEffectiveVisibility(Visibility.VISIBLE))))
.perform(click())
HomeScreenRobot().interact()
return Transition()
}
fun openTopSiteInPrivateTab(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
onView(withText("Open in private tab"))
.check((matches(withEffectiveVisibility(Visibility.VISIBLE))))
.perform(click())
BrowserRobot().interact()
return BrowserRobot.Transition()
}
}
}
@ -176,14 +270,15 @@ 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")))
onView(allOf(withText("Search or enter address")))
private fun closeTabButton() = onView(withId(R.id.close_tab_button))
private fun assertNavigationToolbar() =
onView(CoreMatchers.allOf(withText("Search or enter address")))
onView(allOf(withText("Search or enter address")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout"))
@ -203,50 +298,63 @@ private fun assertHomeToolbar() = onView(ViewMatchers.withResourceName("toolbar"
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertOpenTabsHeader() =
onView(CoreMatchers.allOf(withText("Open tabs")))
onView(allOf(withText("Open tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAddTabButton() =
onView(CoreMatchers.allOf(withId(R.id.add_tab_button), isDisplayed()))
onView(allOf(withId(R.id.add_tab_button), isDisplayed()))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoTabsOpenedHeader() =
onView(CoreMatchers.allOf(withText("No open tabs")))
onView(allOf(withText("No open tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoTabsOpenedText() {
onView(CoreMatchers.allOf(withText("Your open tabs will be shown here.")))
onView(allOf(withText("Your open tabs will be shown here.")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertCollectionsHeader() =
onView(CoreMatchers.allOf(withText("Collections")))
onView(allOf(withText("Collections")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoCollectionsHeader() =
onView(CoreMatchers.allOf(withText("No collections")))
onView(allOf(withText("No collections")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoCollectionsText() =
onView(
CoreMatchers.allOf(
allOf(
withText("Collect the things that matter to you. To start, save open tabs to a new collection.")
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("home_component"))
private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("sessionControlRecyclerView"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
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!")))
onView(allOf(withText("Welcome to Firefox Preview!")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetTheMostHeader() =
onView(CoreMatchers.allOf(withText("Get the most out of Firefox Preview.")))
onView(allOf(withText("Get the most out of Firefox Preview.")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAccountsSignInButton() =
@ -254,15 +362,15 @@ private fun assertAccountsSignInButton() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetToKnowHeader() =
onView(CoreMatchers.allOf(withText("Get to know Firefox Preview")))
onView(allOf(withText("Get to know Firefox Preview")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertChooseThemeHeader() =
onView(CoreMatchers.allOf(withText("Choose your theme")))
onView(allOf(withText("Choose your theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertChooseThemeText() =
onView(CoreMatchers.allOf(withText("Try dark theme: easier on your battery and your eyes.")))
onView(allOf(withText("Try dark theme: easier on your battery and your eyes.")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertLightThemeToggle() =
@ -270,7 +378,7 @@ private fun assertLightThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertLightThemeDescription() =
onView(CoreMatchers.allOf(withText("Light theme")))
onView(allOf(withText("Light theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDarkThemeToggle() =
@ -278,7 +386,7 @@ private fun assertDarkThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDarkThemeDescription() =
onView(CoreMatchers.allOf(withText("Dark theme")))
onView(allOf(withText("Dark theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAutomaticThemeToggle() =
@ -286,21 +394,21 @@ private fun assertAutomaticThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAutomaticThemeDescription() =
onView(CoreMatchers.allOf(withText("Automatic")))
onView(allOf(withText("Automatic")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertProtectYourselfHeader() =
onView(CoreMatchers.allOf(withText("Protect yourself")))
onView(allOf(withText("Protect yourself")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertTrackingProtectionToggle() = onView(
CoreMatchers.allOf(ViewMatchers.withResourceName("tracking_protection_toggle"))
allOf(ViewMatchers.withResourceName("tracking_protection_toggle"))
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertProtectYourselfText() {
onView(
CoreMatchers.allOf(
allOf(
withText(
"Firefox Preview blocks ad trackers that follow you around the web."
)
@ -310,34 +418,39 @@ private fun assertProtectYourselfText() {
}
private fun assertBrowsePrivatelyHeader() =
onView(CoreMatchers.allOf(withText("Browse privately")))
onView(allOf(withText("Browse privately")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertBrowsePrivatelyText() =
onView(CoreMatchers.allOf(withText(containsString("private browsing is just a tap away."))))
onView(allOf(withText(containsString("private browsing is just a tap away."))))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertYourPrivacyHeader() =
onView(CoreMatchers.allOf(withText("Your privacy")))
onView(allOf(withText("Your privacy")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertYourPrivacyText() =
onView(CoreMatchers.allOf(withText(
"Weve designed Firefox Preview to give you control over what you share online and what you share with us.")))
onView(
allOf(
withText(
"Weve designed Firefox Preview to give you control over what you share online and what you share with us."
)
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertPrivacyNoticeButton() =
onView(CoreMatchers.allOf(withText("Read our privacy notice")))
onView(allOf(withText("Read our privacy notice")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertStartBrowsingButton() =
onView(CoreMatchers.allOf(withText("Start browsing")))
onView(allOf(withText("Start browsing")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
// Private mode elements
private fun assertPrivateSessionHeader() =
onView(CoreMatchers.allOf(withText("Private tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(allOf(withText("Private tabs")))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " +
"when you quit the app or close all private tabs. While this doesnt make you anonymous to websites or " +
@ -345,27 +458,28 @@ const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and brow
"who uses this device."
private fun assertPrivateSessionMessage(visible: Boolean) =
onView(CoreMatchers.allOf(withText(PRIVATE_SESSION_MESSAGE)))
onView(allOf(withText(PRIVATE_SESSION_MESSAGE)))
.check(
if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else doesNotExist()
)
private fun assertShareTabsButton(visible: Boolean) =
onView(CoreMatchers.allOf(withId(R.id.share_tabs_button), isDisplayed()))
onView(allOf(withId(R.id.share_tabs_button), isDisplayed()))
.check(matches(withEffectiveVisibility(visibleOrGone(visible))))
private fun assertCloseTabsButton(visible: Boolean) =
onView(CoreMatchers.allOf(withId(R.id.close_tab_button), isDisplayed()))
.check(matches(withEffectiveVisibility(visibleOrGone(visible))))
private fun assertCloseTabsButton(title: String) =
onView(allOf(withId(R.id.close_tab_button), withContentDescription("Close tab $title")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun visibleOrGone(visibility: Boolean) = if (visibility) Visibility.VISIBLE else Visibility.GONE
private fun visibleOrGone(visibility: Boolean) =
if (visibility) Visibility.VISIBLE else Visibility.GONE
private fun assertExistingTabList() =
onView(CoreMatchers.allOf(withId(R.id.item_tab)))
onView(allOf(withId(R.id.item_tab)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertExistingOpenTabs(title: String) =
onView(withId(R.id.home_component)).perform(
onView(withId(R.id.sessionControlRecyclerView)).perform(
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
ViewMatchers.hasDescendant(withText(title))
)
@ -373,6 +487,46 @@ 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")))
private fun collectionNameTextField() =
onView(allOf(ViewMatchers.withResourceName("name_collection_edittext")))
private fun closeTabViaX(title: String) {
val closeButton = onView(
allOf(
withId(R.id.close_tab_button),
withContentDescription("Close tab $title")
)
)
closeButton.perform(click())
}
private fun assertPrivateTabsCloseTabsButton() = onView(allOf(withId(R.id.close_tabs_button)))
private fun assertExistingTopSitesList() =
onView(allOf(withId(R.id.top_sites_list)))
.check((matches(withEffectiveVisibility(Visibility.VISIBLE))))
private fun assertExistingTopSitesTabs(title: String) =
onView(allOf(withId(R.id.top_sites_list)))
.check(matches(hasItem(hasDescendant(withText(title)))))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNotExistingTopSitesList(title: String) =
onView(allOf(withId(R.id.top_sites_list)))
.check(matches(not(hasItem(hasDescendant(withText(title))))))
private fun assertTopSiteContextMenuItems() {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(
Until.findObject(By.text("Open in private tab")),
waitingTime
)
mDevice.waitNotNull(
Until.findObject(By.text("Remove")),
waitingTime
)
}

View File

@ -45,7 +45,7 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
mDevice.waitNotNull(
Until.findObject(
By.text("SHARE A LINK")
By.text("ALL ACTIONS")
), waitingTime
)
}
@ -55,7 +55,7 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
mDevice.waitNotNull(
Until.findObject(
By.text("SHARE A LINK")
By.text("ALL ACTIONS")
), waitingTime
)
}

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())
@ -54,7 +80,7 @@ class NavigationToolbarRobot {
return BrowserRobot.Transition()
}
fun visitLinkFromClipboard(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
fun visitLinkFromClipboard(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_clear_view")),
waitingTime
@ -62,11 +88,12 @@ class NavigationToolbarRobot {
clearAddressBar().click()
mDevice.waitNotNull(
Until.findObject(By.text(url.toString())), waitingTime
Until.findObject(By.res("org.mozilla.fenix.debug:id/clipboard_title")),
waitingTime
)
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/fill_link_from_clipboard")),
Until.findObject(By.res("org.mozilla.fenix.debug:id/clipboard_url")),
waitingTime
)
fillLinkButton().click()
@ -74,6 +101,13 @@ class NavigationToolbarRobot {
BrowserRobot().interact()
return BrowserRobot.Transition()
}
fun goBack(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
goBackButton()
HomeScreenRobot().interact()
return HomeScreenRobot.Transition()
}
}
}
@ -82,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.fill_link_from_clipboard))
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

@ -0,0 +1,263 @@
/* 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 android.content.Context
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
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.withId
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.nthChildOf
/**
* Implementation of Robot Pattern for Reader View UI.
*/
class ReaderViewRobot {
fun verifyReaderViewDetected(visible: Boolean = false): ViewInteraction =
assertReaderViewDetected(visible)
fun verifyAppearanceFontGroup(visible: Boolean = false): ViewInteraction =
assertAppearanceFontGroup(visible)
fun verifyAppearanceFontSansSerif(visible: Boolean = false): ViewInteraction =
assertAppearanceFontSansSerif(visible)
fun verifyAppearanceFontSerif(visible: Boolean = false): ViewInteraction =
assertAppearanceFontSerif(visible)
fun verifyAppearanceFontDecrease(visible: Boolean = false): ViewInteraction =
assertAppearanceFontDecrease(visible)
fun verifyAppearanceFontIncrease(visible: Boolean = false): ViewInteraction =
assertAppearanceFontIncrease(visible)
fun verifyAppearanceColorGroup(visible: Boolean = false): ViewInteraction =
assertAppearanceColorGroup(visible)
fun verifyAppearanceColorSepia(visible: Boolean = false): ViewInteraction =
assertAppearanceColorSepia(visible)
fun verifyAppearanceColorDark(visible: Boolean = false): ViewInteraction =
assertAppearanceColorDark(visible)
fun verifyAppearanceColorLight(visible: Boolean = false): ViewInteraction =
assertAppearanceColorLight(visible)
fun verifyAppearanceFontIsActive(fontType: String) {
val fontTypeKey: String = "mozac-readerview-fonttype"
val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences(
"mozac_feature_reader_view",
Context.MODE_PRIVATE
)
assertEquals(fontType, prefs.getString(fontTypeKey, ""))
}
fun verifyAppearanceFontSize(expectedFontSize: Int) {
val fontSizeKey: String = "mozac-readerview-fontsize"
val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences(
"mozac_feature_reader_view",
Context.MODE_PRIVATE
)
val fontSizeKeyValue = prefs.getInt(fontSizeKey, 3)
assertEquals(expectedFontSize, fontSizeKeyValue)
}
fun verifyAppearanceColorSchemeChange(expectedColorScheme: String) {
val colorSchemeKey: String = "mozac-readerview-colorscheme"
val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences(
"mozac_feature_reader_view",
Context.MODE_PRIVATE
)
assertEquals(expectedColorScheme, prefs.getString(colorSchemeKey, ""))
}
class Transition {
fun toggleSansSerif(interact: ReaderViewRobot.() -> Unit): Transition {
fun sansSerifButton() =
onView(
withId(R.id.mozac_feature_readerview_font_sans_serif)
)
sansSerifButton().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleSerif(interact: ReaderViewRobot.() -> Unit): Transition {
fun serifButton() =
onView(
withId(R.id.mozac_feature_readerview_font_serif)
)
serifButton().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleFontSizeDecrease(interact: ReaderViewRobot.() -> Unit): Transition {
fun fontSizeDecrease() =
onView(
withId(R.id.mozac_feature_readerview_font_size_decrease)
)
fontSizeDecrease().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleFontSizeIncrease(interact: ReaderViewRobot.() -> Unit): Transition {
fun fontSizeIncrease() =
onView(
withId(R.id.mozac_feature_readerview_font_size_increase)
)
fontSizeIncrease().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleColorSchemeChangeLight(interact: ReaderViewRobot.() -> Unit): Transition {
fun toggleLightColorSchemeButton() =
onView(
withId(R.id.mozac_feature_readerview_color_light)
)
toggleLightColorSchemeButton().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleColorSchemeChangeDark(interact: ReaderViewRobot.() -> Unit): Transition {
fun toggleDarkColorSchemeButton() =
onView(
withId(R.id.mozac_feature_readerview_color_dark)
)
toggleDarkColorSchemeButton().click()
ReaderViewRobot().interact()
return Transition()
}
fun toggleColorSchemeChangeSepia(interact: ReaderViewRobot.() -> Unit): Transition {
fun toggleSepiaColorSchemeButton() =
onView(
withId(R.id.mozac_feature_readerview_color_sepia)
)
toggleSepiaColorSchemeButton().click()
ReaderViewRobot().interact()
return Transition()
}
}
}
fun readerViewRobot(interact: ReaderViewRobot.() -> Unit): ReaderViewRobot.Transition {
ReaderViewRobot().interact()
return ReaderViewRobot.Transition()
}
/**
* Detects for the blue notification dot in the three dot menu
*/
private fun assertReaderViewDetected(visible: Boolean) =
onView(
nthChildOf(
withId(R.id.mozac_browser_toolbar_menu), 2
)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceFontGroup(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_font_group)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceFontSansSerif(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_font_sans_serif)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceFontSerif(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_font_serif)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceFontDecrease(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_font_size_decrease)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceFontIncrease(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_font_size_increase)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceColorDark(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_color_dark)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceColorLight(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_color_light)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceColorSepia(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_color_sepia)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun assertAppearanceColorGroup(visible: Boolean) =
onView(
withId(R.id.mozac_feature_readerview_color_scheme_group)
).check(
matches(withEffectiveVisibility(visibleOrGone(visible)))
)
private fun visibleOrGone(visibility: Boolean) =
if (visibility) ViewMatchers.Visibility.VISIBLE else ViewMatchers.Visibility.GONE

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

@ -17,6 +17,7 @@ import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.toPackage
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
@ -35,10 +36,10 @@ import org.mozilla.fenix.helpers.click
class SettingsRobot {
// BASICS SECTION
fun verifyBasicsHeading() = assertBasicsHeading()
fun verifyBasicsHeading() = assertGeneralHeading()
fun verifySearchEngineButton() = assertSearchEngineButton()
fun verifyThemeButton() = assertThemeButton()
fun verifyThemeButton() = assertCustomizeButton()
fun verifyThemeSelected() = assertThemeSelected()
fun verifyAccessibilityButton() = assertAccessibilityButton()
fun verifySetAsDefaultBrowserButton() = assertSetAsDefaultBrowserButton()
@ -51,7 +52,7 @@ class SettingsRobot {
fun verifyEnhancedTrackingProtectionValue(state: String) =
assertEnhancedTrackingProtectionValue(state)
fun verifyAddPrivateBrowsingShortcutButton() = assertAddPrivateBrowsingShortcutButton()
fun verifyAddPrivateBrowsingShortcutButton() = assertPrivateBrowsingButton()
fun verifySitePermissionsButton() = assertSitePermissionsButton()
fun verifyDeleteBrowsingDataButton() = assertDeleteBrowsingDataButton()
fun verifyDeleteBrowsingDataOnQuitButton() = assertDeleteBrowsingDataOnQuitButton()
@ -101,10 +102,10 @@ class SettingsRobot {
return SettingsSubMenuSearchRobot.Transition()
}
fun openThemeSubMenu(interact: SettingsSubMenuThemeRobot.() -> Unit): SettingsSubMenuThemeRobot.Transition {
fun openCustomizeSubMenu(interact: SettingsSubMenuThemeRobot.() -> Unit): SettingsSubMenuThemeRobot.Transition {
fun themeButton() = onView(ViewMatchers.withText("Theme"))
themeButton().click()
fun customizeButton() = onView(ViewMatchers.withText("Customize"))
customizeButton().click()
SettingsSubMenuThemeRobot().interact()
return SettingsSubMenuThemeRobot.Transition()
@ -145,19 +146,27 @@ class SettingsRobot {
SettingsSubMenuLoginsAndPasswordRobot().interact()
return SettingsSubMenuLoginsAndPasswordRobot.Transition()
}
fun openTurnOnSyncMenu(interact: SettingsTurnOnSyncRobot.() -> Unit): SettingsTurnOnSyncRobot.Transition {
fun turnOnSyncButton() = onView(ViewMatchers.withText("Turn on Sync"))
turnOnSyncButton().click()
SettingsTurnOnSyncRobot().interact()
return SettingsTurnOnSyncRobot.Transition()
}
}
}
private fun assertSettingsView() {
// verify that we are in the correct library view
assertBasicsHeading()
assertGeneralHeading()
assertPrivacyHeading()
assertDeveloperToolsHeading()
assertAboutHeading()
}
// BASICS SECTION
private fun assertBasicsHeading() = onView(ViewMatchers.withText("Basics"))
// GENERAL SECTION
private fun assertGeneralHeading() = onView(ViewMatchers.withText("General"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertSearchEngineButton() {
@ -166,7 +175,7 @@ private fun assertSearchEngineButton() {
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertThemeButton() = onView(ViewMatchers.withText("Theme"))
private fun assertCustomizeButton() = onView(ViewMatchers.withText("Customize"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertThemeSelected() = onView(ViewMatchers.withText("Light"))
@ -181,7 +190,7 @@ private fun assertSetAsDefaultBrowserButton() =
// PRIVACY SECTION
private fun assertPrivacyHeading() {
onView(ViewMatchers.withText("Privacy"))
onView(ViewMatchers.withText("Privacy and security"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
@ -202,10 +211,10 @@ private fun assertLoginsButton() {
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertAddPrivateBrowsingShortcutButton() {
TestHelper.scrollToElementByText("Add private browsing shortcut")
mDevice.wait(Until.findObject(By.text("Add private browsing shortcut")), waitingTime)
onView(ViewMatchers.withText("Add private browsing shortcut"))
private fun assertPrivateBrowsingButton() {
TestHelper.scrollToElementByText("Private browsing")
mDevice.wait(Until.findObject(By.text("Private browsing")), waitingTime)
onView(ViewMatchers.withText("Private browsing"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
@ -250,19 +259,19 @@ private fun assertRemoteDebug() {
private fun assertAboutHeading(): ViewInteraction {
TestHelper.scrollToElementByText("About")
return onView(ViewMatchers.withText("About"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.check(matches(isCompletelyDisplayed()))
}
private fun assertRateOnGooglePlay(): ViewInteraction {
TestHelper.scrollToElementByText("About Firefox Preview")
return onView(ViewMatchers.withText("Rate on Google Play"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.check(matches(isCompletelyDisplayed()))
}
private fun assertAboutFirefoxPreview(): ViewInteraction {
TestHelper.scrollToElementByText("About Firefox Preview")
return onView(ViewMatchers.withText("About Firefox Preview"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.check(matches(isCompletelyDisplayed()))
}
fun swipeToBottom() = onView(ViewMatchers.withId(R.id.recycler_view)).perform(ViewActions.swipeUp())

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

@ -6,13 +6,18 @@
package org.mozilla.fenix.ui.robots
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers
import org.hamcrest.Matcher
import org.mozilla.fenix.helpers.TestHelper
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.isChecked
@ -38,8 +43,26 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot {
return SettingsRobot.Transition()
}
fun openExceptions(interact: SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.() -> Unit): SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.Transition {
openExceptions().click()
fun openExceptions(
interact: SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.() -> Unit
): SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.Transition {
TestHelper.scrollToElementByText("Exceptions")
openExceptions().perform(
object : ViewAction {
override fun getConstraints(): Matcher<View> {
// do not check constraints, check if enabled
return ViewMatchers.isEnabled()
}
override fun getDescription(): String {
return "Exceptions"
}
override fun perform(uiController: UiController?, view: View) {
view.performClick()
}
}
)
SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot().interact()
return SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.Transition()

View File

@ -0,0 +1,38 @@
/* 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.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.withEffectiveVisibility
import org.hamcrest.CoreMatchers
/**
* Implementation of Robot Pattern for the Privacy Settings > saved logins sub menu
*/
class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot {
fun verifySaveLoginsOptionsView() {
onView(ViewMatchers.withText("Ask to save"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Never save"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
class Transition {
fun goBack(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordRobot.Transition {
goBackButton().perform(ViewActions.click())
SettingsSubMenuLoginsAndPasswordRobot().interact()
return SettingsSubMenuLoginsAndPasswordRobot.Transition()
}
}
}
private fun goBackButton() =
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))

View File

@ -65,6 +65,14 @@ class SettingsSubMenuLoginsAndPasswordRobot {
SettingsTurnOnSyncRobot().interact()
return SettingsTurnOnSyncRobot.Transition()
}
fun saveLoginsAndPasswordsOptions(interact: SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.Transition {
fun saveLoginsAndPasswordButton() = onView(ViewMatchers.withText("Save logins and passwords"))
saveLoginsAndPasswordButton().click()
SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot().interact()
return SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.Transition()
}
}
}

View File

@ -6,6 +6,7 @@ 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.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
@ -29,6 +30,11 @@ class SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot {
fun tapSetupLater() = onView(ViewMatchers.withText("Later")).perform(ViewActions.click())
fun verifySavedLoginFromPrompt() = mDevice.waitNotNull(Until.findObjects(By.text("test@example.com")))
fun verifyNotSavedLoginFromPromt() = onView(ViewMatchers.withText("test@example.com"))
.check(ViewAssertions.doesNotExist())
class Transition {
fun goBack(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordRobot.Transition {
goBackButton().perform(ViewActions.click())

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")))

View File

@ -6,16 +6,21 @@
package org.mozilla.fenix.ui.robots
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.hasFocus
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
@ -43,9 +48,11 @@ class ThreeDotMenuMainRobot {
fun verifyRefreshButton() = assertRefreshButton()
fun verifyCloseAllTabsButton() = assertCloseAllTabsButton()
fun verifyShareButton() = assertShareButton()
fun verifyReaderViewToggle(visible: Boolean) = assertReaderViewToggle(visible)
fun verifyReaderViewAppearance(visible: Boolean) = assertReaderViewAppearanceButton(visible)
fun clickShareButton() {
shareButton().click()
mDevice.waitNotNull(Until.findObject(By.text("SHARE A LINK")), waitingTime)
mDevice.waitNotNull(Until.findObject(By.text("ALL ACTIONS")), waitingTime)
}
fun verifyShareTabButton() = assertShareTabButton()
@ -72,6 +79,7 @@ class ThreeDotMenuMainRobot {
fun verifySendToDeviceTitle() = assertSendToDeviceTitle()
fun verifyShareALinkTitle() = assertShareALinkTitle()
fun verifyWhatsNewButton() = assertWhatsNewButton()
fun verifyAddFirefoxHome() = assertAddToFirefoxHome()
class Transition {
@ -182,11 +190,41 @@ class ThreeDotMenuMainRobot {
return BrowserRobot.Transition()
}
fun typeCollectionName(name: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.wait(Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")), waitingTime)
fun typeCollectionName(
name: String,
interact: BrowserRobot.() -> Unit
): BrowserRobot.Transition {
mDevice.wait(
Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")),
waitingTime
)
collectionNameTextField().check(matches(hasFocus()))
collectionNameTextField().perform(ViewActions.replaceText(name), ViewActions.pressImeActionButton())
collectionNameTextField().perform(
ViewActions.replaceText(name),
ViewActions.pressImeActionButton()
)
BrowserRobot().interact()
return BrowserRobot.Transition()
}
fun toggleReaderView(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition {
readerViewToggle().click()
NavigationToolbarRobot().interact()
return NavigationToolbarRobot.Transition()
}
fun openReaderViewAppearance(interact: ReaderViewRobot.() -> Unit): ReaderViewRobot.Transition {
readerViewAppearanceToggle().click()
ReaderViewRobot().interact()
return ReaderViewRobot.Transition()
}
fun addToFirefoxHome(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
addToFirefoxHomeButton().click()
BrowserRobot().interact()
return BrowserRobot.Transition()
@ -198,8 +236,13 @@ private fun threeDotMenuRecyclerViewExists() {
onView(withId(R.id.mozac_browser_menu_recyclerView)).check(matches(isDisplayed()))
}
private fun settingsButton() = onView(allOf(withText(R.string.settings),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun settingsButton() = onView(
allOf(
withText(R.string.settings),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun assertSettingsButton() = settingsButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
@ -247,8 +290,12 @@ private fun shareButton() = onView(ViewMatchers.withContentDescription("Share"))
private fun assertShareButton() = shareButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun browserViewSaveCollectionButton() = onView(allOf(withText("Save to Collection"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun browserViewSaveCollectionButton() = onView(
allOf(
withText("Save to Collection"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun saveCollectionButton() = onView(allOf(withText("Save to collection")))
private fun assertSaveCollectionButton() = saveCollectionButton()
@ -276,13 +323,41 @@ private fun assertSendToDeviceTitle() = SendToDeviceTitle()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun ShareALinkTitle() =
onView(allOf(withText("SHARE A LINK"), withResourceName("link_header")))
onView(allOf(withText("ALL ACTIONS"), withResourceName("apps_link_header")))
private fun assertShareALinkTitle() = ShareALinkTitle()
private fun whatsNewButton() = onView(
allOf(
withText("Whats New"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun assertWhatsNewButton() = whatsNewButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun readerViewToggle() = onView(allOf(withText(R.string.browser_menu_read)))
private fun assertReaderViewToggle(visible: Boolean) = readerViewToggle()
.check(
if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) else ViewAssertions.doesNotExist()
)
private fun readerViewAppearanceToggle() =
onView(allOf(withText(R.string.browser_menu_read_appearance)))
private fun assertReaderViewAppearanceButton(visible: Boolean) = readerViewAppearanceToggle()
.check(
if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) else ViewAssertions.doesNotExist()
)
private fun addToFirefoxHomeButton() =
onView(allOf(withText(R.string.browser_menu_add_to_top_sites)))
private fun assertAddToFirefoxHome() {
onView(withId(R.id.mozac_browser_menu_recyclerView))
.perform(
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.browser_menu_add_to_top_sites))
)
).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<resources>
<color name="ic_launcher_background">#ff20123a</color>
</resources>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- 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/. -->
<resources>
<!-- Name of the application -->
<string name="app_name" translatable="false">Firefox Beta</string>
</resources>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="open_new_tab"
android:enabled="true"
android:icon="@drawable/ic_static_shortcut_tab"
android:shortcutShortLabel="@string/home_screen_shortcut_open_new_tab_2"
android:shortcutLongLabel="@string/home_screen_shortcut_open_new_tab_2">
<intent
android:action="org.mozilla.fenix.OPEN_TAB"
android:targetPackage="org.mozilla.firefox_beta"
android:targetClass="org.mozilla.fenix.IntentReceiverActivity" />
</shortcut>
<shortcut
android:shortcutId="open_new_private_tab"
android:enabled="true"
android:icon="@drawable/ic_static_shortcut_private_tab"
android:shortcutShortLabel="@string/home_screen_shortcut_open_new_private_tab_2"
android:shortcutLongLabel="@string/home_screen_shortcut_open_new_private_tab_2">
<intent
android:action="org.mozilla.fenix.OPEN_PRIVATE_TAB"
android:targetPackage="org.mozilla.firefox_beta"
android:targetClass="org.mozilla.fenix.IntentReceiverActivity" />
</shortcut>
</shortcuts>

Some files were not shown because too many files have changed in this diff Show More