Merge pull request #393 from fork-maintainers/upstream-sync
Sync with Mozilla Firefox v92.1.1pull/420/head iceraven-1.13.0
commit
8b1abfbbf0
@ -1,23 +0,0 @@
|
||||
---
|
||||
name: "\U0001F41E Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug]"
|
||||
labels: "\U0001F41E bug"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
[comment]: # (Please do your best to search for duplicate issues before filing a new issue so we can keep our issue board clean)
|
||||
[comment]: # (Every issue should have the exact bug and steps to reproduce described in it. Please do not file feedback list tickets as it is difficult to parse them and address their individual points)
|
||||
[comment]: # (Read https://github.com/mozilla-mobile/fenix#i-want-to-file-an-issue for more information)
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Device information
|
||||
|
||||
* Device vendor / model and Android version: ?
|
||||
* Firefox for Android version: ? (go to Settings -> About Firefox)
|
||||
|
@ -0,0 +1,91 @@
|
||||
name: "\U0001F41E Bug report"
|
||||
description: Create a report to help us improve.
|
||||
title: "[Bug]: "
|
||||
labels: ["\U0001F41E bug", "needs:triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Please do your best to search for duplicate issues before filing a new issue so we can keep our issue board clean.
|
||||
- Have a look at ["I want to file an issue!"][info] for more information.
|
||||
- Read the [Community Participation Guidelines][guidelines] and the [Bugzilla Etiquette guidelines][bugzilla] before filing an issue.
|
||||
|
||||
[info]: https://github.com/mozilla-mobile/fenix#i-want-to-file-an-issue
|
||||
[guidelines]: https://www.mozilla.org/en-US/about/governance/policies/participation/
|
||||
[bugzilla]: https://bugzilla.mozilla.org/page.cgi?id=etiquette.html
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Steps to reproduce the behaviour.
|
||||
placeholder: |
|
||||
1. Have a tab open..
|
||||
2. Go to..
|
||||
3. Click on..
|
||||
4. Observe..
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
placeholder: A menu should open..
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual behaviour
|
||||
placeholder: The app closes unexpectedly..
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Device information
|
||||
- type: input
|
||||
attributes:
|
||||
label: Device name
|
||||
description: The name of the device model and manufacturer.
|
||||
placeholder: Google Pixel 2
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Android version
|
||||
description: You can find the Android version information in the About section of your device's system settings.
|
||||
placeholder: Android 10
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Firefox release type
|
||||
description: You can find this information in Settings -> About Firefox.
|
||||
options:
|
||||
- Firefox Nightly
|
||||
- Firefox Beta
|
||||
- Firefox
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Firefox version
|
||||
description: You can find this information in Settings -> About Firefox.
|
||||
placeholder: 89.0.10
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Device logs
|
||||
description: |
|
||||
Device logs or crash information can greatly aid in debugging. You can find some details here on how to [retrieve device logs or crash IDs][log].
|
||||
|
||||
[log]: https://github.com/mozilla-mobile/fenix/wiki/Logging-Crash-Information
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: |
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
||||
validations:
|
||||
required: false
|
@ -0,0 +1,66 @@
|
||||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This comment contains the central documentation for how we configured Jetpack Benchmark. Currently:
|
||||
// - microbenchmark: configured differently than recommended (see inline notes below)
|
||||
// - macrobenchmark: not configured
|
||||
//
|
||||
// To run our benchmarks, you need to set the "benchmark" gradle property. You can:
|
||||
// - (preferred) Run via the command line (change the class you run on):
|
||||
// ./gradlew -Pbenchmark app:connectedCheck -Pandroid.testInstrumentationRunnerArguments.class=org.mozilla.fenix.perf.SampleBenchmark
|
||||
// - Use the IDE. Temporarily set the "benchmark" property in app/build.gradle with "ext.benchmark=true"
|
||||
// near the top of the file. DO NOT COMMIT THIS.
|
||||
// - (note: I was unable to get IDE run configurations working)
|
||||
//
|
||||
// To get the results, look at this file (we recommend using the median; results are in nanoseconds):
|
||||
// app/build/outputs/connected_android_test_additional_output/nightlyAndroidTest/connected/<device>/org.mozilla.fenix-benchmarkData.json
|
||||
//
|
||||
// I was unable to get the results to print directly in Android Studio (perhaps it's my device).
|
||||
//
|
||||
// The official documentation suggests configuring microbenchmark in a separate module. This would
|
||||
// require any benchmarked code to be in a library module, not the :app module (see below). To avoid
|
||||
// this requirement, we created the "benchmark" gradle property.
|
||||
//
|
||||
// For the most accurate results, the documentation recommends running tests on rooted devices with
|
||||
// the CPU clock locked.
|
||||
//
|
||||
// See https://developer.android.com/studio/profile/benchmark#what-to-benchmark for when writing a
|
||||
// jetpack microbenchmark is a good fit.
|
||||
|
||||
// I think `android` represents this object:
|
||||
// https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.AppExtension.html
|
||||
ext.maybeConfigForJetpackBenchmark = { android ->
|
||||
if (!project.hasProperty("benchmark")) {
|
||||
return
|
||||
}
|
||||
|
||||
// The official documentation https://developer.android.com/studio/profile/benchmark#full-setup
|
||||
// recommends setting up the Microbenchmark library in a separate module from your app: AFAICT,
|
||||
// the reason for this is to prevent the benchmarks from being configured against debug
|
||||
// builds. We chose not to do this because it's a lot of work to pull code out into a
|
||||
// separate module just to benchmark it. We were able to replicate the outcome by setting
|
||||
// this testBuildType property.
|
||||
android.testBuildType "nightly"
|
||||
|
||||
// WARNING: our proguard configuration for androidTest is not set up correctly so the tests
|
||||
// fail if we don't disable minification. DISABLING MINIFICATION PRODUCES BENCHMARKS THAT ARE
|
||||
// LESS REPRESENTATIVE TO THE USER EXPERIENCE, however, so we made this tradeoff to reduce
|
||||
// implementation time.
|
||||
project.ext.disableOptimization = true
|
||||
|
||||
android.defaultConfig {
|
||||
// WARNING: the benchmark framework warns you if you're running the test in a configuration
|
||||
// that will compromise the accuracy of the results. Unfortunately, I couldn't get everything
|
||||
// working so I had to suppress some things.
|
||||
//
|
||||
// - ACTIVITY-MISSING: we're supposed to use the test instrumentation runner,
|
||||
// "androidx.benchmark.junit4.AndroidBenchmarkRunner". However, when I do so, I get an error
|
||||
// that we're unable to launch the activity. My understanding is that this runner will use an
|
||||
// "IsolationActivity" to reduce the impact of other work on the device from affecting the benchmark
|
||||
// and to opt into a lower-max CPU frequency on unrooted devices that support it
|
||||
// - UNLOCKED: ./gradlew lockClocks, which locks the CPU frequency, fails on my device. See
|
||||
// https://issuetracker.google.com/issues/176836267 for potential workarounds.
|
||||
testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'ACTIVITY-MISSING,UNLOCKED'
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -1,120 +0,0 @@
|
||||
####################################################################################################
|
||||
# Copy of `proguard-android-optimize.txt`.
|
||||
# We need to _remove_ some of the configuration, so the only way to achieve that is to dump it here
|
||||
# and modify it.
|
||||
####################################################################################################
|
||||
|
||||
# This is a configuration file for ProGuard.
|
||||
# http://proguard.sourceforge.net/index.html#manual/usage.html
|
||||
#
|
||||
# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with
|
||||
# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and
|
||||
# will be ignored by new version of the Android plugin for Gradle.
|
||||
|
||||
# Optimizations: If you don't want to optimize, use the proguard-android.txt configuration file
|
||||
# instead of this one, which turns off the optimization flags.
|
||||
# Adding optimization introduces certain risks, since for example not all optimizations performed by
|
||||
# ProGuard works on all versions of Dalvik. The following flags turn off various optimizations
|
||||
# known to have issues, but the list may not be complete or up to date. (The "arithmetic"
|
||||
# optimization can be used if you are only targeting Android 2.0 or later.) Make sure you test
|
||||
# thoroughly if you go this route.
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-verbose
|
||||
|
||||
# Preserve some attributes that may be required for reflection.
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
|
||||
-keep public class com.google.vending.licensing.ILicensingService
|
||||
-keep public class com.android.vending.licensing.ILicensingService
|
||||
-keep public class com.google.android.vending.licensing.ILicensingService
|
||||
-dontnote com.android.vending.licensing.ILicensingService
|
||||
-dontnote com.google.vending.licensing.ILicensingService
|
||||
-dontnote com.google.android.vending.licensing.ILicensingService
|
||||
|
||||
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# Keep setters in Views so that animations can still work.
|
||||
-keepclassmembers public class * extends android.view.View {
|
||||
void set*(***);
|
||||
*** get*();
|
||||
}
|
||||
|
||||
# We want to keep methods in Activity that could be used in the XML attribute onClick.
|
||||
-keepclassmembers class * extends android.app.Activity {
|
||||
public void *(android.view.View);
|
||||
}
|
||||
|
||||
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
-keepclassmembers class **.R$* {
|
||||
public static <fields>;
|
||||
}
|
||||
|
||||
# Preserve annotated Javascript interface methods.
|
||||
-keepclassmembers class * {
|
||||
@android.webkit.JavascriptInterface <methods>;
|
||||
}
|
||||
|
||||
# The support libraries contains references to newer platform versions.
|
||||
# Don't warn about those in case this app is linking against an older
|
||||
# platform version. We know about them, and they are safe.
|
||||
-dontnote android.support.**
|
||||
-dontnote androidx.**
|
||||
-dontwarn android.support.**
|
||||
-dontwarn androidx.**
|
||||
|
||||
# This class is deprecated, but remains for backward compatibility.
|
||||
-dontwarn android.util.FloatMath
|
||||
|
||||
# Understand the @Keep support annotation.
|
||||
-keep class android.support.annotation.Keep
|
||||
-keep class androidx.annotation.Keep
|
||||
|
||||
-keep @android.support.annotation.Keep class * {*;}
|
||||
-keep @androidx.annotation.Keep class * {*;}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@android.support.annotation.Keep <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@android.support.annotation.Keep <fields>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <fields>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@android.support.annotation.Keep <init>(...);
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <init>(...);
|
||||
}
|
||||
|
||||
# These classes are duplicated between android.jar and org.apache.http.legacy.jar.
|
||||
-dontnote org.apache.http.**
|
||||
-dontnote android.net.http.**
|
||||
|
||||
# These classes are duplicated between android.jar and core-lambda-stubs.jar.
|
||||
-dontnote java.lang.invoke.**
|
@ -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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.perf
|
||||
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* To run this benchmark:
|
||||
* - Comment out @Ignore: DO NOT COMMIT THIS!
|
||||
* - See run instructions in app/benchmark.gradle
|
||||
*
|
||||
* See https://developer.android.com/studio/profile/benchmark#write-benchmark for how to write a
|
||||
* real benchmark, including testing UI code. See
|
||||
* https://developer.android.com/studio/profile/benchmark#what-to-benchmark for when jetpack
|
||||
* microbenchmark is a good fit.
|
||||
*/
|
||||
@Ignore("This is a sample: we don't want it to run when we run all the tests")
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SampleBenchmark {
|
||||
@get:Rule
|
||||
val benchmarkRule = BenchmarkRule()
|
||||
|
||||
@Test
|
||||
fun additionBenchmark() = benchmarkRule.measureRepeated {
|
||||
var i = 0
|
||||
while (i < 10_000_000) {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,262 @@
|
||||
/* 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.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getGenericAsset
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.tabDrawer
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of tab collections
|
||||
*
|
||||
*/
|
||||
|
||||
class CollectionTest {
|
||||
/* ktlint-disable no-blank-line-before-rbrace */
|
||||
// This imposes unreadable grouping.
|
||||
|
||||
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val firstCollectionName = "testcollection_1"
|
||||
private val secondCollectionName = "testcollection_2"
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityTestRule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
// open a webpage, and add currently opened tab to existing collection
|
||||
fun mainMenuSaveToExistingCollection() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(firstWebPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
verifyPageContent(secondWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
}.selectExistingCollection(firstCollectionName) {
|
||||
verifySnackBarText("Tab saved!")
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
verifyTabSavedInCollection(firstWebPage.title)
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAddTabButtonOfCollectionMenu() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(firstWebPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
closeTab()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
clickCollectionThreeDotButton()
|
||||
selectAddTabToCollection()
|
||||
verifyTabsSelectedCounterText(1)
|
||||
saveTabsSelectedForCollection()
|
||||
verifySnackBarText("Tab saved!")
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renameCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(webPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
clickCollectionThreeDotButton()
|
||||
selectRenameCollection()
|
||||
}.typeCollectionNameAndSave("renamed_collection") {}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed("renamed_collection")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createSecondCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(webPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
createCollection(webPage.title, secondCollectionName, false)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(firstCollectionName)
|
||||
verifyCollectionIsDisplayed(secondCollectionName)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeTabFromCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(webPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
removeTabFromCollection(webPage.title)
|
||||
verifyTabSavedInCollection(webPage.title, false)
|
||||
}
|
||||
// To add this step when https://github.com/mozilla-mobile/fenix/issues/13177 is fixed
|
||||
// homeScreen {
|
||||
// verifyCollectionIsDisplayed(firstCollectionName, false)
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/20702")
|
||||
fun swipeToRemoveTabFromCollectionTest() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(firstWebPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
closeTab()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
}.selectExistingCollection(firstCollectionName) {
|
||||
}.goToHomescreen {}
|
||||
|
||||
homeScreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
swipeCollectionItemLeft(firstWebPage.title)
|
||||
verifyTabSavedInCollection(firstWebPage.title, false)
|
||||
swipeCollectionItemRight(secondWebPage.title)
|
||||
verifyTabSavedInCollection(secondWebPage.title, false)
|
||||
}
|
||||
// To add this step when https://github.com/mozilla-mobile/fenix/issues/13177 is fixed
|
||||
// homeScreen {
|
||||
// verifyCollectionIsDisplayed(firstCollectionName, false)
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun selectTabOnLongTapTest() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openTabDrawer {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebPage.url.toString()) {
|
||||
}.openTabDrawer {
|
||||
longClickTab(firstWebPage.title)
|
||||
verifyTabsMultiSelectionCounter(1)
|
||||
selectTab(secondWebPage.title)
|
||||
verifyTabsMultiSelectionCounter(2)
|
||||
}.clickSaveCollection {
|
||||
typeCollectionNameAndSave(firstCollectionName)
|
||||
verifySnackBarText("Tabs saved!")
|
||||
}
|
||||
|
||||
tabDrawer {
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(firstCollectionName) {
|
||||
verifyTabSavedInCollection(firstWebPage.title)
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun navigateBackInCollectionFlowTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openTabDrawer {
|
||||
createCollection(webPage.title, firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
verifySelectCollectionScreen()
|
||||
goBackInCollectionFlow()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
verifySelectCollectionScreen()
|
||||
clickAddNewCollection()
|
||||
verifyCollectionNameTextField()
|
||||
goBackInCollectionFlow()
|
||||
verifySelectCollectionScreen()
|
||||
goBackInCollectionFlow()
|
||||
}
|
||||
// verify the browser layout is visible
|
||||
browserScreen {
|
||||
verifyMenuButton()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,266 +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.espresso.NoMatchingViewException
|
||||
//import androidx.test.platform.app.InstrumentationRegistry
|
||||
//import androidx.test.uiautomator.By
|
||||
//import androidx.test.uiautomator.UiDevice
|
||||
//import androidx.test.uiautomator.Until
|
||||
//import okhttp3.mockwebserver.MockWebServer
|
||||
//import org.junit.After
|
||||
//import org.junit.Before
|
||||
//import org.junit.Ignore
|
||||
//import org.junit.Rule
|
||||
//import org.junit.Test
|
||||
//import org.mozilla.fenix.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 tab collection
|
||||
// *
|
||||
// */
|
||||
//
|
||||
//class CollectionTest {
|
||||
// /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||
//
|
||||
// private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
// private lateinit var mockWebServer: MockWebServer
|
||||
// private val firstCollectionName = "testcollection_1"
|
||||
// private val secondCollectionName = "testcollection_2"
|
||||
//
|
||||
// @get:Rule
|
||||
// val activityTestRule = HomeActivityTestRule()
|
||||
//
|
||||
// @Before
|
||||
// fun setUp() {
|
||||
// mockWebServer = MockWebServer().apply {
|
||||
// setDispatcher(AndroidAssetDispatcher())
|
||||
// start()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @After
|
||||
// fun tearDown() {
|
||||
// mockWebServer.shutdown()
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// // open a webpage, and add currently opened tab to existing collection
|
||||
// fun addTabToExistingCollectionTest() {
|
||||
// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
//
|
||||
// homeScreen {
|
||||
//// verifyExistingTabList()
|
||||
// closeTab()
|
||||
// }.openNavigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// verifyPageContent(secondWebPage.content)
|
||||
// }.openThreeDotMenu {
|
||||
// clickBrowserViewSaveCollectionButton()
|
||||
// }.selectExistingCollection(firstCollectionName) {
|
||||
// verifySnackBarText("Tab saved!")
|
||||
// }.openHomeScreen {
|
||||
//// verifyExistingTabList()
|
||||
// expandCollection(firstCollectionName)
|
||||
// verifyItemInCollectionExists(firstWebPage.title)
|
||||
// verifyItemInCollectionExists(secondWebPage.title)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun collectionMenuAddTabButtonTest() {
|
||||
// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
//
|
||||
// homeScreen {
|
||||
// closeTab()
|
||||
// }.openNavigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// expandCollection(firstCollectionName)
|
||||
// clickCollectionThreeDotButton()
|
||||
// selectAddTabToCollection()
|
||||
// verifyTabsSelectedCounterText(1)
|
||||
// saveTabsSelectedForCollection()
|
||||
// verifySnackBarText("Tab saved!")
|
||||
// verifyItemInCollectionExists(secondWebPage.title)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun renameCollectionTest() {
|
||||
// createCollection(firstCollectionName)
|
||||
//
|
||||
// homeScreen {
|
||||
// // On homeview, tap the 3-dot button to expand, select rename, rename collection
|
||||
// expandCollection(firstCollectionName)
|
||||
// clickCollectionThreeDotButton()
|
||||
// selectRenameCollection()
|
||||
// typeCollectionName("renamed_collection")
|
||||
// verifyCollectionIsDisplayed("renamed_collection")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun createCollectionFromTabTest() {
|
||||
// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
// homeScreen {
|
||||
// }.openTabDrawer {
|
||||
// verifyExistingOpenTabs(firstWebPage.title)
|
||||
// }.openHomeScreen {
|
||||
// try {
|
||||
// verifyCollectionIsDisplayed(firstCollectionName)
|
||||
// } catch (e: NoMatchingViewException) {
|
||||
// scrollToElementByText(firstCollectionName)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun removeTabFromCollectionTest() {
|
||||
// val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
// homeScreen {
|
||||
// closeTab()
|
||||
// expandCollection(firstCollectionName)
|
||||
// removeTabFromCollection(webPage.title)
|
||||
// verifyItemInCollectionExists(webPage.title, false)
|
||||
// }
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
// homeScreen {
|
||||
// closeTab()
|
||||
// expandCollection(firstCollectionName)
|
||||
// swipeCollectionItemLeft(webPage.title)
|
||||
// verifyItemInCollectionExists(webPage.title, false)
|
||||
// }
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
// homeScreen {
|
||||
// closeTab()
|
||||
// expandCollection(firstCollectionName)
|
||||
// swipeCollectionItemRight(webPage.title)
|
||||
// verifyItemInCollectionExists(webPage.title, false)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun selectTabOnLongTapTest() {
|
||||
// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
//
|
||||
// navigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// }.openNavigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// longTapSelectTab(firstWebPage.title)
|
||||
// verifySelectTabsView()
|
||||
// verifyTabsSelectedCounterText(1)
|
||||
// selectTabForCollection(secondWebPage.title)
|
||||
// verifyTabsSelectedCounterText(2)
|
||||
// saveTabsSelectedForCollection()
|
||||
// typeCollectionName(firstCollectionName)
|
||||
// verifySnackBarText("Tabs saved!")
|
||||
//// closeTabViaXButton(firstWebPage.title)
|
||||
//// closeTabViaXButton(secondWebPage.title)
|
||||
// expandCollection(firstCollectionName)
|
||||
// verifyItemInCollectionExists(firstWebPage.title)
|
||||
// verifyItemInCollectionExists(secondWebPage.title)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun tabsOverflowMenuSaveCollectionTest() {
|
||||
// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
//
|
||||
// navigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// }.openNavigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// }.openTabsListThreeDotMenu {
|
||||
// verifySaveCollection()
|
||||
// }.clickOpenTabsMenuSaveCollection {
|
||||
// verifySelectTabsView()
|
||||
// verifyTabsSelectedCounterText(0)
|
||||
// selectAllTabsForCollection()
|
||||
// verifyTabsSelectedCounterText(2)
|
||||
// saveTabsSelectedForCollection()
|
||||
// typeCollectionName(firstCollectionName)
|
||||
//// closeTabViaXButton(firstWebPage.title)
|
||||
//// closeTabViaXButton(secondWebPage.title)
|
||||
// expandCollection(firstCollectionName)
|
||||
// verifyItemInCollectionExists(firstWebPage.title)
|
||||
// verifyItemInCollectionExists(secondWebPage.title)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587")
|
||||
// @Test
|
||||
// fun navigateBackInCollectionFlowTest() {
|
||||
// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
//
|
||||
// createCollection(firstCollectionName)
|
||||
// navigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// }.openHomeScreen {
|
||||
// longTapSelectTab(secondWebPage.title)
|
||||
// verifySelectTabsView()
|
||||
// saveTabsSelectedForCollection()
|
||||
// verifySelectCollectionView()
|
||||
// clickAddNewCollection()
|
||||
// verifyNameCollectionView()
|
||||
// goBackCollectionFlow()
|
||||
// verifySelectCollectionView()
|
||||
// goBackCollectionFlow()
|
||||
// verifySelectTabsView()
|
||||
// goBackCollectionFlow()
|
||||
// verifyHomeComponent()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun createCollection(collectionName: String, firstCollection: Boolean = true) {
|
||||
// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
//
|
||||
// navigationToolbar {
|
||||
// }.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent(firstWebPage.content)
|
||||
// }.openThreeDotMenu {
|
||||
// clickBrowserViewSaveCollectionButton()
|
||||
// if (!firstCollection)
|
||||
// clickAddNewCollection()
|
||||
//
|
||||
// }.typeCollectionName(collectionName) {
|
||||
// verifySnackBarText("Tab saved!")
|
||||
// }.openHomeScreen {
|
||||
// mDevice.wait(
|
||||
// Until.findObject(By.text(collectionName)),
|
||||
// TestAssetHelper.waitingTime
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -0,0 +1,275 @@
|
||||
package org.mozilla.fenix.ui.robots
|
||||
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.NoMatchingViewException
|
||||
import androidx.test.espresso.action.ViewActions.pressImeActionButton
|
||||
import androidx.test.espresso.action.ViewActions.replaceText
|
||||
import androidx.test.espresso.action.ViewActions.swipeLeft
|
||||
import androidx.test.espresso.action.ViewActions.swipeRight
|
||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.UiSelector
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.hamcrest.Matchers.allOf
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||
import org.mozilla.fenix.helpers.TestHelper.packageName
|
||||
import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText
|
||||
import org.mozilla.fenix.helpers.click
|
||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||
|
||||
class CollectionRobot {
|
||||
|
||||
fun verifySelectCollectionScreen() {
|
||||
onView(withText("Select collection"))
|
||||
.check(matches(isDisplayed()))
|
||||
|
||||
onView(withId(R.id.collections_list))
|
||||
.check(matches(isDisplayed()))
|
||||
|
||||
onView(withText("Add new collection"))
|
||||
.check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
fun clickAddNewCollection() = addNewCollectionButton().click()
|
||||
|
||||
fun verifyCollectionNameTextField() {
|
||||
mainMenuEditCollectionNameField().check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
// names a collection saved from tab drawer
|
||||
fun typeCollectionNameAndSave(collectionName: String) {
|
||||
collectionNameTextField().perform(replaceText(collectionName))
|
||||
mDevice.findObject(UiSelector().textContains("OK")).click()
|
||||
}
|
||||
|
||||
fun verifyTabsSelectedCounterText(numOfTabs: Int) {
|
||||
mDevice.findObject(UiSelector().text("Select tabs to save"))
|
||||
.waitUntilGone(waitingTime)
|
||||
|
||||
val tabsCounter = onView(withId(R.id.bottom_bar_text))
|
||||
when (numOfTabs) {
|
||||
1 -> tabsCounter.check(matches(withText("$numOfTabs tab selected")))
|
||||
2 -> tabsCounter.check(matches(withText("$numOfTabs tabs selected")))
|
||||
}
|
||||
}
|
||||
|
||||
fun saveTabsSelectedForCollection() {
|
||||
onView(withId(R.id.save_button)).click()
|
||||
}
|
||||
|
||||
fun verifyTabSavedInCollection(title: String, visible: Boolean = true) {
|
||||
if (visible) {
|
||||
scrollToElementByText(title)
|
||||
collectionItem(title)
|
||||
.check(
|
||||
matches(isDisplayed())
|
||||
)
|
||||
} else
|
||||
collectionItem(title)
|
||||
.check(doesNotExist())
|
||||
}
|
||||
|
||||
fun verifyCollectionTabUrl() {
|
||||
onView(withId(R.id.caption)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
fun verifyCollectionTabLogo() {
|
||||
onView(withId(R.id.favicon)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
fun verifyShareCollectionButtonIsVisible(visible: Boolean) {
|
||||
shareCollectionButton()
|
||||
.check(
|
||||
if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))
|
||||
else matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))
|
||||
)
|
||||
}
|
||||
|
||||
fun clickShareCollectionButton() = onView(withId(R.id.collection_share_button)).click()
|
||||
|
||||
fun verifyCollectionMenuIsVisible(visible: Boolean) {
|
||||
collectionThreeDotButton()
|
||||
.check(
|
||||
if (visible) matches(
|
||||
withEffectiveVisibility(
|
||||
ViewMatchers.Visibility.VISIBLE
|
||||
)
|
||||
)
|
||||
else matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))
|
||||
)
|
||||
}
|
||||
|
||||
fun clickCollectionThreeDotButton() {
|
||||
collectionThreeDotButton().click()
|
||||
mDevice.waitNotNull(
|
||||
Until.findObject(By.text("Delete collection")),
|
||||
waitingTime
|
||||
)
|
||||
}
|
||||
|
||||
fun selectOpenTabs() {
|
||||
onView(withText("Open tabs")).click()
|
||||
}
|
||||
|
||||
fun selectRenameCollection() {
|
||||
onView(withText("Rename collection")).click()
|
||||
mDevice.waitNotNull(Until.findObject(By.text("Rename collection")))
|
||||
}
|
||||
|
||||
fun selectAddTabToCollection() {
|
||||
onView(withText("Add tab")).click()
|
||||
mDevice.waitNotNull(Until.findObject(By.text("Select Tabs")))
|
||||
}
|
||||
|
||||
fun selectDeleteCollection() {
|
||||
onView(withText("Delete collection")).click()
|
||||
mDevice.waitNotNull(Until.findObject(By.res("android:id/message")), waitingTime)
|
||||
}
|
||||
|
||||
fun confirmDeleteCollection() {
|
||||
onView(withText("DELETE")).click()
|
||||
mDevice.waitNotNull(
|
||||
Until.findObject(By.res("$packageName:id/no_collections_header")),
|
||||
waitingTime
|
||||
)
|
||||
}
|
||||
|
||||
fun verifyCollectionItemRemoveButtonIsVisible(title: String, visible: Boolean) {
|
||||
removeTabFromCollectionButton(title)
|
||||
.check(
|
||||
if (visible) matches(
|
||||
withEffectiveVisibility(
|
||||
ViewMatchers.Visibility.VISIBLE
|
||||
)
|
||||
)
|
||||
else doesNotExist()
|
||||
)
|
||||
}
|
||||
|
||||
fun removeTabFromCollection(title: String) = removeTabFromCollectionButton(title).click()
|
||||
|
||||
fun swipeCollectionItemRight(title: String) {
|
||||
scrollToElementByText(title)
|
||||
// Swipping can sometimes fail to remove the tab, so if the tab still exists, we need to repeat it
|
||||
var retries = 0 // number of retries before failing, will stop at 2
|
||||
while (mDevice.findObject(
|
||||
UiSelector()
|
||||
.resourceId("$packageName:id/label")
|
||||
.text(title)
|
||||
).exists() && retries < 2
|
||||
) {
|
||||
collectionItem(title).perform(swipeRight())
|
||||
retries++
|
||||
}
|
||||
}
|
||||
|
||||
fun swipeCollectionItemLeft(title: String) {
|
||||
scrollToElementByText(title)
|
||||
// Swipping can sometimes fail to remove the tab, so if the tab still exists, we need to repeat it
|
||||
var retries = 0 // number of retries before failing, will stop at 2
|
||||
while (mDevice.findObject(
|
||||
UiSelector()
|
||||
.resourceId("$packageName:id/label")
|
||||
.text(title)
|
||||
).exists() && retries < 2
|
||||
) {
|
||||
collectionItem(title).perform(swipeLeft())
|
||||
retries++
|
||||
}
|
||||
}
|
||||
|
||||
fun verifySnackBarText(expectedText: String) {
|
||||
mDevice.findObject(UiSelector().text(expectedText)).waitForExists(waitingTime)
|
||||
}
|
||||
|
||||
fun goBackInCollectionFlow() = backButton().click()
|
||||
|
||||
class Transition {
|
||||
fun collapseCollection(
|
||||
title: String,
|
||||
interact: HomeScreenRobot.() -> Unit
|
||||
): HomeScreenRobot.Transition {
|
||||
try {
|
||||
mDevice.waitNotNull(Until.findObject(By.text(title)), waitingTime)
|
||||
onView(allOf(withId(R.id.chevron), hasSibling(withText(title)))).click()
|
||||
} catch (e: NoMatchingViewException) {
|
||||
scrollToElementByText(title)
|
||||
}
|
||||
|
||||
HomeScreenRobot().interact()
|
||||
return HomeScreenRobot.Transition()
|
||||
}
|
||||
|
||||
// names a collection saved from the 3dot menu
|
||||
fun typeCollectionNameAndSave(
|
||||
name: String,
|
||||
interact: BrowserRobot.() -> Unit
|
||||
): BrowserRobot.Transition {
|
||||
mDevice.findObject(UiSelector().resourceId("$packageName:id/name_collection_edittext"))
|
||||
.waitForExists(waitingTime)
|
||||
|
||||
mainMenuEditCollectionNameField().perform(
|
||||
replaceText(name),
|
||||
pressImeActionButton()
|
||||
)
|
||||
|
||||
// wait for the collection creation wrapper to be dismissed
|
||||
mDevice.waitNotNull(Until.gone(By.res("$packageName:id/createCollectionWrapper")))
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
}
|
||||
|
||||
fun selectExistingCollection(
|
||||
title: String,
|
||||
interact: BrowserRobot.() -> Unit
|
||||
): BrowserRobot.Transition {
|
||||
mDevice.waitNotNull(Until.findObject(By.text(title)), waitingTime)
|
||||
onView(withText(title)).click()
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun collectionRobot(interact: CollectionRobot.() -> Unit): CollectionRobot.Transition {
|
||||
CollectionRobot().interact()
|
||||
return CollectionRobot.Transition()
|
||||
}
|
||||
|
||||
private fun collectionThreeDotButton() =
|
||||
onView(withId(R.id.collection_overflow_button))
|
||||
|
||||
private fun collectionItem(title: String) =
|
||||
onView(allOf(withId(R.id.label), withText(title)))
|
||||
|
||||
private fun shareCollectionButton() = onView(withId(R.id.collection_share_button))
|
||||
|
||||
private fun removeTabFromCollectionButton(title: String) =
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.secondary_button),
|
||||
hasSibling(withText(title))
|
||||
)
|
||||
)
|
||||
|
||||
// collection name text field, opened from tab drawer
|
||||
private fun collectionNameTextField() = onView(withId(R.id.collection_name))
|
||||
|
||||
// collection name text field, opened from main menu
|
||||
private fun mainMenuEditCollectionNameField() =
|
||||
onView(withId(R.id.name_collection_edittext))
|
||||
|
||||
private fun addNewCollectionButton() = onView(withText("Add new collection"))
|
||||
|
||||
private fun backButton() =
|
||||
onView(withId(R.id.back_button))
|
@ -1,46 +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.robots
|
||||
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.click
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for Synced Tabs sub menu.
|
||||
*/
|
||||
class SyncedTabsRobot {
|
||||
|
||||
fun verifySyncedTabsMenuHeader() = assertSyncedTabsMenuHeader()
|
||||
|
||||
class Transition {
|
||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())!!
|
||||
|
||||
fun goBack(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||
goBackButton().click()
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun goBackButton() =
|
||||
onView(allOf(withContentDescription("Navigate up")))
|
||||
|
||||
private fun assertSyncedTabsMenuHeader() {
|
||||
// Replaced with the new string here, the test is assuming we are NOT signed in
|
||||
// Sync tests in SettingsSyncTest are still TO-DO, so I'm not sure that we have a test for signing into Sync
|
||||
onView(withText(R.string.sync_menu_sign_in))
|
||||
.check((matches(withEffectiveVisibility(Visibility.VISIBLE))))
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.mozilla.fenix"
|
||||
android:sharedUserId="${sharedUserId}">
|
||||
<application
|
||||
android:name="org.mozilla.fenix.MigratingFenixApplication"
|
||||
tools:replace="android:name">
|
||||
|
||||
<activity android:name=".autofill.AutofillUnlockActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.AppCompat.Translucent" />
|
||||
|
||||
<activity android:name=".autofill.AutofillConfirmActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.AppCompat.Translucent" />
|
||||
|
||||
<activity android:name=".autofill.AutofillSearchActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/DialogActivityTheme" />
|
||||
|
||||
<service
|
||||
android:name=".autofill.AutofillService"
|
||||
android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.autofill.AutofillService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- Overriding the alias of the main manifest to route app launches through our
|
||||
MigrationDecisionActivity which will show the migration screen before launching
|
||||
into the app if needed. -->
|
||||
<activity-alias
|
||||
android:name="${applicationId}.App"
|
||||
android:targetActivity="org.mozilla.fenix.MigrationDecisionActivity"
|
||||
tools:replace="android:targetActivity" />
|
||||
|
||||
<activity
|
||||
android:name="org.mozilla.fenix.MigrationDecisionActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name="org.mozilla.fenix.MigrationService" />
|
||||
</application>
|
||||
</manifest>
|
@ -1,101 +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/. */
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import mozilla.components.browser.engine.gecko.autofill.GeckoLoginDelegateWrapper
|
||||
import mozilla.components.browser.engine.gecko.ext.toContentBlockingSetting
|
||||
import mozilla.components.browser.engine.gecko.glean.GeckoAdapter
|
||||
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
|
||||
import mozilla.components.concept.storage.LoginsStorage
|
||||
import mozilla.components.lib.crash.handler.CrashHandlerService
|
||||
import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.geckoview.ContentBlocking
|
||||
import org.mozilla.geckoview.GeckoRuntime
|
||||
import org.mozilla.geckoview.GeckoRuntimeSettings
|
||||
import org.mozilla.geckoview.ContentBlocking.SafeBrowsingProvider
|
||||
|
||||
object GeckoProvider {
|
||||
var testConfig: Bundle? = null
|
||||
private var runtime: GeckoRuntime? = null
|
||||
const val CN_UPDATE_URL =
|
||||
"https://sb.firefox.com.cn/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2"
|
||||
const val CN_GET_HASH_URL =
|
||||
"https://sb.firefox.com.cn/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2"
|
||||
|
||||
@Synchronized
|
||||
fun getOrCreateRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>,
|
||||
trackingProtectionPolicy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
if (runtime == null) {
|
||||
runtime = createRuntime(context, storage, trackingProtectionPolicy)
|
||||
}
|
||||
|
||||
return runtime!!
|
||||
}
|
||||
|
||||
private fun createRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>,
|
||||
policy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
val builder = GeckoRuntimeSettings.Builder()
|
||||
|
||||
testConfig?.let {
|
||||
builder.extras(it)
|
||||
.remoteDebuggingEnabled(true)
|
||||
}
|
||||
|
||||
// Use meeee.
|
||||
policy.hashCode()
|
||||
|
||||
val runtimeSettings = builder
|
||||
.crashHandler(CrashHandlerService::class.java)
|
||||
.telemetryDelegate(GeckoAdapter())
|
||||
.contentBlocking(policy.toContentBlockingSetting())
|
||||
.aboutConfigEnabled(true)
|
||||
.debugLogging(Config.channel.isDebug)
|
||||
.build()
|
||||
|
||||
val settings = context.components.settings
|
||||
if (!settings.shouldUseAutoSize) {
|
||||
runtimeSettings.automaticFontSizeAdjustment = false
|
||||
val fontSize = settings.fontSizeFactor
|
||||
runtimeSettings.fontSizeFactor = fontSize
|
||||
}
|
||||
|
||||
// Add safebrowsing providers for China
|
||||
if (Config.channel.isMozillaOnline) {
|
||||
val mozcn = SafeBrowsingProvider
|
||||
.withName("mozcn")
|
||||
.version("2.2")
|
||||
.lists("m6eb-phish-shavar", "m6ib-phish-shavar")
|
||||
.updateUrl(CN_UPDATE_URL)
|
||||
.getHashUrl(CN_GET_HASH_URL)
|
||||
.build()
|
||||
|
||||
runtimeSettings.contentBlocking.setSafeBrowsingProviders(mozcn,
|
||||
// Keep the existing configuration
|
||||
ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER,
|
||||
ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER)
|
||||
|
||||
runtimeSettings.contentBlocking.setSafeBrowsingPhishingTable(
|
||||
"m6eb-phish-shavar",
|
||||
"m6ib-phish-shavar",
|
||||
// Existing configuration
|
||||
"goog-phish-proto")
|
||||
}
|
||||
|
||||
val geckoRuntime = GeckoRuntime.create(context, runtimeSettings)
|
||||
val loginStorageDelegate = GeckoLoginStorageDelegate(storage)
|
||||
@Suppress("Deprecation")
|
||||
geckoRuntime.loginStorageDelegate = GeckoLoginDelegateWrapper(loginStorageDelegate)
|
||||
|
||||
return geckoRuntime
|
||||
}
|
||||
}
|
@ -1,61 +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/. */
|
||||
|
||||
const ADLINK_CHECK_TIMEOUT_MS = 1000;
|
||||
|
||||
function collectLinks(urls) {
|
||||
let anchors = document.getElementsByTagName("a");
|
||||
for (let anchor of anchors) {
|
||||
if (!anchor.href) {
|
||||
continue;
|
||||
}
|
||||
urls.push(anchor.href);
|
||||
}
|
||||
}
|
||||
|
||||
function sendLinks(cookies) {
|
||||
let urls = [];
|
||||
collectLinks(urls);
|
||||
|
||||
let message = {
|
||||
'url': document.location.href,
|
||||
'urls': urls,
|
||||
'cookies': cookies
|
||||
};
|
||||
browser.runtime.sendNativeMessage("MozacBrowserAds", message);
|
||||
}
|
||||
|
||||
function notify(message) {
|
||||
sendLinks(message.cookies);
|
||||
}
|
||||
|
||||
browser.runtime.onMessage.addListener(notify);
|
||||
|
||||
const events = ["pageshow", "load", "unload"];
|
||||
var timeout;
|
||||
|
||||
const eventLogger = event => {
|
||||
switch (event.type) {
|
||||
case "load":
|
||||
timeout = setTimeout(() => {
|
||||
browser.runtime.sendMessage({ "checkCookies": true });
|
||||
}, ADLINK_CHECK_TIMEOUT_MS)
|
||||
break;
|
||||
case "pageshow":
|
||||
if (event.persisted) {
|
||||
timeout = setTimeout(() => {
|
||||
browser.runtime.sendMessage({ "checkCookies": true });
|
||||
}, ADLINK_CHECK_TIMEOUT_MS)
|
||||
}
|
||||
break;
|
||||
case "unload":
|
||||
clearTimeout(timeout);
|
||||
default:
|
||||
console.log('Event:', event.type);
|
||||
}
|
||||
};
|
||||
|
||||
events.forEach(eventName =>
|
||||
window.addEventListener(eventName, eventLogger)
|
||||
);
|
@ -1,28 +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/. */
|
||||
|
||||
browser.runtime.onMessage.addListener(notify);
|
||||
|
||||
function sendMessageToTabs(tabs, cookies) {
|
||||
for (let tab of tabs) {
|
||||
browser.tabs.sendMessage(
|
||||
tab.id,
|
||||
{ cookies }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function notify(message) {
|
||||
if (message.checkCookies) {
|
||||
browser.cookies.getAll({})
|
||||
.then(cookies => {
|
||||
browser.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}).then(tabs => {
|
||||
sendMessageToTabs(tabs, cookies);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "ads@mozac.org"
|
||||
}
|
||||
},
|
||||
"name": "Mozilla Android Components - Ads",
|
||||
"version": "${version}",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["https://*/*"],
|
||||
"include_globs": [
|
||||
"https://www.google.*/search*",
|
||||
"https://www.bing.com/search*",
|
||||
"https://duckduckgo.com/*"
|
||||
],
|
||||
"js": ["ads.js"],
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["adsBackground.js"]
|
||||
},
|
||||
"permissions": [
|
||||
"geckoViewAddons",
|
||||
"nativeMessaging",
|
||||
"nativeMessagingFromContent",
|
||||
"geckoViewAddons",
|
||||
"nativeMessaging",
|
||||
"nativeMessagingFromContent",
|
||||
"webNavigation",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"cookies",
|
||||
"*://*/*"
|
||||
]
|
||||
}
|
@ -1,47 +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/. */
|
||||
|
||||
const COOKIES_CHECK_TIMEOUT_MS = 1000;
|
||||
|
||||
function sendCookies(cookies) {
|
||||
let message = {
|
||||
'url': document.location.href,
|
||||
'cookies': cookies
|
||||
}
|
||||
browser.runtime.sendNativeMessage("BrowserCookiesMessage", message);
|
||||
}
|
||||
|
||||
function notify(message) {
|
||||
sendCookies(message.cookies);
|
||||
}
|
||||
|
||||
browser.runtime.onMessage.addListener(notify);
|
||||
|
||||
const events = ["pageshow", "load", "unload"];
|
||||
var timeout;
|
||||
|
||||
const eventLogger = event => {
|
||||
switch (event.type) {
|
||||
case "load":
|
||||
timeout = setTimeout(() => {
|
||||
browser.runtime.sendMessage({"checkCookies": true});
|
||||
}, COOKIES_CHECK_TIMEOUT_MS);
|
||||
break;
|
||||
case "pageshow":
|
||||
if (event.persisted) {
|
||||
timeout = setTimeout(() => {
|
||||
browser.runtime.sendMessage({"checkCookies": true});
|
||||
}, COOKIES_CHECK_TIMEOUT_MS);
|
||||
}
|
||||
break;
|
||||
case "unload":
|
||||
clearTimeout(timeout);
|
||||
default:
|
||||
console.log('Event:', event.type);
|
||||
}
|
||||
};
|
||||
|
||||
events.forEach(eventName =>
|
||||
window.addEventListener(eventName, eventLogger)
|
||||
);
|
@ -1,28 +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/. */
|
||||
|
||||
browser.runtime.onMessage.addListener(notify);
|
||||
|
||||
function sendMessageToTabs(tabs, cookies) {
|
||||
for (let tab of tabs) {
|
||||
browser.tabs.sendMessage(
|
||||
tab.id,
|
||||
{cookies: cookies}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function notify(message) {
|
||||
if(message.checkCookies) {
|
||||
browser.cookies.getAll({})
|
||||
.then(cookies => {
|
||||
browser.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}).then(tabs => {
|
||||
sendMessageToTabs(tabs, cookies);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "cookies@mozac.org"
|
||||
}
|
||||
},
|
||||
"name": "Mozilla Android Components - Cookies",
|
||||
"version": "${version}",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["https://*/*"],
|
||||
"include_globs": [
|
||||
"https://www.google.*/search*",
|
||||
"https://www.baidu.com/from=844b/s*",
|
||||
"https://www.baidu.com/from=844b/baidu*",
|
||||
"https://*search.yahoo.com/search*",
|
||||
"https://www.bing.com/search*",
|
||||
"https://duckduckgo.com/*"
|
||||
],
|
||||
"js": ["cookies.js"],
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["cookiesBackground.js"]
|
||||
},
|
||||
"permissions": [
|
||||
"geckoViewAddons",
|
||||
"nativeMessaging",
|
||||
"nativeMessagingFromContent",
|
||||
"webNavigation",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"cookies",
|
||||
"*://*/*"
|
||||
]
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/* 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.autofill
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import mozilla.components.feature.autofill.AutofillConfiguration
|
||||
import mozilla.components.feature.autofill.ui.AbstractAutofillSearchActivity
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
/**
|
||||
* Activity responsible for letting the user manually search and pick credentials for auto-filling a
|
||||
* third-party app.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class AutofillSearchActivity : AbstractAutofillSearchActivity() {
|
||||
override val configuration: AutofillConfiguration by lazy { components.autofillConfiguration }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// To avoid the dialog constantly resizing horizontally while typing, let's always use
|
||||
// the full width of the screen for the dialog.
|
||||
window.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue