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: when:
- {hour: 6, minute: 0} - {hour: 6, minute: 0}
- {hour: 18, minute: 0} - {hour: 18, minute: 0}
- name: fennec-nightly - name: fennec-beta
job: job:
type: decision-task type: decision-task
treeherder-symbol: N-fennec treeherder-symbol: fennec-beta
target-tasks-method: fennec-nightly target-tasks-method: fennec-beta
when: [] # Force hook only when: [] # Force hook only
- name: raptor - name: raptor
job: job:

1
.gitignore vendored
View File

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

View File

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

View File

@ -5,6 +5,7 @@ plugins {
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply from: "$project.rootDir/automation/gradle/versionCode.gradle" apply from: "$project.rootDir/automation/gradle/versionCode.gradle"
apply plugin: 'androidx.navigation.safeargs.kotlin' apply plugin: 'androidx.navigation.safeargs.kotlin'
@ -150,6 +151,7 @@ android {
testOptions { testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR' execution 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests.includeAndroidResources = true unitTests.includeAndroidResources = true
animationsDisabled = true
} }
flavorDimensions "engine" flavorDimensions "engine"
@ -355,6 +357,21 @@ android.applicationVariants.all { variant ->
buildConfigField 'String', 'DIGITAL_ASSET_LINKS_TOKEN', 'null' buildConfigField 'String', 'DIGITAL_ASSET_LINKS_TOKEN', 'null'
println("X_X") 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 { androidExtensions {
@ -374,9 +391,11 @@ dependencies {
implementation Deps.androidx_coordinatorlayout implementation Deps.androidx_coordinatorlayout
implementation Deps.sentry implementation Deps.sentry
implementation Deps.leanplum
implementation Deps.osslicenses_library implementation Deps.osslicenses_library
implementation Deps.leanplum_core
implementation Deps.leanplum_fcm
implementation Deps.mozilla_concept_engine implementation Deps.mozilla_concept_engine
implementation Deps.mozilla_concept_push implementation Deps.mozilla_concept_push
implementation Deps.mozilla_concept_storage implementation Deps.mozilla_concept_storage
@ -394,6 +413,9 @@ dependencies {
implementation Deps.mozilla_browser_storage_sync implementation Deps.mozilla_browser_storage_sync
implementation Deps.mozilla_browser_toolbar implementation Deps.mozilla_browser_toolbar
implementation Deps.mozilla_support_extensions
implementation Deps.mozilla_feature_addons
implementation Deps.mozilla_feature_accounts implementation Deps.mozilla_feature_accounts
implementation Deps.mozilla_feature_app_links implementation Deps.mozilla_feature_app_links
implementation Deps.mozilla_feature_awesomebar implementation Deps.mozilla_feature_awesomebar
@ -415,6 +437,7 @@ dependencies {
implementation Deps.mozilla_feature_readerview implementation Deps.mozilla_feature_readerview
implementation Deps.mozilla_feature_tab_collections implementation Deps.mozilla_feature_tab_collections
implementation Deps.mozilla_feature_top_sites implementation Deps.mozilla_feature_top_sites
implementation Deps.mozilla_feature_share
implementation Deps.mozilla_feature_accounts_push implementation Deps.mozilla_feature_accounts_push
implementation Deps.mozilla_feature_webcompat implementation Deps.mozilla_feature_webcompat
implementation Deps.mozilla_feature_webnotifications implementation Deps.mozilla_feature_webnotifications
@ -423,6 +446,8 @@ dependencies {
implementation Deps.mozilla_service_firefox_accounts implementation Deps.mozilla_service_firefox_accounts
implementation Deps.mozilla_service_glean implementation Deps.mozilla_service_glean
implementation Deps.mozilla_service_experiments implementation Deps.mozilla_service_experiments
implementation Deps.mozilla_service_location
implementation Deps.mozilla_support_base implementation Deps.mozilla_support_base
implementation Deps.mozilla_support_ktx implementation Deps.mozilla_support_ktx
@ -494,6 +519,8 @@ dependencies {
exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'com.android.support', module: 'support-annotations'
} }
androidTestImplementation Deps.androidx_junit
androidTestImplementation Deps.androidx_work_testing
androidTestImplementation Deps.mockwebserver androidTestImplementation Deps.mockwebserver
testImplementation Deps.mozilla_support_test testImplementation Deps.mozilla_support_test
testImplementation Deps.androidx_junit 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: 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, 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, 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: bugs:
- https://github.com/mozilla-mobile/fenix/issues/1024 - https://github.com/mozilla-mobile/fenix/issues/1024
data_reviews: 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. 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: send_in_pings:
- baseline - baseline
- metrics
bugs: bugs:
- https://github.com/mozilla-mobile/fenix/issues/1301 - https://github.com/mozilla-mobile/fenix/issues/1301
- https://github.com/mozilla-mobile/fenix/issues/4456
data_reviews: data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1785 - https://github.com/mozilla-mobile/fenix/pull/1785
- https://github.com/mozilla-mobile/fenix/pull/8314
notification_emails: notification_emails:
- fenix-core@mozilla.com - fenix-core@mozilla.com
expires: "2020-09-01" expires: "2020-09-01"
@ -362,20 +365,6 @@ metrics:
notification_emails: notification_emails:
- fenix-core@mozilla.com - fenix-core@mozilla.com
expires: "2020-09-01" 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: toolbar_position:
type: string type: string
lifetime: application lifetime: application
@ -1703,3 +1692,167 @@ top_sites:
notification_emails: notification_emails:
- fenix-core@mozilla.com - fenix-core@mozilla.com
expires: "2020-09-01" 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 - https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209
notification_emails: notification_emails:
- fenix-core@mozilla.com - 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> <body>
<h1> <p id="testContent">Page content: lorem ipsum</p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit,<br>
sed do eiusmod tempor incididunt ut labore et dolore magna <br> <h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt</h1>
aliqua. Ut enim ad minim veniam, quis nostrud exercitation <br>
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis <br> <p>
aute irure dolor in reprehenderit in voluptate velit esse cillum <br> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat <br> eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
non proident, sunt in culpa qui officia deserunt mollit anim id est <br> voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
laborum. clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
</h1> 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> </body>
</html> </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 package org.mozilla.fenix.helpers
import android.graphics.Bitmap
import android.view.View import android.view.View
import android.view.ViewGroup
import org.hamcrest.CoreMatchers.not import org.hamcrest.CoreMatchers.not
import org.hamcrest.Description
import org.hamcrest.Matcher 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.isChecked as espressoIsChecked
import androidx.test.espresso.matcher.ViewMatchers.isEnabled as espressoIsEnabled import androidx.test.espresso.matcher.ViewMatchers.isEnabled as espressoIsEnabled
import androidx.test.espresso.matcher.ViewMatchers.isSelected as espressoIsSelected import androidx.test.espresso.matcher.ViewMatchers.isSelected as espressoIsSelected
@ -30,3 +35,24 @@ private fun maybeInvertMatcher(matcher: Matcher<View>, useUnmodifiedMatcher: Boo
useUnmodifiedMatcher -> matcher useUnmodifiedMatcher -> matcher
else -> not(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 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, "") 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 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() 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.Espresso.onView
import androidx.test.espresso.action.ViewActions.click 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.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.helpers.HomeActivityTestRule 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.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until
import okhttp3.mockwebserver.MockWebServer
import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.allOf
import org.junit.After
import org.junit.Before
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.ext.toUri
import org.mozilla.fenix.helpers.ext.waitNotNull 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") @Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class SyncIntegrationTest { class SyncIntegrationTest {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
@get:Rule @get:Rule
val activityTestRule = HomeActivityTestRule() val activityTestRule = HomeActivityTestRule()
@Before
fun setUp() {
mockWebServer = MockWebServer().apply {
setDispatcher(AndroidAssetDispatcher())
start()
}
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
// History item Desktop -> Fenix // History item Desktop -> Fenix
@Test @Test
fun checkHistoryFromDesktopTest() { fun checkHistoryFromDesktopTest() {
@ -82,6 +101,7 @@ class SyncIntegrationTest {
} }
} }
// Login item Desktop -> Fenix
@Test @Test
fun checkLoginsFromDesktopTest() { fun checkLoginsFromDesktopTest() {
homeScreen { homeScreen {
@ -113,30 +133,46 @@ class SyncIntegrationTest {
} }
} }
/* These tests will be running in the future // Bookmark item Fenix -> Desktop
// once the test above runs successfully and @Test
// the environment is stable 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 // History item Fenix -> Desktop
@Test @Test
fun checkBookmarkFromDeviceTest() {
tapInToolBar()
typeInToolBar()
seeBookmark()
mDevice.pressBack()
signInFxSync()
}
// Bookmark item Fenix -> Desktop
@Test
fun checkHistoryFromDeviceTest() { fun checkHistoryFromDeviceTest() {
tapInToolBar() val defaultWebPage = "example.com".toUri()!!
typeInToolBar() navigationToolbar {
sleep(TestAssetHelper.waitingTime) }.enterURLAndEnterToBrowser(defaultWebPage) {
mDevice.pressBack() }.openThreeDotMenu {
signInFxSync() }.openSettings {
}.openTurnOnSyncMenu {
useEmailInsteadButton()
typeEmail()
tapOnContinueButton()
typePassword()
sleep(TestAssetHelper.waitingTimeShort)
tapOnSignIn()
}
} }
*/
// Useful functions for the tests // Useful functions for the tests
fun typeEmail() { fun typeEmail() {
@ -170,11 +206,6 @@ class SyncIntegrationTest {
mDevice.pressEnter() mDevice.pressEnter()
} }
fun typeInToolBar() {
awesomeBar().perform(replaceText("example.com"),
pressImeActionButton())
}
fun historyAfterSyncIsShown() { fun historyAfterSyncIsShown() {
val historyEntry = mDevice.findObject(By.text("http://www.example.com/")) val historyEntry = mDevice.findObject(By.text("http://www.example.com/"))
historyEntry.isEnabled() historyEntry.isEnabled()
@ -185,12 +216,6 @@ class SyncIntegrationTest {
bookmarkEntry.isEnabled() bookmarkEntry.isEnabled()
} }
fun seeBookmark() {
mDevice.waitNotNull(Until.findObjects(By.text("Bookmark")), TestAssetHelper.waitingTime)
val bookmarkButton = mDevice.findObject(By.text("Bookmark"))
bookmarkButton.click()
}
fun tapReturnToPreviousApp() { fun tapReturnToPreviousApp() {
mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime) mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)
mDevice.pressBack() mDevice.pressBack()
@ -218,7 +243,5 @@ class SyncIntegrationTest {
} }
fun settingsAccount() = onView(allOf(withText("Turn on Sync"))).perform(click()) 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 useEmailInsteadButton() = onView(withId(R.id.signInEmailButton)).perform(click())
fun enterAccountSettings() = onView(withId(R.id.email)).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') tps.run('test_history.js')
gradlewbuild.test('checkHistoryFromDesktopTest') gradlewbuild.test('checkHistoryFromDesktopTest')
def test_sync_bookmark_from_device(tps, gradlewbuild): def test_sync_bookmark_from_desktop(tps, gradlewbuild):
os.chdir('app/src/androidTest/java/org/mozilla/fenix/syncintegration/')
tps.run('test_bookmark.js') tps.run('test_bookmark.js')
gradlewbuild.test('checkBookmarkFromDesktopTest') gradlewbuild.test('checkBookmarkFromDesktopTest')
def test_sync_logins_from_device(tps, gradlewbuild): def test_sync_logins_from_device(tps, gradlewbuild):
os.chdir('app/src/androidTest/java/org/mozilla/fenix/syncintegration/')
tps.run('test_logins.js') tps.run('test_logins.js')
gradlewbuild.test('checkLoginsFromDesktopTest') 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 okhttp3.mockwebserver.MockWebServer
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.ext.bookmarkStorage import org.mozilla.fenix.ext.bookmarkStorage
@ -103,6 +104,7 @@ class BookmarksTest {
verifyKeyboardVisible() verifyKeyboardVisible()
addNewFolderName(bookmarksFolderName) addNewFolderName(bookmarksFolderName)
saveNewFolder() saveNewFolder()
getInstrumentation().waitForIdleSync()
verifyFolderTitle(bookmarksFolderName) verifyFolderTitle(bookmarksFolderName)
verifyKeyboardHidden() verifyKeyboardHidden()
} }
@ -268,6 +270,7 @@ class BookmarksTest {
} }
} }
@Ignore("Temp disable: Nexus 6 failures - issue: https://github.com/mozilla-mobile/fenix/issues/7417")
@Test @Test
fun deleteMultipleSelectionTest() { fun deleteMultipleSelectionTest() {
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) 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 @Test
fun multipleBookmarkDeletions() { fun multipleBookmarkDeletions() {
homeScreen { homeScreen {

View File

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

View File

@ -110,7 +110,6 @@ class HomeScreenTest {
verifyHomeWordmark() verifyHomeWordmark()
verifyAddTabButton() verifyAddTabButton()
verifyShareTabsButton(visible = false) verifyShareTabsButton(visible = false)
verifyCloseTabsButton(visible = false)
verifyPrivateSessionHeader() verifyPrivateSessionHeader()
verifyPrivateSessionMessage(visible = true) verifyPrivateSessionMessage(visible = true)
verifyHomeToolbar() verifyHomeToolbar()
@ -130,7 +129,6 @@ class HomeScreenTest {
verifyHomeWordmark() verifyHomeWordmark()
verifyAddTabButton() verifyAddTabButton()
verifyShareTabsButton(visible = true) verifyShareTabsButton(visible = true)
verifyCloseTabsButton(visible = true)
verifyPrivateSessionHeader() verifyPrivateSessionHeader()
verifyPrivateSessionMessage(visible = false) verifyPrivateSessionMessage(visible = false)
verifyHomeToolbar() 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 { homeScreen {
}.openSearch { }.openSearch {
verifySearchWithText() verifySearchWithText()
clickDuckDuckGoEngineButton() clickSearchEngineButton("DuckDuckGo")
typeSearch("mozilla") typeSearch("mozilla")
verifyDuckDuckGoResults() verifySearchEngineResults("DuckDuckGo")
clickDuckDuckGoResult() clickSearchEngineResult("DuckDuckGo")
verifyDuckDuckGoURL() verifySearchEngineURL("DuckDuckGo")
} }
} }

View File

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

View File

@ -14,8 +14,10 @@ import org.junit.Ignore
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestHelper import org.mozilla.fenix.helpers.TestHelper
import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
/** /**
* Tests for verifying the main three dot menu options * 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") @Ignore("This is a stub test, ignore for now")
@Test @Test
fun toggleTrackingProtection() { fun toggleTrackingProtection() {

View File

@ -28,8 +28,10 @@ import org.mozilla.fenix.ui.robots.navigationToolbar
* - Opening a private tab * - Opening a private tab
* - Verifying tab list * - Verifying tab list
* - Closing all tabs * - Closing all tabs
* - Close tab
* - Swipe to close tab
* - Undo close tab
* *
* TODO: Tab Collections
*/ */
class TabbedBrowsingTest { class TabbedBrowsingTest {
@ -76,7 +78,10 @@ class TabbedBrowsingTest {
homeScreen { homeScreen {
// Timing issue on slow devices on Firebase // 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() verifyExistingTabList()
}.openTabsListThreeDotMenu { }.openTabsListThreeDotMenu {
@ -106,10 +111,13 @@ class TabbedBrowsingTest {
verifyTabCounter("1") verifyTabCounter("1")
}.openHomeScreen { }.openHomeScreen {
// Timing issue on slow devices on Firebase // 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() verifyExistingTabList()
verifyShareTabsButton(true) verifyShareTabsButton(true)
verifyCloseTabsButton(true) verifyCloseTabsButton("Test_Page_1")
}.togglePrivateBrowsingMode() }.togglePrivateBrowsingMode()
// Verify private tabs remain in private browsing mode // Verify private tabs remain in private browsing mode
@ -135,7 +143,10 @@ class TabbedBrowsingTest {
homeScreen { homeScreen {
// Timing issue on slow devices on Firebase // 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() verifyExistingTabList()
}.openTabsListThreeDotMenu { }.openTabsListThreeDotMenu {
verifyCloseAllTabsButton() verifyCloseAllTabsButton()
@ -147,5 +158,59 @@ class TabbedBrowsingTest {
verifyNoTabsOpenedHeader() verifyNoTabsOpenedHeader()
verifyNoTabsOpenedText() 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 package org.mozilla.fenix.ui.robots
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.test.espresso.Espresso.onView 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.BundleMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers 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.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
@ -25,11 +27,13 @@ import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.containsString
import org.junit.Assert.assertTrue
import org.mozilla.fenix.R 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.TestAssetHelper
import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.helpers.Constants.LongClickDuration
class BrowserRobot { class BrowserRobot {
@ -38,14 +42,19 @@ class BrowserRobot {
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .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()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view")), Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view")),
TestAssetHelper.waitingTime TestAssetHelper.waitingTime
) )
onView(withId(R.id.mozac_browser_toolbar_url_view)) onView(withId(R.id.mozac_browser_toolbar_url_view))
.check(matches(withText(containsString(redirectUrl)))) .check(matches(withText(containsString(url))))
} }
fun verifyHelpUrl() { fun verifyHelpUrl() {
@ -76,6 +85,10 @@ class BrowserRobot {
fun verifySnackBarText(expectedText: String) { fun verifySnackBarText(expectedText: String) {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime) mDevice.waitNotNull(Until.findObject(By.text(expectedText)), TestAssetHelper.waitingTime)
onView(withText(expectedText)).check(
matches(isCompletelyDisplayed())
)
} }
fun verifyLinkContextMenuItems(containsURL: Uri) { fun verifyLinkContextMenuItems(containsURL: Uri) {
@ -256,6 +269,18 @@ class BrowserRobot {
).perform(ViewActions.click()) ).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 { class Transition {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private fun threeDotButton() = onView( private fun threeDotButton() = onView(
@ -312,4 +337,4 @@ fun dismissTrackingOnboarding() {
fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view)) 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 package org.mozilla.fenix.ui.robots
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.graphics.Bitmap
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions 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
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.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.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.Until
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import org.hamcrest.CoreMatchers import androidx.test.uiautomator.Until
import org.hamcrest.Matchers.allOf import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Matchers.containsString import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.mozilla.fenix.R 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.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull 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. * Implementation of Robot Pattern for the home screen menu.
@ -51,6 +59,7 @@ class HomeScreenRobot {
fun verifyHomeWordmark() = assertHomeWordmark() fun verifyHomeWordmark() = assertHomeWordmark()
fun verifyHomeToolbar() = assertHomeToolbar() fun verifyHomeToolbar() = assertHomeToolbar()
fun verifyHomeComponent() = assertHomeComponent() fun verifyHomeComponent() = assertHomeComponent()
fun verifyDefaultSearchEngine(searchEngine: String) = verifySearchEngineIcon(searchEngine)
// First Run elements // First Run elements
fun verifyWelcomeHeader() = assertWelcomeHeader() fun verifyWelcomeHeader() = assertWelcomeHeader()
@ -79,41 +88,55 @@ class HomeScreenRobot {
// Private mode elements // Private mode elements
fun verifyPrivateSessionHeader() = assertPrivateSessionHeader() fun verifyPrivateSessionHeader() = assertPrivateSessionHeader()
fun verifyPrivateSessionMessage(visible: Boolean = true) = assertPrivateSessionMessage(visible) fun verifyPrivateSessionMessage(visible: Boolean = true) = assertPrivateSessionMessage(visible)
fun verifyPrivateTabsCloseTabsButton() = assertPrivateTabsCloseTabsButton()
fun verifyShareTabsButton(visible: Boolean = true) = assertShareTabsButton(visible) fun verifyShareTabsButton(visible: Boolean = true) = assertShareTabsButton(visible)
fun verifyCloseTabsButton(visible: Boolean = true) = assertCloseTabsButton(visible) fun verifyCloseTabsButton(title: String) =
assertCloseTabsButton(title)
fun verifyExistingTabList() = assertExistingTabList() fun verifyExistingTabList() = assertExistingTabList()
fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title) 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 // Collections element
fun clickCollectionThreeDotButton() { fun clickCollectionThreeDotButton() {
collectionThreeDotButton().click() collectionThreeDotButton().click()
mDevice.waitNotNull(Until.findObject(By.text("Delete collection")), waitingTime) mDevice.waitNotNull(Until.findObject(By.text("Delete collection")), waitingTime)
} }
fun selectRenameCollection() { fun selectRenameCollection() {
onView(allOf(ViewMatchers.withText("Rename collection"))).click() onView(allOf(ViewMatchers.withText("Rename collection"))).click()
mDevice.waitNotNull(Until.findObject(By.res("name_collection_edittext"))) mDevice.waitNotNull(Until.findObject(By.res("name_collection_edittext")))
} }
fun selectDeleteCollection() { fun selectDeleteCollection() {
onView(allOf(ViewMatchers.withText("Delete collection"))).click() onView(allOf(ViewMatchers.withText("Delete collection"))).click()
mDevice.waitNotNull(Until.findObject(By.res("message")), waitingTime) mDevice.waitNotNull(Until.findObject(By.res("message")), waitingTime)
} }
fun confirmDeleteCollection() { fun confirmDeleteCollection() {
onView(allOf(ViewMatchers.withText("DELETE"))).click() onView(allOf(ViewMatchers.withText("DELETE"))).click()
mDevice.waitNotNull(Until.findObject(By.res("collections_header")), waitingTime) mDevice.waitNotNull(Until.findObject(By.res("collections_header")), waitingTime)
} }
fun typeCollectionName(name: String) { fun typeCollectionName(name: String) {
mDevice.wait(Until.findObject(By.res("name_collection_edittext")), waitingTime) mDevice.wait(Until.findObject(By.res("name_collection_edittext")), waitingTime)
collectionNameTextField().perform(ViewActions.replaceText(name)) collectionNameTextField().perform(ViewActions.replaceText(name))
collectionNameTextField().perform(ViewActions.pressImeActionButton()) collectionNameTextField().perform(ViewActions.pressImeActionButton())
} }
fun scrollToElementByText(text: String): UiScrollable { fun scrollToElementByText(text: String): UiScrollable {
val appView = UiScrollable(UiSelector().scrollable(true)) val appView = UiScrollable(UiSelector().scrollable(true))
appView.scrollTextIntoView(text) appView.scrollTextIntoView(text)
return appView return appView
} }
fun swipeUpToDismissFirstRun() { fun swipeUpToDismissFirstRun() {
scrollToElementByText("Start browsing") scrollToElementByText("Start browsing")
} }
@ -124,12 +147,31 @@ class HomeScreenRobot {
fun togglePrivateBrowsingModeOnOff() { fun togglePrivateBrowsingModeOnOff() {
onView(ViewMatchers.withResourceName("privateBrowsingButton")) 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 { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@ -167,6 +209,58 @@ class HomeScreenRobot {
ThreeDotMenuMainRobot().interact() ThreeDotMenuMainRobot().interact()
return ThreeDotMenuMainRobot.Transition() 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 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
private fun navigationToolbar() = 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 closeTabButton() = onView(withId(R.id.close_tab_button))
private fun assertNavigationToolbar() = private fun assertNavigationToolbar() =
onView(CoreMatchers.allOf(withText("Search or enter address"))) onView(allOf(withText("Search or enter address")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout")) private fun assertHomeScreen() = onView(ViewMatchers.withResourceName("homeLayout"))
@ -203,50 +298,63 @@ private fun assertHomeToolbar() = onView(ViewMatchers.withResourceName("toolbar"
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertOpenTabsHeader() = private fun assertOpenTabsHeader() =
onView(CoreMatchers.allOf(withText("Open tabs"))) onView(allOf(withText("Open tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAddTabButton() = 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoTabsOpenedHeader() = private fun assertNoTabsOpenedHeader() =
onView(CoreMatchers.allOf(withText("No open tabs"))) onView(allOf(withText("No open tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoTabsOpenedText() { 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertCollectionsHeader() = private fun assertCollectionsHeader() =
onView(CoreMatchers.allOf(withText("Collections"))) onView(allOf(withText("Collections")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoCollectionsHeader() = private fun assertNoCollectionsHeader() =
onView(CoreMatchers.allOf(withText("No collections"))) onView(allOf(withText("No collections")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoCollectionsText() = private fun assertNoCollectionsText() =
onView( onView(
CoreMatchers.allOf( allOf(
withText("Collect the things that matter to you. To start, save open tabs to a new collection.") withText("Collect the things that matter to you. To start, save open tabs to a new collection.")
) )
) )
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun threeDotButton() = onView(allOf(withId(R.id.menuButton))) 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 // First Run elements
private fun assertWelcomeHeader() = private fun assertWelcomeHeader() =
onView(CoreMatchers.allOf(withText("Welcome to Firefox Preview!"))) onView(allOf(withText("Welcome to Firefox Preview!")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetTheMostHeader() = 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAccountsSignInButton() = private fun assertAccountsSignInButton() =
@ -254,15 +362,15 @@ private fun assertAccountsSignInButton() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetToKnowHeader() = 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertChooseThemeHeader() = private fun assertChooseThemeHeader() =
onView(CoreMatchers.allOf(withText("Choose your theme"))) onView(allOf(withText("Choose your theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertChooseThemeText() = 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertLightThemeToggle() = private fun assertLightThemeToggle() =
@ -270,7 +378,7 @@ private fun assertLightThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertLightThemeDescription() = private fun assertLightThemeDescription() =
onView(CoreMatchers.allOf(withText("Light theme"))) onView(allOf(withText("Light theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDarkThemeToggle() = private fun assertDarkThemeToggle() =
@ -278,7 +386,7 @@ private fun assertDarkThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDarkThemeDescription() = private fun assertDarkThemeDescription() =
onView(CoreMatchers.allOf(withText("Dark theme"))) onView(allOf(withText("Dark theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAutomaticThemeToggle() = private fun assertAutomaticThemeToggle() =
@ -286,21 +394,21 @@ private fun assertAutomaticThemeToggle() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAutomaticThemeDescription() = private fun assertAutomaticThemeDescription() =
onView(CoreMatchers.allOf(withText("Automatic"))) onView(allOf(withText("Automatic")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertProtectYourselfHeader() = private fun assertProtectYourselfHeader() =
onView(CoreMatchers.allOf(withText("Protect yourself"))) onView(allOf(withText("Protect yourself")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertTrackingProtectionToggle() = onView( private fun assertTrackingProtectionToggle() = onView(
CoreMatchers.allOf(ViewMatchers.withResourceName("tracking_protection_toggle")) allOf(ViewMatchers.withResourceName("tracking_protection_toggle"))
) )
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertProtectYourselfText() { private fun assertProtectYourselfText() {
onView( onView(
CoreMatchers.allOf( allOf(
withText( withText(
"Firefox Preview blocks ad trackers that follow you around the web." "Firefox Preview blocks ad trackers that follow you around the web."
) )
@ -310,34 +418,39 @@ private fun assertProtectYourselfText() {
} }
private fun assertBrowsePrivatelyHeader() = private fun assertBrowsePrivatelyHeader() =
onView(CoreMatchers.allOf(withText("Browse privately"))) onView(allOf(withText("Browse privately")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertBrowsePrivatelyText() = 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertYourPrivacyHeader() = private fun assertYourPrivacyHeader() =
onView(CoreMatchers.allOf(withText("Your privacy"))) onView(allOf(withText("Your privacy")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertYourPrivacyText() = private fun assertYourPrivacyText() =
onView(CoreMatchers.allOf(withText( onView(
"Weve designed Firefox Preview to give you control over what you share online and what you share with us."))) 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))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertPrivacyNoticeButton() = private fun assertPrivacyNoticeButton() =
onView(CoreMatchers.allOf(withText("Read our privacy notice"))) onView(allOf(withText("Read our privacy notice")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertStartBrowsingButton() = private fun assertStartBrowsingButton() =
onView(CoreMatchers.allOf(withText("Start browsing"))) onView(allOf(withText("Start browsing")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
// Private mode elements // Private mode elements
private fun assertPrivateSessionHeader() = private fun assertPrivateSessionHeader() =
onView(CoreMatchers.allOf(withText("Private tabs"))) onView(allOf(withText("Private tabs")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " + 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 " + "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." "who uses this device."
private fun assertPrivateSessionMessage(visible: Boolean) = private fun assertPrivateSessionMessage(visible: Boolean) =
onView(CoreMatchers.allOf(withText(PRIVATE_SESSION_MESSAGE))) onView(allOf(withText(PRIVATE_SESSION_MESSAGE)))
.check( .check(
if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else doesNotExist() if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else doesNotExist()
) )
private fun assertShareTabsButton(visible: Boolean) = 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)))) .check(matches(withEffectiveVisibility(visibleOrGone(visible))))
private fun assertCloseTabsButton(visible: Boolean) = private fun assertCloseTabsButton(title: String) =
onView(CoreMatchers.allOf(withId(R.id.close_tab_button), isDisplayed())) onView(allOf(withId(R.id.close_tab_button), withContentDescription("Close tab $title")))
.check(matches(withEffectiveVisibility(visibleOrGone(visible)))) .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() = private fun assertExistingTabList() =
onView(CoreMatchers.allOf(withId(R.id.item_tab))) onView(allOf(withId(R.id.item_tab)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertExistingOpenTabs(title: String) = private fun assertExistingOpenTabs(title: String) =
onView(withId(R.id.home_component)).perform( onView(withId(R.id.sessionControlRecyclerView)).perform(
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>( RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
ViewMatchers.hasDescendant(withText(title)) 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 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( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("SHARE A LINK") By.text("ALL ACTIONS")
), waitingTime ), waitingTime
) )
} }
@ -55,7 +55,7 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("SHARE A LINK") By.text("ALL ACTIONS")
), waitingTime ), waitingTime
) )
} }

View File

@ -10,26 +10,49 @@ import android.net.Uri
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.pressImeActionButton import androidx.test.espresso.action.ViewActions.pressImeActionButton
import androidx.test.espresso.action.ViewActions.replaceText 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.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.not
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull 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. * Implementation of Robot Pattern for the URL toolbar.
*/ */
class NavigationToolbarRobot { 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 { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun enterURLAndEnterToBrowser(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { fun enterURLAndEnterToBrowser(
mDevice.waitNotNull(Until.findObject(By.res("org.mozilla.fenix.debug:id/toolbar")), waitingTime) url: Uri,
interact: BrowserRobot.() -> Unit
): BrowserRobot.Transition {
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/toolbar")),
waitingTime
)
urlBar().click() urlBar().click()
awesomeBar().perform(replaceText(url.toString()), pressImeActionButton()) awesomeBar().perform(replaceText(url.toString()), pressImeActionButton())
@ -45,7 +68,10 @@ class NavigationToolbarRobot {
return ThreeDotMenuMainRobot.Transition() 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) mDevice.waitNotNull(Until.findObject(By.descContains("Add tab")), waitingTime)
newTab().click() newTab().click()
awesomeBar().perform(replaceText(url.toString()), pressImeActionButton()) awesomeBar().perform(replaceText(url.toString()), pressImeActionButton())
@ -54,7 +80,7 @@ class NavigationToolbarRobot {
return BrowserRobot.Transition() return BrowserRobot.Transition()
} }
fun visitLinkFromClipboard(url: Uri, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { fun visitLinkFromClipboard(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_clear_view")), Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_clear_view")),
waitingTime waitingTime
@ -62,11 +88,12 @@ class NavigationToolbarRobot {
clearAddressBar().click() clearAddressBar().click()
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject(By.text(url.toString())), waitingTime Until.findObject(By.res("org.mozilla.fenix.debug:id/clipboard_title")),
waitingTime
) )
mDevice.waitNotNull( 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 waitingTime
) )
fillLinkButton().click() fillLinkButton().click()
@ -74,6 +101,13 @@ class NavigationToolbarRobot {
BrowserRobot().interact() BrowserRobot().interact()
return BrowserRobot.Transition() 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() return NavigationToolbarRobot.Transition()
} }
private fun dismissOnboardingButton() = onView(ViewMatchers.withId(R.id.close_onboarding)) private fun assertSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) {
private fun urlBar() = onView(ViewMatchers.withId(R.id.toolbar)) mDevice.waitForIdle()
private fun awesomeBar() = onView(ViewMatchers.withId(R.id.mozac_browser_toolbar_edit_url_view)) awesomeBar().perform(typeText(searchTerm))
private fun threeDotButton() = onView(ViewMatchers.withContentDescription("Menu"))
private fun newTab() = onView(ViewMatchers.withContentDescription("Add tab")) mDevice.waitForIdle()
private fun fillLinkButton() = onView(ViewMatchers.withId(R.id.fill_link_from_clipboard)) onView(withId(R.id.awesomeBar)).check(suggestionsAreEqualTo(suggestionSize))
private fun clearAddressBar() = onView(ViewMatchers.withId(R.id.mozac_browser_toolbar_clear_view)) }
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 verifyBrowserToolbar() = assertBrowserToolbarEditView()
fun verifyScanButton() = assertScanButton() fun verifyScanButton() = assertScanButton()
fun verifySearchWithText() = assertSearchWithText() fun verifySearchWithText() = assertSearchWithText()
fun verifyDuckDuckGoResults() = assertDuckDuckGoResults() fun verifySearchEngineResults(searchEngineName: String) =
fun verifyDuckDuckGoURL() = assertDuckDuckGoURL() assertSearchEngineResults(searchEngineName)
fun verifySearchEngineURL(searchEngineName: String) = assertSearchEngineURL(searchEngineName)
fun verifySearchSettings() = assertSearchSettings() fun verifySearchSettings() = assertSearchSettings()
fun verifySearchBarEmpty() = assertSearchBarEmpty() fun verifySearchBarEmpty() = assertSearchBarEmpty()
@ -60,13 +62,21 @@ class SearchRobot {
browserToolbarEditView().perform(typeText(searchTerm)) browserToolbarEditView().perform(typeText(searchTerm))
} }
fun clickDuckDuckGoEngineButton() { fun clickSearchEngineButton(searchEngineName: String) {
duckDuckGoEngineButton().perform(click()) searchEngineButton(searchEngineName).perform(click())
} }
fun clickDuckDuckGoResult() { fun clickSearchEngineResult(searchEngineName: String) {
mDevice.waitNotNull(Until.findObjects(By.text("DuckDuckGo")), TestAssetHelper.waitingTime) mDevice.waitNotNull(
awesomeBar().perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click())) Until.findObjects(By.text(searchEngineName)),
TestAssetHelper.waitingTime
)
awesomeBar().perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
0,
click()
)
)
} }
fun scrollToSearchEngineSettings(): UiScrollable { fun scrollToSearchEngineSettings(): UiScrollable {
@ -88,7 +98,7 @@ class SearchRobot {
fun openBrowser(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { fun openBrowser(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitForIdle() mDevice.waitForIdle()
browserToolbarEditView().perform(typeText("Mozilla\n")) browserToolbarEditView().perform(typeText("mozilla\n"))
BrowserRobot().interact() BrowserRobot().interact()
return BrowserRobot.Transition() return BrowserRobot.Transition()
@ -98,11 +108,12 @@ class SearchRobot {
private fun awesomeBar() = onView(withId(R.id.awesomeBar)) 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 { private fun searchEngineButton(searchEngineName: String): ViewInteraction {
mDevice.waitNotNull(Until.findObject(By.text("DuckDuckGo")), TestAssetHelper.waitingTime) mDevice.waitNotNull(Until.findObject(By.text(searchEngineName)), TestAssetHelper.waitingTime)
return onView(Matchers.allOf(withText("DuckDuckGo"))) return onView(Matchers.allOf(withText(searchEngineName)))
} }
private fun denyPermissionButton(): UiObject { 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 clearButton() = onView(withId(R.id.mozac_browser_toolbar_clear_view))
private fun assertDuckDuckGoURL() { private fun assertSearchEngineURL(searchEngineName: String) {
mDevice.waitNotNull(Until.findObject(By.textContains("https://duckduckgo.com/?q=mozilla")), TestAssetHelper.waitingTime) mDevice.waitNotNull(
onView(allOf(withText(startsWith("https://duckduckgo.com")))) 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))) .check(matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
} }
private fun assertDuckDuckGoResults() { private fun assertSearchEngineResults(searchEngineName: String) {
val count = mDevice.wait(Until.findObjects(By.text(("DuckDuckGo"))), TestAssetHelper.waitingTime) val count =
mDevice.wait(Until.findObjects(By.text((searchEngineName))), TestAssetHelper.waitingTime)
assert(count.size > 1) 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.intent.matcher.IntentMatchers.toPackage
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
@ -35,10 +36,10 @@ import org.mozilla.fenix.helpers.click
class SettingsRobot { class SettingsRobot {
// BASICS SECTION // BASICS SECTION
fun verifyBasicsHeading() = assertBasicsHeading() fun verifyBasicsHeading() = assertGeneralHeading()
fun verifySearchEngineButton() = assertSearchEngineButton() fun verifySearchEngineButton() = assertSearchEngineButton()
fun verifyThemeButton() = assertThemeButton() fun verifyThemeButton() = assertCustomizeButton()
fun verifyThemeSelected() = assertThemeSelected() fun verifyThemeSelected() = assertThemeSelected()
fun verifyAccessibilityButton() = assertAccessibilityButton() fun verifyAccessibilityButton() = assertAccessibilityButton()
fun verifySetAsDefaultBrowserButton() = assertSetAsDefaultBrowserButton() fun verifySetAsDefaultBrowserButton() = assertSetAsDefaultBrowserButton()
@ -51,7 +52,7 @@ class SettingsRobot {
fun verifyEnhancedTrackingProtectionValue(state: String) = fun verifyEnhancedTrackingProtectionValue(state: String) =
assertEnhancedTrackingProtectionValue(state) assertEnhancedTrackingProtectionValue(state)
fun verifyAddPrivateBrowsingShortcutButton() = assertAddPrivateBrowsingShortcutButton() fun verifyAddPrivateBrowsingShortcutButton() = assertPrivateBrowsingButton()
fun verifySitePermissionsButton() = assertSitePermissionsButton() fun verifySitePermissionsButton() = assertSitePermissionsButton()
fun verifyDeleteBrowsingDataButton() = assertDeleteBrowsingDataButton() fun verifyDeleteBrowsingDataButton() = assertDeleteBrowsingDataButton()
fun verifyDeleteBrowsingDataOnQuitButton() = assertDeleteBrowsingDataOnQuitButton() fun verifyDeleteBrowsingDataOnQuitButton() = assertDeleteBrowsingDataOnQuitButton()
@ -101,10 +102,10 @@ class SettingsRobot {
return SettingsSubMenuSearchRobot.Transition() return SettingsSubMenuSearchRobot.Transition()
} }
fun openThemeSubMenu(interact: SettingsSubMenuThemeRobot.() -> Unit): SettingsSubMenuThemeRobot.Transition { fun openCustomizeSubMenu(interact: SettingsSubMenuThemeRobot.() -> Unit): SettingsSubMenuThemeRobot.Transition {
fun themeButton() = onView(ViewMatchers.withText("Theme")) fun customizeButton() = onView(ViewMatchers.withText("Customize"))
themeButton().click() customizeButton().click()
SettingsSubMenuThemeRobot().interact() SettingsSubMenuThemeRobot().interact()
return SettingsSubMenuThemeRobot.Transition() return SettingsSubMenuThemeRobot.Transition()
@ -145,19 +146,27 @@ class SettingsRobot {
SettingsSubMenuLoginsAndPasswordRobot().interact() SettingsSubMenuLoginsAndPasswordRobot().interact()
return SettingsSubMenuLoginsAndPasswordRobot.Transition() 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() { private fun assertSettingsView() {
// verify that we are in the correct library view // verify that we are in the correct library view
assertBasicsHeading() assertGeneralHeading()
assertPrivacyHeading() assertPrivacyHeading()
assertDeveloperToolsHeading() assertDeveloperToolsHeading()
assertAboutHeading() assertAboutHeading()
} }
// BASICS SECTION // GENERAL SECTION
private fun assertBasicsHeading() = onView(ViewMatchers.withText("Basics")) private fun assertGeneralHeading() = onView(ViewMatchers.withText("General"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertSearchEngineButton() { private fun assertSearchEngineButton() {
@ -166,7 +175,7 @@ private fun assertSearchEngineButton() {
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertThemeButton() = onView(ViewMatchers.withText("Theme")) private fun assertCustomizeButton() = onView(ViewMatchers.withText("Customize"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertThemeSelected() = onView(ViewMatchers.withText("Light")) private fun assertThemeSelected() = onView(ViewMatchers.withText("Light"))
@ -181,7 +190,7 @@ private fun assertSetAsDefaultBrowserButton() =
// PRIVACY SECTION // PRIVACY SECTION
private fun assertPrivacyHeading() { private fun assertPrivacyHeading() {
onView(ViewMatchers.withText("Privacy")) onView(ViewMatchers.withText("Privacy and security"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
@ -202,10 +211,10 @@ private fun assertLoginsButton() {
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertAddPrivateBrowsingShortcutButton() { private fun assertPrivateBrowsingButton() {
TestHelper.scrollToElementByText("Add private browsing shortcut") TestHelper.scrollToElementByText("Private browsing")
mDevice.wait(Until.findObject(By.text("Add private browsing shortcut")), waitingTime) mDevice.wait(Until.findObject(By.text("Private browsing")), waitingTime)
onView(ViewMatchers.withText("Add private browsing shortcut")) onView(ViewMatchers.withText("Private browsing"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
@ -250,19 +259,19 @@ private fun assertRemoteDebug() {
private fun assertAboutHeading(): ViewInteraction { private fun assertAboutHeading(): ViewInteraction {
TestHelper.scrollToElementByText("About") TestHelper.scrollToElementByText("About")
return onView(ViewMatchers.withText("About")) return onView(ViewMatchers.withText("About"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(isCompletelyDisplayed()))
} }
private fun assertRateOnGooglePlay(): ViewInteraction { private fun assertRateOnGooglePlay(): ViewInteraction {
TestHelper.scrollToElementByText("About Firefox Preview") TestHelper.scrollToElementByText("About Firefox Preview")
return onView(ViewMatchers.withText("Rate on Google Play")) return onView(ViewMatchers.withText("Rate on Google Play"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(isCompletelyDisplayed()))
} }
private fun assertAboutFirefoxPreview(): ViewInteraction { private fun assertAboutFirefoxPreview(): ViewInteraction {
TestHelper.scrollToElementByText("About Firefox Preview") TestHelper.scrollToElementByText("About Firefox Preview")
return onView(ViewMatchers.withText("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()) fun swipeToBottom() = onView(ViewMatchers.withId(R.id.recycler_view)).perform(ViewActions.swipeUp())

View File

@ -6,27 +6,67 @@
package org.mozilla.fenix.ui.robots package org.mozilla.fenix.ui.robots
import androidx.test.espresso.Espresso 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 import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice 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. * Implementation of Robot Pattern for the settings Accessibility sub menu.
*/ */
class SettingsSubMenuAccessibilityRobot { 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 { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
mDevice.waitForIdle() mDevice.waitForIdle()
goBackButton().perform(ViewActions.click()) goBackButton().perform(click())
SettingsRobot().interact() SettingsRobot().interact()
return SettingsRobot.Transition() return SettingsRobot.Transition()
@ -34,13 +74,127 @@ class SettingsSubMenuAccessibilityRobot {
} }
} }
private fun assertAutomaticFontSizing() { val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
Espresso.onView(ViewMatchers.withText("Automatic Font Sizing"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) 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." val strFont = "Font size will match your Android settings. Disable to manage font size here."
Espresso.onView(ViewMatchers.withText(strFont)) onView(withText(strFont))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .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() = 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.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers 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.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice 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. * Implementation of Robot Pattern for the settings DefaultBrowser sub menu.
*/ */
class SettingsSubMenuDefaultBrowserRobot { class SettingsSubMenuDefaultBrowserRobot {
companion object {
const val DEFAULT_APPS_SETTINGS_ACTION = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"
}
fun verifyOpenLinksInPrivateTab() = assertOpenLinksInPrivateTab() fun verifyOpenLinksInPrivateTab() = assertOpenLinksInPrivateTab()
fun verifyDefaultBrowserIsDisabled() = assertDefaultBrowserIsDisabled()
fun clickDefaultBrowserSwitch() = toggleDefaultBrowserSwitch()
fun verifyAndroidDefaultAppsMenuAppears() = assertAndroidDefaultAppsMenuAppears()
class Transition { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) 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() { private fun assertOpenLinksInPrivateTab() {
onView(ViewMatchers.withText("Open links in private tab")) onView(withText("Open links in private tab"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun goBackButton() = 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 package org.mozilla.fenix.ui.robots
import android.view.View
import androidx.test.espresso.Espresso.onView 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.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withResourceName import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers 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.click
import org.mozilla.fenix.helpers.isChecked import org.mozilla.fenix.helpers.isChecked
@ -38,8 +43,26 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot {
return SettingsRobot.Transition() return SettingsRobot.Transition()
} }
fun openExceptions(interact: SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.() -> Unit): SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.Transition { fun openExceptions(
openExceptions().click() 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() SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot().interact()
return SettingsSubMenuEnhancedTrackingProtectionExceptionsRobot.Transition() 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() SettingsTurnOnSyncRobot().interact()
return SettingsTurnOnSyncRobot.Transition() 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.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
@ -29,6 +30,11 @@ class SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot {
fun tapSetupLater() = onView(ViewMatchers.withText("Later")).perform(ViewActions.click()) 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 { class Transition {
fun goBack(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordRobot.Transition { fun goBack(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordRobot.Transition {
goBackButton().perform(ViewActions.click()) goBackButton().perform(ViewActions.click())

View File

@ -8,14 +8,15 @@ package org.mozilla.fenix.ui.robots
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView 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.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions 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.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers
@ -31,13 +32,17 @@ class SettingsSubMenuSearchRobot {
fun verifyShowClipboardSuggestions() = assertShowClipboardSuggestions() fun verifyShowClipboardSuggestions() = assertShowClipboardSuggestions()
fun verifySearchBrowsingHistory() = assertSearchBrowsingHistory() fun verifySearchBrowsingHistory() = assertSearchBrowsingHistory()
fun verifySearchBookmarks() = assertSearchBookmarks() fun verifySearchBookmarks() = assertSearchBookmarks()
fun changeDefaultSearchEngine(searchEngineName: String) =
selectDefaultSearchEngine(searchEngineName)
fun disableShowSearchSuggestions() = toggleShowSearchSuggestions()
class Transition { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
mDevice.waitForIdle() mDevice.waitForIdle()
goBackButton().perform(ViewActions.click()) goBackButton().perform(click())
SettingsRobot().interact() SettingsRobot().interact()
return SettingsRobot.Transition() return SettingsRobot.Transition()
@ -46,24 +51,24 @@ class SettingsSubMenuSearchRobot {
} }
private fun assertDefaultSearchEngineHeader() = private fun assertDefaultSearchEngineHeader() =
onView(ViewMatchers.withText("Default search engine")) onView(withText("Default search engine"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertSearchEngineList() { private fun assertSearchEngineList() {
onView(ViewMatchers.withText("Google")) onView(withText("Google"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("Amazon.com")) onView(withText("Amazon.com"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("Bing")) onView(withText("Bing"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("DuckDuckGo")) onView(withText("DuckDuckGo"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("Twitter")) onView(withText("Twitter"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("Wikipedia")) onView(withText("Wikipedia"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
onView(ViewMatchers.withText("Add search engine")) onView(withText("Add search engine"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertShowSearchSuggestions() { private fun assertShowSearchSuggestions() {
@ -72,8 +77,8 @@ private fun assertShowSearchSuggestions() {
hasDescendant(withText("Show search suggestions")) hasDescendant(withText("Show search suggestions"))
) )
) )
onView(ViewMatchers.withText("Show search suggestions")) onView(withText("Show search suggestions"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertShowSearchShortcuts() { private fun assertShowSearchShortcuts() {
@ -82,8 +87,8 @@ private fun assertShowSearchShortcuts() {
hasDescendant(withText("Show search shortcuts")) hasDescendant(withText("Show search shortcuts"))
) )
) )
onView(ViewMatchers.withText("Show search shortcuts")) onView(withText("Show search shortcuts"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertShowClipboardSuggestions() { private fun assertShowClipboardSuggestions() {
@ -92,8 +97,8 @@ private fun assertShowClipboardSuggestions() {
hasDescendant(withText("Show clipboard suggestions")) hasDescendant(withText("Show clipboard suggestions"))
) )
) )
onView(ViewMatchers.withText("Show clipboard suggestions")) onView(withText("Show clipboard suggestions"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertSearchBrowsingHistory() { private fun assertSearchBrowsingHistory() {
@ -102,8 +107,8 @@ private fun assertSearchBrowsingHistory() {
hasDescendant(withText("Search browsing history")) hasDescendant(withText("Search browsing history"))
) )
) )
onView(ViewMatchers.withText("Search browsing history")) onView(withText("Search browsing history"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertSearchBookmarks() { private fun assertSearchBookmarks() {
@ -112,9 +117,30 @@ private fun assertSearchBookmarks() {
hasDescendant(withText("Search bookmarks")) hasDescendant(withText("Search bookmarks"))
) )
) )
onView(ViewMatchers.withText("Search bookmarks")) onView(withText("Search bookmarks"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .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() = 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 package org.mozilla.fenix.ui.robots
import android.os.Build
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
@ -25,7 +27,8 @@ class SettingsSubMenuThemeRobot {
fun verifyThemes() = assertThemes() 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) fun verifyDarkThemeApplied(expected: Boolean) = assertTrue("Dark theme not selected", expected)
@ -47,18 +50,23 @@ class SettingsSubMenuThemeRobot {
} }
private fun assertThemes() { private fun assertThemes() {
onView(withText("Light")) lightModeToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .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))) .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 darkModeToggle() = onView(withText("Dark"))
private fun lightModeToggle() = onView(withText("Light")) 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 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.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.hasFocus import androidx.test.espresso.matcher.ViewMatchers.hasFocus
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withResourceName import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
@ -43,9 +48,11 @@ class ThreeDotMenuMainRobot {
fun verifyRefreshButton() = assertRefreshButton() fun verifyRefreshButton() = assertRefreshButton()
fun verifyCloseAllTabsButton() = assertCloseAllTabsButton() fun verifyCloseAllTabsButton() = assertCloseAllTabsButton()
fun verifyShareButton() = assertShareButton() fun verifyShareButton() = assertShareButton()
fun verifyReaderViewToggle(visible: Boolean) = assertReaderViewToggle(visible)
fun verifyReaderViewAppearance(visible: Boolean) = assertReaderViewAppearanceButton(visible)
fun clickShareButton() { fun clickShareButton() {
shareButton().click() shareButton().click()
mDevice.waitNotNull(Until.findObject(By.text("SHARE A LINK")), waitingTime) mDevice.waitNotNull(Until.findObject(By.text("ALL ACTIONS")), waitingTime)
} }
fun verifyShareTabButton() = assertShareTabButton() fun verifyShareTabButton() = assertShareTabButton()
@ -72,6 +79,7 @@ class ThreeDotMenuMainRobot {
fun verifySendToDeviceTitle() = assertSendToDeviceTitle() fun verifySendToDeviceTitle() = assertSendToDeviceTitle()
fun verifyShareALinkTitle() = assertShareALinkTitle() fun verifyShareALinkTitle() = assertShareALinkTitle()
fun verifyWhatsNewButton() = assertWhatsNewButton() fun verifyWhatsNewButton() = assertWhatsNewButton()
fun verifyAddFirefoxHome() = assertAddToFirefoxHome()
class Transition { class Transition {
@ -182,11 +190,41 @@ class ThreeDotMenuMainRobot {
return BrowserRobot.Transition() return BrowserRobot.Transition()
} }
fun typeCollectionName(name: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { fun typeCollectionName(
mDevice.wait(Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")), waitingTime) 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().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() BrowserRobot().interact()
return BrowserRobot.Transition() return BrowserRobot.Transition()
@ -198,8 +236,13 @@ private fun threeDotMenuRecyclerViewExists() {
onView(withId(R.id.mozac_browser_menu_recyclerView)).check(matches(isDisplayed())) onView(withId(R.id.mozac_browser_menu_recyclerView)).check(matches(isDisplayed()))
} }
private fun settingsButton() = onView(allOf(withText(R.string.settings), private fun settingsButton() = onView(
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) allOf(
withText(R.string.settings),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun assertSettingsButton() = settingsButton() private fun assertSettingsButton() = settingsButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
@ -247,8 +290,12 @@ private fun shareButton() = onView(ViewMatchers.withContentDescription("Share"))
private fun assertShareButton() = shareButton() private fun assertShareButton() = shareButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun browserViewSaveCollectionButton() = onView(allOf(withText("Save to Collection"), private fun browserViewSaveCollectionButton() = onView(
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) allOf(
withText("Save to Collection"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun saveCollectionButton() = onView(allOf(withText("Save to collection"))) private fun saveCollectionButton() = onView(allOf(withText("Save to collection")))
private fun assertSaveCollectionButton() = saveCollectionButton() private fun assertSaveCollectionButton() = saveCollectionButton()
@ -276,13 +323,41 @@ private fun assertSendToDeviceTitle() = SendToDeviceTitle()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun ShareALinkTitle() = 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 assertShareALinkTitle() = ShareALinkTitle()
private fun whatsNewButton() = onView( private fun whatsNewButton() = onView(
allOf( allOf(
withText("Whats New"), withText("Whats New"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
)
)
private fun assertWhatsNewButton() = whatsNewButton() private fun assertWhatsNewButton() = whatsNewButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .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