Compare commits
No commits in common. 'fork' and 'iceraven-2.2.0' have entirely different histories.
fork
...
iceraven-2
@ -0,0 +1,142 @@
|
||||
projects:
|
||||
app:
|
||||
upstream_dependencies:
|
||||
- browser-domains
|
||||
- browser-engine-gecko
|
||||
- browser-errorpages
|
||||
- browser-icons
|
||||
- browser-menu
|
||||
- browser-menu2
|
||||
- browser-session-storage
|
||||
- browser-state
|
||||
- browser-storage-sync
|
||||
- browser-tabstray
|
||||
- browser-thumbnails
|
||||
- browser-toolbar
|
||||
- compose-awesomebar
|
||||
- compose-cfr
|
||||
- concept-awesomebar
|
||||
- concept-base
|
||||
- concept-engine
|
||||
- concept-fetch
|
||||
- concept-menu
|
||||
- concept-push
|
||||
- concept-storage
|
||||
- concept-sync
|
||||
- concept-tabstray
|
||||
- concept-toolbar
|
||||
- feature-accounts
|
||||
- feature-accounts-push
|
||||
- feature-addons
|
||||
- feature-app-links
|
||||
- feature-autofill
|
||||
- feature-awesomebar
|
||||
- feature-contextmenu
|
||||
- feature-customtabs
|
||||
- feature-downloads
|
||||
- feature-findinpage
|
||||
- feature-intent
|
||||
- feature-logins
|
||||
- feature-media
|
||||
- feature-privatemode
|
||||
- feature-prompts
|
||||
- feature-push
|
||||
- feature-pwa
|
||||
- feature-qr
|
||||
- feature-readerview
|
||||
- feature-recentlyclosed
|
||||
- feature-search
|
||||
- feature-session
|
||||
- feature-share
|
||||
- feature-sitepermissions
|
||||
- feature-syncedtabs
|
||||
- feature-tab-collections
|
||||
- feature-tabs
|
||||
- feature-toolbar
|
||||
- feature-top-sites
|
||||
- feature-webauthn
|
||||
- feature-webcompat
|
||||
- feature-webcompat-reporter
|
||||
- feature-webnotifications
|
||||
- lib-crash
|
||||
- lib-crash-sentry
|
||||
- lib-dataprotect
|
||||
- lib-publicsuffixlist
|
||||
- lib-push-firebase
|
||||
- lib-state
|
||||
- service-contile
|
||||
- service-digitalassetlinks
|
||||
- service-firefox-accounts
|
||||
- service-glean
|
||||
- service-location
|
||||
- service-nimbus
|
||||
- service-pocket
|
||||
- service-sync-autofill
|
||||
- service-sync-logins
|
||||
- support-base
|
||||
- support-images
|
||||
- support-ktx
|
||||
- support-locale
|
||||
- support-rusterrors
|
||||
- support-rusthttp
|
||||
- support-rustlog
|
||||
- support-sync-telemetry
|
||||
- support-test
|
||||
- support-test-libstate
|
||||
- support-utils
|
||||
- support-webextensions
|
||||
- ui-autocomplete
|
||||
- ui-colors
|
||||
- ui-icons
|
||||
- ui-tabcounter
|
||||
- ui-widgets
|
||||
variants:
|
||||
- apks:
|
||||
- abi: arm64-v8a
|
||||
fileName: app-fenix-arm64-v8a-debug.apk
|
||||
- abi: armeabi-v7a
|
||||
fileName: app-fenix-armeabi-v7a-debug.apk
|
||||
- abi: x86
|
||||
fileName: app-fenix-x86-debug.apk
|
||||
- abi: x86_64
|
||||
fileName: app-fenix-x86_64-debug.apk
|
||||
build_type: debug
|
||||
name: fenixDebug
|
||||
- apks:
|
||||
- abi: arm64-v8a
|
||||
fileName: app-fenix-arm64-v8a-release-unsigned.apk
|
||||
- abi: armeabi-v7a
|
||||
fileName: app-fenix-armeabi-v7a-release-unsigned.apk
|
||||
- abi: x86
|
||||
fileName: app-fenix-x86-release-unsigned.apk
|
||||
- abi: x86_64
|
||||
fileName: app-fenix-x86_64-release-unsigned.apk
|
||||
build_type: release
|
||||
name: fenixRelease
|
||||
- apks:
|
||||
- abi: arm64-v8a
|
||||
fileName: app-fenix-arm64-v8a-nightly-unsigned.apk
|
||||
- abi: armeabi-v7a
|
||||
fileName: app-fenix-armeabi-v7a-nightly-unsigned.apk
|
||||
- abi: x86
|
||||
fileName: app-fenix-x86-nightly-unsigned.apk
|
||||
- abi: x86_64
|
||||
fileName: app-fenix-x86_64-nightly-unsigned.apk
|
||||
build_type: nightly
|
||||
name: fenixNightly
|
||||
- apks:
|
||||
- abi: arm64-v8a
|
||||
fileName: app-fenix-arm64-v8a-beta-unsigned.apk
|
||||
- abi: armeabi-v7a
|
||||
fileName: app-fenix-armeabi-v7a-beta-unsigned.apk
|
||||
- abi: x86
|
||||
fileName: app-fenix-x86-beta-unsigned.apk
|
||||
- abi: x86_64
|
||||
fileName: app-fenix-x86_64-beta-unsigned.apk
|
||||
build_type: beta
|
||||
name: fenixBeta
|
||||
- apks:
|
||||
- abi: noarch
|
||||
fileName: app-debug-androidTest.apk
|
||||
build_type: androidTest
|
||||
name: androidTest
|
@ -1,41 +0,0 @@
|
||||
# Definitions for jobs that run periodically. For details on the format, see
|
||||
# `taskcluster/taskgraph/cron/schema.py`. For documentation, see
|
||||
# `taskcluster/docs/cron.rst`.
|
||||
---
|
||||
|
||||
jobs:
|
||||
- name: nightly
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: Nd
|
||||
target-tasks-method: nightly
|
||||
when:
|
||||
- {hour: 5, minute: 0}
|
||||
- {hour: 17, minute: 0}
|
||||
- name: nightly-test
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: Nt
|
||||
target-tasks-method: nightly-test
|
||||
when:
|
||||
- {hour: 5, minute: 0}
|
||||
- name: fennec-production
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: fennec-production
|
||||
target-tasks-method: fennec-production
|
||||
when: [] # Force hook only
|
||||
- name: screenshots
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: screenshots-D
|
||||
target-tasks-method: screenshots
|
||||
when: [{weekday: 'Monday', hour: 10, minute: 0}]
|
||||
- name: legacy-api-ui-tests
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: legacy-api-ui
|
||||
target-tasks-method: legacy_api_ui_tests
|
||||
when:
|
||||
- {hour: 11, minute: 0}
|
||||
- {hour: 20, minute: 0}
|
@ -1,31 +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/.
|
||||
|
||||
# This CODEOWNERS file defines individuals or teams that are responsible
|
||||
# for code in this repository. Code owners are automatically requested
|
||||
# for review when someone opens a pull request that modifies code that
|
||||
# they own. Order is important; the last matching pattern takes the most
|
||||
# precedence.
|
||||
# A CODEOWNERS file uses a pattern that follows the same rules used in
|
||||
# gitignore files. The pattern is followed by one or more GitHub usernames
|
||||
# or team names using the standard @username or @org/team-name format.
|
||||
# You can also refer to a user by an email address that has been added
|
||||
# to their GitHub account, for example user@example.com.
|
||||
# https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# WARNING: if there is a single syntax error in this file, CODEOWNERS
|
||||
# WILL NOT WORK AT ALL. Please be careful when editing this file.
|
||||
#
|
||||
# You can use the technique described in this blog post to validate
|
||||
# the paths you specify in .gitignore:
|
||||
# http://www.benjaminoakes.com/git/2018/08/10/Testing-changes-to-GitHub-CODEOWNERS/
|
||||
|
||||
# By default the Android Components team will be the owner for everything in
|
||||
# the repo. Unless a later match takes precedence.
|
||||
* @mozilla-mobile/ACT @mozilla-mobile/fenix
|
||||
/.cron.yml @mozilla-mobile/releng @mozilla-mobile/fenix
|
||||
/.taskcluster.yml @mozilla-mobile/releng @mozilla-mobile/fenix
|
||||
/automation/ @mozilla-mobile/fenix
|
||||
/taskcluster/ @mozilla-mobile/releng @mozilla-mobile/fenix
|
||||
/.github/ @mozilla-mobile/fenix
|
@ -1,91 +0,0 @@
|
||||
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
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
name: "⌛ Performance issue"
|
||||
about: Create a performance issue if the app is slow or it uses too much memory, disk space, battery, or network data
|
||||
title: ""
|
||||
labels: "performance"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Device information
|
||||
|
||||
* Android device: ?
|
||||
* Fenix version: ?
|
@ -1,25 +0,0 @@
|
||||
---
|
||||
name: "\U0001F4BB Web content issue report"
|
||||
about: Create an issue specifically about something wrong with web content while using Fenix
|
||||
title: "[webcontent]"
|
||||
labels: "\U0001F4BB bug"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Site with issue and any steps to reproduce
|
||||
|
||||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Does toggling Tracking Protection fix the issue? (Press the shield icon in the toolbar while on the site to see toggle)
|
||||
|
||||
### Can you reproduce in Chrome (or other non-Mozilla browser)?
|
||||
<!--- Note: If you can reproduce the same issue in Firefox for Android AND Chrome, this issue is very unlikely to be fixed by our teams. -->
|
||||
<!-- If you cannot reproduce in Chrome and know how to do so, please consider filing a Bugzilla issue here for faster triage https://bugzilla.mozilla.org/enter_bug.cgi?product=GeckoView&component=General -->
|
||||
|
||||
### Device information
|
||||
|
||||
* Android device: ?
|
||||
* Fenix version: ?
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
name: "\U000026CF Investigative Spike"
|
||||
about: Create an investigation spike
|
||||
title: "[Spike]"
|
||||
|
||||
---
|
||||
|
||||
## Title
|
||||
Brief description of what needs to be investigated, including the User story for which the spike is needed.
|
||||
|
||||
## Description
|
||||
Description of what is being investigated, including:
|
||||
Method of investigation (engineering research, prototype, etc.
|
||||
Boundaries of investigation (time box to x hours, does not include UX, etc.)
|
||||
|
||||
## Deliverables
|
||||
Description of deliverables, including:
|
||||
Documentation of investigation results (within the spike ticket, or linked to it), including:
|
||||
Findings
|
||||
Recommendations
|
||||
List of possible user stories to implement recommendations, including estimates
|
||||
Next Steps
|
||||
Reach out to Product to go over results of investigation.
|
||||
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
name: "\U0001F4C8 Telemetry User Story"
|
||||
about: Create a telemetry user story, assigned to an Epic to track telemetry
|
||||
title: "[Telemetry]"
|
||||
labels: Feature:Telemetry
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Description & Product Manager / Data Scientist User Story
|
||||
|
||||
## What questions will you answer with this data?
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] ENG files a [DS JIRA](https://jira.mozilla.com/projects/DO/issues/DO-228?filter=allopenissues) request outlining their methodology.
|
||||
- [ ] DS sign off on instrumentation methodology addressing product questions.
|
||||
- [ ] Event pings can be queried via re:dash
|
||||
- [ ] Event pings can be queried via amplitude
|
||||
- [ ] We are sending telemetry events for the actions listed in the requirements
|
||||
- [ ] We have documented the telemetry
|
||||
- [ ] We have asked a data steward to [review](https://github.com/mozilla/data-review/blob/master/request.md) the telemetry
|
@ -1,13 +0,0 @@
|
||||
---
|
||||
name: "\U0001F494 Intermittent UI Test Issue"
|
||||
about: Create an issue to help log a UI test failure
|
||||
labels: "eng:ui-test, intermittent-test"
|
||||
title: "Intermittent UI test failure - <Classname.testName>"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Firebase Test Run:
|
||||
Provide a Firebase test run report link here showcasing the problem
|
||||
### Stacktrace:
|
||||
### Build:
|
@ -1,13 +0,0 @@
|
||||
---
|
||||
name: "\U0001F6A8 Intermittent Unit Test Issue"
|
||||
about: Create an issue to help log a Unit Test failure
|
||||
labels: "eng:intermittent-test"
|
||||
title: "Intermittent Unit Test failure - <Classname.testName>"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Test Run:
|
||||
Provide a test run report link here showcasing the problem (e.g, Taskcluster), and a link to the source Github event
|
||||
### Stacktrace:
|
||||
### Build:
|
@ -1,14 +0,0 @@
|
||||
|
||||
|
||||
### Pull Request checklist
|
||||
<!-- Before submitting the PR, please address each item -->
|
||||
- [ ] **Tests**: This PR includes thorough tests or an explanation of why it does not
|
||||
- [ ] **Screenshots**: This PR includes screenshots or GIFs of the changes made or an explanation of why it does not
|
||||
- [ ] **Accessibility**: The code in this PR follows [accessibility best practices](https://github.com/mozilla-mobile/shared-docs/blob/master/android/accessibility_guide.md) or does not include any user facing features. In addition, it includes a screenshot of a successful [accessibility scan](https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor&hl=en_US) to ensure no new defects are added to the product.
|
||||
|
||||
### To download an APK when reviewing a PR:
|
||||
The PR runs an Android build check (`run-build`) that builds a `forkRelease` variant of the app. If it succeeds, then we upload the apks (signed with debug keys) via Github actions. We also generate a comment with some instructions and a link to help you find the downloads. You can also follow the instructions below:
|
||||
1. Click Details next to "run-build (pull_request_target)" after it finishes with a green checkmark.
|
||||
2. Click the "Artifacts" drop-down near the top right of the page.
|
||||
3. The apk links should be present in the drop-down menu. You can click on the suitable CPU architecture to download a zipped apk file.
|
||||
4. Unzip the file and install the apk.
|
@ -1,115 +0,0 @@
|
||||
name: Android build PR
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- fork
|
||||
jobs:
|
||||
run-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Install Android SDK with pieces Gradle skips
|
||||
run: ./automation/iceraven/install-sdk.sh
|
||||
- name: Create version name
|
||||
run: echo "VERSION_NAME=$(git describe --tags HEAD)" >> $GITHUB_ENV
|
||||
- name: Build forkRelease variant of app
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: app:assembleForkRelease -PversionName=${{ env.VERSION_NAME }}
|
||||
|
||||
|
||||
run-testDebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run tests
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: testDebug
|
||||
|
||||
|
||||
run-detekt:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run detekt
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: detekt
|
||||
- name: Archive detekt results
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: detekt report
|
||||
path: build/reports/detekt.html
|
||||
|
||||
|
||||
run-ktlint:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run ktlint
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: ktlint
|
||||
|
||||
|
||||
run-lintDebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run lintDebug
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: lintDebug
|
||||
- name: Archive lint results
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lintDebug report
|
||||
path: app/build/reports/lint-results-debug.html
|
@ -1,143 +0,0 @@
|
||||
name: Android build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- fork
|
||||
jobs:
|
||||
run-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Install Android SDK with pieces Gradle skips
|
||||
run: ./automation/iceraven/install-sdk.sh
|
||||
- name: Create version name
|
||||
run: echo "VERSION_NAME=$(git describe --tags HEAD)" >> $GITHUB_ENV
|
||||
- name: Build forkRelease variant of app
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: app:assembleForkRelease -PversionName=${{ env.VERSION_NAME }}
|
||||
- name: Create signed APKs
|
||||
uses: abhijitvalluri/sign-apks@v0.8
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/forkRelease/
|
||||
signingKeyBase64: ${{ secrets.DEBUG_SIGNING_KEY }}
|
||||
alias: ${{ secrets.DEBUG_ALIAS }}
|
||||
keyStorePassword: ${{ secrets.DEBUG_KEY_STORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.DEBUG_KEY_PASSWORD }}
|
||||
- name: Archive arm64 apk
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-arm64-v8a-forkRelease.apk
|
||||
path: app/build/outputs/apk/forkRelease/app-arm64-v8a-forkRelease.apk
|
||||
- name: Archive armeabi apk
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-armeabi-v7a-forkRelease.apk
|
||||
path: app/build/outputs/apk/forkRelease/app-armeabi-v7a-forkRelease.apk
|
||||
- name: Archive x86 apk
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-x86-forkRelease.apk
|
||||
path: app/build/outputs/apk/forkRelease/app-x86-forkRelease.apk
|
||||
- name: Archive x86_64 apk
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-x86_64-forkRelease.apk
|
||||
path: app/build/outputs/apk/forkRelease/app-x86_64-forkRelease.apk
|
||||
|
||||
|
||||
run-testDebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run tests
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: testDebug
|
||||
|
||||
|
||||
run-detekt:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run detekt
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: detekt
|
||||
- name: Archive detekt results
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: detekt report
|
||||
path: build/reports/detekt.html
|
||||
|
||||
|
||||
run-ktlint:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run ktlint
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: ktlint
|
||||
|
||||
|
||||
run-lintDebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Run lintDebug
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
arguments: lintDebug
|
||||
- name: Archive lint results
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lintDebug report
|
||||
path: app/build/reports/lint-results-debug.html
|
@ -1,24 +0,0 @@
|
||||
name: PR comment
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
branches:
|
||||
- fork
|
||||
jobs: # Disabled because we cannot build changes from fork PRs using this repo's secrets due to Github limitations. So, the built apk will be from wrong code, so this is pointless.
|
||||
comment-on-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: "! contains(toJSON(github.event.pull_request.title), '[skip ci]')"
|
||||
steps:
|
||||
- name: Comment on PR with link to checks page
|
||||
uses: mshick/add-pr-comment@v1
|
||||
with:
|
||||
message: |
|
||||
### Download the built apks
|
||||
You can download the apks built by Github actions **after** the CI checks pass.
|
||||
Please go to the <a href="${{ github.event.pull_request.html_url }}/checks">checks page for this PR</a> to find the zipped apk files under the artifacts drop-down, as seen in the example screenshot below.
|
||||
|
||||
Note that you will have to click on the "Android build PR" tab on the left side to see the artifacts.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/fork-maintainers/iceraven-browser/fork/.github/imgs/download-artifacts-screenshot.png" />
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allow-repeats: false
|
Binary file not shown.
Before Width: | Height: | Size: 99 KiB |
@ -1,63 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 180
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- pin
|
||||
- "feature request 🌟"
|
||||
- "eng:disabled-test"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: wontfix
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
See: https://github.com/mozilla-mobile/fenix/issues/17373
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
issues:
|
||||
exemptLabels:
|
||||
- pin
|
||||
- "feature request 🌟"
|
@ -1,16 +0,0 @@
|
||||
name: AssignTriageLabel
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
assign:
|
||||
name: Triage Issues
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add Triage Label
|
||||
uses: boek/AddTriageLabel@v1.2
|
||||
with:
|
||||
repotoken: ${{ secrets.GITHUB_TOKEN }}
|
||||
labeltoadd: "needs:triage"
|
@ -0,0 +1,3 @@
|
||||
[submodule "android-components"]
|
||||
path = android-components
|
||||
url = https://github.com/akliuxingyuan/android-components
|
@ -1,122 +0,0 @@
|
||||
queue_rules:
|
||||
- name: default
|
||||
conditions:
|
||||
- status-success=complete-pr
|
||||
pull_request_rules:
|
||||
- name: Resolve conflict
|
||||
conditions:
|
||||
- conflict
|
||||
actions:
|
||||
comment:
|
||||
message: This pull request has conflicts when rebasing. Could you fix it @{{author}}? 🙏
|
||||
- name: MickeyMoz - Auto Merge
|
||||
conditions:
|
||||
- author=MickeyMoz
|
||||
- status-success=pr-complete
|
||||
- files~=(Gecko.kt|AndroidComponents.kt)
|
||||
actions:
|
||||
review:
|
||||
type: APPROVE
|
||||
message: MickeyMoz 💪
|
||||
queue:
|
||||
method: rebase
|
||||
name: default
|
||||
rebase_fallback: none
|
||||
- name: L10N - Auto Merge
|
||||
conditions:
|
||||
- author=mozilla-l10n-automation-bot
|
||||
- status-success=pr-complete
|
||||
- files~=(strings.xml|l10n.toml)
|
||||
actions:
|
||||
review:
|
||||
type: APPROVE
|
||||
message: LGTM 😎
|
||||
queue:
|
||||
method: rebase
|
||||
name: default
|
||||
rebase_fallback: none
|
||||
- name: Release automation (Old)
|
||||
conditions:
|
||||
- base~=releases[_/].*
|
||||
- author=github-actions[bot]
|
||||
# Listing checks manually beause we do not have a "push complete" check yet.
|
||||
- check-success=build-android-test-debug
|
||||
- check-success=build-debug
|
||||
- check-success=build-nightly-simulation
|
||||
- check-success=lint-compare-locales
|
||||
- check-success=lint-detekt
|
||||
- check-success=lint-ktlint
|
||||
- check-success=lint-lint
|
||||
- check-success=signing-android-test-debug
|
||||
- check-success=signing-debug
|
||||
- check-success=signing-nightly-simulation
|
||||
- check-success=test-debug
|
||||
# TODO Temporarily disabled - should be renamed to -build
|
||||
#- check-success=ui-test-x86-debug
|
||||
- files~=(AndroidComponents.kt)
|
||||
actions:
|
||||
review:
|
||||
type: APPROVE
|
||||
message: 🚢
|
||||
queue:
|
||||
method: rebase
|
||||
name: default
|
||||
rebase_fallback: none
|
||||
delete_head_branch:
|
||||
force: false
|
||||
- name: Release automation (New)
|
||||
conditions:
|
||||
- base~=releases[_/].*
|
||||
- author=github-actions[bot]
|
||||
# Listing checks manually beause we do not have a "push complete" check yet.
|
||||
- check-success=build-android-test-beta
|
||||
- check-success=build-android-test-debug
|
||||
- check-success=build-beta-firebase
|
||||
- check-success=build-debug
|
||||
- check-success=build-nightly-simulation
|
||||
- check-success=lint-compare-locales
|
||||
- check-success=lint-detekt
|
||||
- check-success=lint-ktlint
|
||||
- check-success=lint-lint
|
||||
- check-success=signing-android-test-beta
|
||||
- check-success=signing-beta-firebase
|
||||
- check-success=signing-nightly-simulation
|
||||
- check-success=test-debug
|
||||
- check-success=ui-test-x86-beta
|
||||
- files~=(AndroidComponents.kt)
|
||||
actions:
|
||||
review:
|
||||
type: APPROVE
|
||||
message: 🚢
|
||||
queue:
|
||||
method: rebase
|
||||
name: default
|
||||
rebase_fallback: none
|
||||
delete_head_branch:
|
||||
force: false
|
||||
- name: Needs landing - Rebase
|
||||
conditions:
|
||||
- check-success=pr-complete
|
||||
- label=pr:needs-landing
|
||||
- "#approved-reviews-by>=1"
|
||||
- -draft
|
||||
- label!=pr:work-in-progress
|
||||
- label!=pr:do-not-land
|
||||
actions:
|
||||
queue:
|
||||
method: rebase
|
||||
name: default
|
||||
rebase_fallback: none
|
||||
- name: Needs landing - Squash
|
||||
conditions:
|
||||
- check-success=pr-complete
|
||||
- label=pr:needs-landing-squashed
|
||||
- "#approved-reviews-by>=1"
|
||||
- -draft
|
||||
- label!=pr:work-in-progress
|
||||
- label!=pr:do-not-land
|
||||
actions:
|
||||
queue:
|
||||
method: squash
|
||||
name: default
|
||||
rebase_fallback: none
|
@ -1,293 +0,0 @@
|
||||
---
|
||||
version: 1
|
||||
reporting: checks-v1
|
||||
policy:
|
||||
# XXX We restrict taskcluster to collaborators so priviledged tests (like UI tests) can run on PRs
|
||||
pullRequests: collaborators
|
||||
tasks:
|
||||
- $let:
|
||||
trustDomain: mobile
|
||||
|
||||
# Github events have this stuff in different places...
|
||||
ownerEmail:
|
||||
$if: 'tasks_for in ["cron", "action"]'
|
||||
then: '${tasks_for}@noreply.mozilla.org'
|
||||
else:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then:
|
||||
$if: 'event.pusher.email'
|
||||
then: '${event.pusher.email}'
|
||||
else: '${event.pusher.name}@users.noreply.github.com'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: '${event.pull_request.user.login}@users.noreply.github.com'
|
||||
baseRepoUrl:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then: '${event.repository.html_url}'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: '${event.pull_request.base.repo.html_url}'
|
||||
else:
|
||||
$if: 'tasks_for in ["cron", "action"]'
|
||||
then: '${repository.url}'
|
||||
repoUrl:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then: '${event.repository.html_url}'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: '${event.pull_request.head.repo.html_url}'
|
||||
else:
|
||||
$if: 'tasks_for in ["cron", "action"]'
|
||||
then: '${repository.url}'
|
||||
project:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then: '${event.repository.name}'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: '${event.pull_request.head.repo.name}'
|
||||
else:
|
||||
$if: 'tasks_for in ["cron", "action"]'
|
||||
then: '${repository.project}'
|
||||
head_branch:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: ${event.pull_request.head.ref}
|
||||
else:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then: ${event.ref}
|
||||
else:
|
||||
$if: 'tasks_for in ["action", "cron"]'
|
||||
then: '${push.branch}'
|
||||
head_sha:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then: '${event.after}'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: '${event.pull_request.head.sha}'
|
||||
else:
|
||||
$if: 'tasks_for in ["action", "cron"]'
|
||||
then: '${push.revision}'
|
||||
ownTaskId:
|
||||
$if: '"github" in tasks_for'
|
||||
then: {$eval: as_slugid("decision_task")}
|
||||
else:
|
||||
$if: 'tasks_for in ["cron", "action"]'
|
||||
then: '${ownTaskId}'
|
||||
pullRequestAction:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then: ${event.action}
|
||||
else: 'UNDEFINED'
|
||||
in:
|
||||
$if: >
|
||||
tasks_for in ["action", "cron"]
|
||||
|| (tasks_for == "github-pull-request" && pullRequestAction in ["opened", "reopened", "synchronize"])
|
||||
|| (tasks_for == "github-push" && head_branch[:10] != "refs/tags/") && (head_branch != "staging.tmp") && (head_branch != "trying.tmp") && (head_branch[:8] != "mergify/")
|
||||
then:
|
||||
$let:
|
||||
level:
|
||||
$if: 'tasks_for in ["github-push", "action", "cron"] && repoUrl == "https://github.com/mozilla-mobile/fenix"'
|
||||
then: '3'
|
||||
else: '1'
|
||||
short_head_branch:
|
||||
$if: 'head_branch[:11] == "refs/heads/"'
|
||||
then: {$eval: 'head_branch[11:]'}
|
||||
in:
|
||||
taskId:
|
||||
$if: 'tasks_for != "action"'
|
||||
then: '${ownTaskId}'
|
||||
taskGroupId:
|
||||
$if: 'tasks_for == "action"'
|
||||
then: '${action.taskGroupId}'
|
||||
else: '${ownTaskId}' # same as taskId; this is how automation identifies a decision task
|
||||
schedulerId: '${trustDomain}-level-${level}'
|
||||
created: {$fromNow: ''}
|
||||
deadline: {$fromNow: '1 day'}
|
||||
expires: {$fromNow: '1 year 1 second'} # 1 second so artifacts expire first, despite rounding errors
|
||||
metadata:
|
||||
$merge:
|
||||
- owner: "${ownerEmail}"
|
||||
source: '${repoUrl}/raw/${head_sha}/.taskcluster.yml'
|
||||
- $if: 'tasks_for in ["github-push", "github-pull-request"]'
|
||||
then:
|
||||
name: "Decision Task"
|
||||
description: 'The task that creates all of the other tasks in the task graph'
|
||||
else:
|
||||
$if: 'tasks_for == "action"'
|
||||
then:
|
||||
name: "Action: ${action.title}"
|
||||
description: |
|
||||
${action.description}
|
||||
|
||||
Action triggered by clientID `${clientId}`
|
||||
else:
|
||||
name: "Decision Task for cron job ${cron.job_name}"
|
||||
description: 'Created by a [cron task](https://firefox-ci-tc.services.mozilla.com/tasks/${cron.task_id})'
|
||||
provisionerId: "${trustDomain}-${level}"
|
||||
workerType: "decision-gcp"
|
||||
tags:
|
||||
$if: 'tasks_for in ["github-push", "github-pull-request"]'
|
||||
then:
|
||||
kind: decision-task
|
||||
else:
|
||||
$if: 'tasks_for == "action"'
|
||||
then:
|
||||
kind: 'action-callback'
|
||||
else:
|
||||
$if: 'tasks_for == "cron"'
|
||||
then:
|
||||
kind: cron-task
|
||||
routes:
|
||||
$flattenDeep:
|
||||
- checks
|
||||
- $if: 'level == "3" || repoUrl == "https://github.com/mozilla-releng/staging-fenix"'
|
||||
then:
|
||||
- tc-treeherder.v2.${project}.${head_sha}
|
||||
# TODO Bug 1601928: Make this scope fork-friendly once ${project} is better defined. This will enable
|
||||
# staging release promotion on forks.
|
||||
- $if: 'tasks_for == "github-push"'
|
||||
then:
|
||||
- index.${trustDomain}.v2.${project}.branch.${short_head_branch}.latest.taskgraph.decision
|
||||
- index.${trustDomain}.v2.${project}.branch.${short_head_branch}.revision.${head_sha}.taskgraph.decision
|
||||
- index.${trustDomain}.v2.${project}.revision.${head_sha}.taskgraph.decision
|
||||
- $if: 'tasks_for == "cron"'
|
||||
then:
|
||||
# cron context provides ${head_branch} as a short one
|
||||
- index.${trustDomain}.v2.${project}.branch.${head_branch}.latest.taskgraph.decision-${cron.job_name}
|
||||
- index.${trustDomain}.v2.${project}.branch.${head_branch}.revision.${head_sha}.taskgraph.decision-${cron.job_name}
|
||||
- index.${trustDomain}.v2.${project}.branch.${head_branch}.revision.${head_sha}.taskgraph.cron.${ownTaskId}
|
||||
scopes:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then:
|
||||
# `https://` is 8 characters so, ${repoUrl[8:]} is the repository without the protocol.
|
||||
- 'assume:repo:${repoUrl[8:]}:branch:${short_head_branch}'
|
||||
else:
|
||||
$if: 'tasks_for == "github-pull-request"'
|
||||
then:
|
||||
- 'assume:repo:github.com/${event.pull_request.base.repo.full_name}:pull-request'
|
||||
else:
|
||||
$if: 'tasks_for == "action"'
|
||||
then:
|
||||
# when all actions are hooks, we can calculate this directly rather than using a variable
|
||||
- '${action.repo_scope}'
|
||||
else:
|
||||
- 'assume:repo:${repoUrl[8:]}:cron:${cron.job_name}'
|
||||
|
||||
requires: all-completed
|
||||
priority: lowest
|
||||
retries: 5
|
||||
payload:
|
||||
env:
|
||||
# run-task uses these to check out the source; the inputs
|
||||
# to `mach taskgraph decision` are all on the command line.
|
||||
$merge:
|
||||
- MOBILE_BASE_REPOSITORY: '${baseRepoUrl}'
|
||||
MOBILE_HEAD_REPOSITORY: '${repoUrl}'
|
||||
MOBILE_HEAD_REF: '${head_branch}'
|
||||
MOBILE_HEAD_REV: '${head_sha}'
|
||||
MOBILE_REPOSITORY_TYPE: git
|
||||
MOBILE_PIP_REQUIREMENTS: taskcluster/requirements.txt
|
||||
MOZ_AUTOMATION: "1"
|
||||
REPOSITORIES: {$json: {mobile: "Fenix"}}
|
||||
HG_STORE_PATH: /builds/worker/checkouts/hg-store
|
||||
ANDROID_SDK_ROOT: /builds/worker/android-sdk
|
||||
- $if: 'tasks_for in ["github-pull-request"]'
|
||||
then:
|
||||
MOBILE_PULL_REQUEST_NUMBER: '${event.pull_request.number}'
|
||||
- $if: 'tasks_for == "action"'
|
||||
then:
|
||||
ACTION_TASK_GROUP_ID: '${action.taskGroupId}' # taskGroupId of the target task
|
||||
ACTION_TASK_ID: {$json: {$eval: 'taskId'}} # taskId of the target task (JSON-encoded)
|
||||
ACTION_INPUT: {$json: {$eval: 'input'}}
|
||||
ACTION_CALLBACK: '${action.cb_name}'
|
||||
features:
|
||||
taskclusterProxy: true
|
||||
chainOfTrust: true
|
||||
# Note: This task is built server side without the context or tooling that
|
||||
# exist in tree so we must hard code the hash
|
||||
image: mozillareleases/taskgraph:decision-mobile-625975b642c148be4c6f1d8ee5cedf7399f5d0dd33d275ff69d5934e3082d4a9@sha256:bfb26700182486e1c6c52701baea6f386fa39e5e25417423c27845933605ad43
|
||||
|
||||
maxRunTime: 1800
|
||||
|
||||
command:
|
||||
- /usr/local/bin/run-task
|
||||
- '--mobile-checkout=/builds/worker/checkouts/src'
|
||||
- '--task-cwd=/builds/worker/checkouts/src'
|
||||
- '--'
|
||||
- bash
|
||||
- -cx
|
||||
- $let:
|
||||
extraArgs:
|
||||
$if: 'tasks_for == "cron"'
|
||||
then: '${cron.quoted_args}'
|
||||
else: ''
|
||||
in:
|
||||
$if: 'tasks_for == "action"'
|
||||
then: >
|
||||
taskcluster/scripts/decision-install-sdk.sh &&
|
||||
ln -s /builds/worker/artifacts artifacts &&
|
||||
~/.local/bin/taskgraph action-callback
|
||||
else: >
|
||||
taskcluster/scripts/decision-install-sdk.sh &&
|
||||
ln -s /builds/worker/artifacts artifacts &&
|
||||
~/.local/bin/taskgraph decision
|
||||
--pushlog-id='0'
|
||||
--pushdate='0'
|
||||
--project='${project}'
|
||||
--message=""
|
||||
--owner='${ownerEmail}'
|
||||
--level='${level}'
|
||||
--base-repository="$MOBILE_BASE_REPOSITORY"
|
||||
--head-repository="$MOBILE_HEAD_REPOSITORY"
|
||||
--head-ref="$MOBILE_HEAD_REF"
|
||||
--head-rev="$MOBILE_HEAD_REV"
|
||||
--repository-type="$MOBILE_REPOSITORY_TYPE"
|
||||
--tasks-for='${tasks_for}'
|
||||
${extraArgs}
|
||||
|
||||
artifacts:
|
||||
'public':
|
||||
type: 'directory'
|
||||
path: '/builds/worker/artifacts'
|
||||
expires:
|
||||
$fromNow: '1 year'
|
||||
'public/docker-contexts':
|
||||
type: 'directory'
|
||||
path: '/builds/worker/checkouts/src/docker-contexts'
|
||||
# This needs to be at least the deadline of the
|
||||
# decision task + the docker-image task deadlines.
|
||||
# It is set to a week to allow for some time for
|
||||
# debugging, but they are not useful long-term.
|
||||
expires:
|
||||
$fromNow: '7 day'
|
||||
|
||||
extra:
|
||||
$merge:
|
||||
- treeherder:
|
||||
$merge:
|
||||
- machine:
|
||||
platform: gecko-decision
|
||||
- $if: 'tasks_for in ["github-push", "github-pull-request"]'
|
||||
then:
|
||||
symbol: D
|
||||
else:
|
||||
$if: 'tasks_for == "action"'
|
||||
then:
|
||||
groupName: 'action-callback'
|
||||
groupSymbol: AC
|
||||
symbol: "${action.symbol}"
|
||||
else:
|
||||
groupSymbol: cron
|
||||
symbol: "${cron.job_symbol}"
|
||||
- $if: 'tasks_for == "action"'
|
||||
then:
|
||||
parent: '${action.taskGroupId}'
|
||||
action:
|
||||
name: '${action.name}'
|
||||
context:
|
||||
taskGroupId: '${action.taskGroupId}'
|
||||
taskId: {$eval: 'taskId'}
|
||||
input: {$eval: 'input'}
|
||||
clientId: {$eval: 'clientId'}
|
||||
- $if: 'tasks_for == "cron"'
|
||||
then:
|
||||
cron: {$json: {$eval: 'cron'}}
|
||||
- tasks_for: '${tasks_for}'
|
@ -1,16 +0,0 @@
|
||||
language: android
|
||||
dist: trusty
|
||||
script:
|
||||
# Prepare SDK
|
||||
- sudo mkdir -p /usr/local/android-sdk/licenses/
|
||||
- sudo touch /usr/local/android-sdk/licenses/android-sdk-license
|
||||
- echo "8933bad161af4178b1185d1a37fbf41ea5269c55" | sudo tee -a /usr/local/android-sdk/licenses/android-sdk-license
|
||||
- echo "d56f5187479451eabf01fb78af6dfcb131a6481e" | sudo tee -a /usr/local/android-sdk/licenses/android-sdk-license
|
||||
- echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" | sudo tee -a /usr/local/android-sdk/licenses/android-sdk-license
|
||||
# The build needs this but Gradle refuses to fetch it.
|
||||
- sdkmanager "ndk;21.0.6113669"
|
||||
# Run tests
|
||||
- ./gradlew -q testDebug 2>&1
|
||||
# Make sure a release build builds
|
||||
- ./gradlew assembleForkRelease -PversionName="$(git describe --tags HEAD)"
|
||||
|
@ -1,373 +0,0 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
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/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
@ -0,0 +1 @@
|
||||
Subproject commit 557610ccc734506d89fab0e136fd2038913abb6c
|
@ -0,0 +1,267 @@
|
||||
---
|
||||
features:
|
||||
nimbus-system:
|
||||
description: |
|
||||
Configuration of the Nimbus System in Fenix.
|
||||
variables:
|
||||
refresh-interval-foreground:
|
||||
description: |
|
||||
The minimum interval in minutes between fetching experiment
|
||||
recipes in the foreground.
|
||||
type: Int
|
||||
default: 60 # 1 hour
|
||||
|
||||
messaging:
|
||||
description: |
|
||||
Configuration for the messaging system.
|
||||
|
||||
In practice this is a set of growable lookup tables for the
|
||||
message controller to piece together.
|
||||
|
||||
variables:
|
||||
message-under-experiment:
|
||||
description: Id or prefix of the message under experiment.
|
||||
type: Option<String>
|
||||
default: null
|
||||
|
||||
messages:
|
||||
description: A growable collection of messages
|
||||
type: Map<String, MessageData>
|
||||
default: {}
|
||||
|
||||
triggers:
|
||||
description: >
|
||||
A collection of out the box trigger
|
||||
expressions. Each entry maps to a
|
||||
valid JEXL expression.
|
||||
type: Map<String, String>
|
||||
default: {}
|
||||
styles:
|
||||
description: >
|
||||
A map of styles to configure message
|
||||
appearance.
|
||||
type: Map<String, StyleData>
|
||||
default: {}
|
||||
|
||||
actions:
|
||||
type: Map<String, String>
|
||||
description: A growable map of action URLs.
|
||||
default: {}
|
||||
on-control:
|
||||
type: ControlMessageBehavior
|
||||
description: What should be displayed when a control message is selected.
|
||||
default: show-next-message
|
||||
notification-config:
|
||||
description: Configuration of the notification worker for all notification messages.
|
||||
type: NotificationConfig
|
||||
default: {}
|
||||
defaults:
|
||||
- value:
|
||||
triggers:
|
||||
# Using attributes built into the Nimbus SDK
|
||||
USER_RECENTLY_INSTALLED: days_since_install < 7
|
||||
USER_RECENTLY_UPDATED: days_since_update < 7 && days_since_install != days_since_update
|
||||
USER_TIER_ONE_COUNTRY: ('US' in locale || 'GB' in locale || 'CA' in locale || 'DE' in locale || 'FR' in locale)
|
||||
USER_EN_SPEAKER: "'en' in locale"
|
||||
USER_ES_SPEAKER: "'es' in locale"
|
||||
USER_DE_SPEAKER: "'de' in locale"
|
||||
USER_FR_SPEAKER: "'fr' in locale"
|
||||
DEVICE_ANDROID: os == 'Android'
|
||||
DEVICE_IOS: os == 'iOS'
|
||||
ALWAYS: "true"
|
||||
NEVER: "false"
|
||||
DAY_1_AFTER_INSTALL: days_since_install == 1
|
||||
DAY_2_AFTER_INSTALL: days_since_install == 2
|
||||
DAY_3_AFTER_INSTALL: days_since_install == 3
|
||||
DAY_4_AFTER_INSTALL: days_since_install == 4
|
||||
DAY_5_AFTER_INSTALL: days_since_install == 5
|
||||
|
||||
# Using custom attributes for the browser
|
||||
I_AM_DEFAULT_BROWSER: "is_default_browser"
|
||||
I_AM_NOT_DEFAULT_BROWSER: "is_default_browser == false"
|
||||
USER_ESTABLISHED_INSTALL: "number_of_app_launches >=4"
|
||||
|
||||
FUNNEL_PAID: "adjust_campaign != ''"
|
||||
FUNNEL_ORGANIC: "adjust_campaign == ''"
|
||||
|
||||
# Using Glean events, specific to the browser
|
||||
INACTIVE_1_DAY: "'app_launched'|eventLastSeen('Hours') >= 24"
|
||||
INACTIVE_2_DAYS: "'app_launched'|eventLastSeen('Days', 0) >= 2"
|
||||
INACTIVE_3_DAYS: "'app_launched'|eventLastSeen('Days', 0) >= 3"
|
||||
INACTIVE_4_DAYS: "'app_launched'|eventLastSeen('Days', 0) >= 4"
|
||||
INACTIVE_5_DAYS: "'app_launched'|eventLastSeen('Days', 0) >= 5"
|
||||
|
||||
# Has the user signed in the last 4 years
|
||||
FXA_SIGNED_IN: "'sync_auth.sign_in'|eventLastSeen('Years', 0) <= 4"
|
||||
FXA_NOT_SIGNED_IN: "'sync_auth.sign_in'|eventLastSeen('Years', 0) > 4"
|
||||
|
||||
# https://mozilla-hub.atlassian.net/wiki/spaces/FJT/pages/11469471/Core+Active
|
||||
USER_INFREQUENT: "'app_launched'|eventCountNonZero('Days', 28) >= 1 && 'app_launched'|eventCountNonZero('Days', 28) < 7"
|
||||
USER_CASUAL: "'app_launched'|eventCountNonZero('Days', 28) >= 7 && 'app_launched'|eventCountNonZero('Days', 28) < 14"
|
||||
USER_REGULAR: "'app_launched'|eventCountNonZero('Days', 28) >= 14 && 'app_launched'|eventCountNonZero('Days', 28) < 21"
|
||||
USER_CORE_ACTIVE: "'app_launched'|eventCountNonZero('Days', 28) >= 21"
|
||||
|
||||
LAUNCHED_ONCE_THIS_WEEK: "'app_launched'|eventSum('Days', 7) == 1"
|
||||
|
||||
actions:
|
||||
ENABLE_PRIVATE_BROWSING: ://enable_private_browsing
|
||||
INSTALL_SEARCH_WIDGET: ://install_search_widget
|
||||
MAKE_DEFAULT_BROWSER: ://make_default_browser
|
||||
VIEW_BOOKMARKS: ://urls_bookmarks
|
||||
VIEW_COLLECTIONS: ://home_collections
|
||||
VIEW_HISTORY: ://urls_history
|
||||
VIEW_HOMESCREEN: ://home
|
||||
OPEN_SETTINGS_ACCESSIBILITY: ://settings_accessibility
|
||||
OPEN_SETTINGS_ADDON_MANAGER: ://settings_addon_manager
|
||||
OPEN_SETTINGS_DELETE_BROWSING_DATA: ://settings_delete_browsing_data
|
||||
OPEN_SETTINGS_LOGINS: ://settings_logins
|
||||
OPEN_SETTINGS_NOTIFICATIONS: ://settings_notifications
|
||||
OPEN_SETTINGS_PRIVACY: ://settings_privacy
|
||||
OPEN_SETTINGS_SEARCH_ENGINE: ://settings_search_engine
|
||||
OPEN_SETTINGS_TRACKING_PROTECTION: ://settings_tracking_protection
|
||||
OPEN_SETTINGS_WALLPAPERS: ://settings_wallpapers
|
||||
OPEN_SETTINGS: ://settings
|
||||
TURN_ON_SYNC: ://turn_on_sync
|
||||
styles:
|
||||
DEFAULT:
|
||||
priority: 50
|
||||
max-display-count: 5
|
||||
SURVEY:
|
||||
priority: 55
|
||||
max-display-count: 10
|
||||
PERSISTENT:
|
||||
priority: 50
|
||||
max-display-count: 20
|
||||
WARNING:
|
||||
priority: 60
|
||||
max-display-count: 10
|
||||
URGENT:
|
||||
priority: 100
|
||||
max-display-count: 10
|
||||
NOTIFICATION:
|
||||
priority: 50
|
||||
max-display-count: 1
|
||||
messages:
|
||||
default-browser:
|
||||
text: default_browser_experiment_card_text
|
||||
surface: homescreen
|
||||
action: "MAKE_DEFAULT_BROWSER"
|
||||
trigger: [ "I_AM_NOT_DEFAULT_BROWSER","USER_ESTABLISHED_INSTALL" ]
|
||||
style: "PERSISTENT"
|
||||
button-label: preferences_set_as_default_browser
|
||||
default-browser-notification:
|
||||
title: nimbus_notification_default_browser_title
|
||||
text: nimbus_notification_default_browser_text
|
||||
surface: notification
|
||||
style: NOTIFICATION
|
||||
trigger:
|
||||
- I_AM_NOT_DEFAULT_BROWSER
|
||||
- DAY_3_AFTER_INSTALL
|
||||
action: MAKE_DEFAULT_BROWSER
|
||||
|
||||
- channel: developer
|
||||
value:
|
||||
styles:
|
||||
DEFAULT:
|
||||
priority: 50
|
||||
max-display-count: 100
|
||||
EXPIRES_QUICKLY:
|
||||
priority: 100
|
||||
max-display-count: 1
|
||||
notification-config:
|
||||
refresh-interval: 120 # minutes (2 hours)
|
||||
|
||||
objects:
|
||||
MessageData:
|
||||
description: >
|
||||
An object to describe a message. It uses human
|
||||
readable strings to describe the triggers, action and
|
||||
style of the message as well as the text of the message
|
||||
and call to action.
|
||||
fields:
|
||||
action:
|
||||
type: Text
|
||||
description: >
|
||||
A URL of a page or a deeplink.
|
||||
This may have substitution variables in.
|
||||
# This should never be defaulted.
|
||||
default: empty_string
|
||||
title:
|
||||
type: Option<Text>
|
||||
description: "The title text displayed to the user"
|
||||
default: null
|
||||
text:
|
||||
type: Text
|
||||
description: "The message text displayed to the user"
|
||||
# This should never be defaulted.
|
||||
default: empty_string
|
||||
is-control:
|
||||
type: Boolean
|
||||
description: "Indicates if this message is the control message, if true shouldn't be displayed"
|
||||
default: false
|
||||
button-label:
|
||||
type: Option<Text>
|
||||
description: >
|
||||
The text on the button. If no text
|
||||
is present, the whole message is clickable.
|
||||
default: null
|
||||
style:
|
||||
type: String
|
||||
description: >
|
||||
The style as described in a
|
||||
`StyleData` from the styles table.
|
||||
default: DEFAULT
|
||||
surface:
|
||||
description:
|
||||
The surface identifier for this message.
|
||||
type: MessageSurfaceId
|
||||
default: homescreen
|
||||
trigger:
|
||||
type: List<String>
|
||||
description: >
|
||||
A list of strings corresponding to
|
||||
targeting expressions. The message will be
|
||||
shown if all expressions `true`.
|
||||
default: []
|
||||
StyleData:
|
||||
description: >
|
||||
A group of properties (predominantly visual) to
|
||||
describe the style of the message.
|
||||
fields:
|
||||
priority:
|
||||
type: Int
|
||||
description: >
|
||||
The importance of this message.
|
||||
0 is not very important, 100 is very important.
|
||||
default: 50
|
||||
max-display-count:
|
||||
type: Int
|
||||
description: >
|
||||
How many sessions will this message be shown to the user
|
||||
before it is expired.
|
||||
default: 5
|
||||
NotificationConfig:
|
||||
description: Attributes controlling the global configuration of notification messages.
|
||||
fields:
|
||||
refresh-interval:
|
||||
type: Int
|
||||
description: >
|
||||
How often, in minutes, the notification message worker will wake up and check for new
|
||||
messages.
|
||||
default: 240 # 4 hours
|
||||
|
||||
enums:
|
||||
ControlMessageBehavior:
|
||||
description: An enum to influence what should be displayed when a control message is selected.
|
||||
variants:
|
||||
show-next-message:
|
||||
description: The next eligible message should be shown.
|
||||
show-none:
|
||||
description: The surface should show no message.
|
||||
MessageSurfaceId:
|
||||
description: The identity of a message surface
|
||||
variants:
|
||||
homescreen:
|
||||
description: A banner in the homescreen.
|
||||
notification:
|
||||
description: A local notification in the background, like a push notification.
|
@ -0,0 +1,272 @@
|
||||
---
|
||||
about:
|
||||
description: Nimbus Feature Manifest for Fenix (Firefox Android)
|
||||
kotlin:
|
||||
package: org.mozilla.fenix
|
||||
class: .nimbus.FxNimbus
|
||||
channels:
|
||||
- release
|
||||
- beta
|
||||
- nightly
|
||||
- developer
|
||||
- forkRelease
|
||||
includes:
|
||||
- messaging.fml.yaml
|
||||
features:
|
||||
homescreen:
|
||||
description: The homescreen that the user goes to when they press home or new tab.
|
||||
variables:
|
||||
sections-enabled:
|
||||
description: "This property provides a lookup table of whether or not the given section should be enabled.
|
||||
If the section is enabled, it should be toggleable in the settings screen, and on by default."
|
||||
type: Map<HomeScreenSection, Boolean>
|
||||
default:
|
||||
{
|
||||
"top-sites": true,
|
||||
"jump-back-in": true,
|
||||
"recently-saved": true,
|
||||
"recent-explorations": true,
|
||||
"pocket": true,
|
||||
"pocket-sponsored-stories": true,
|
||||
}
|
||||
defaults:
|
||||
- channel: nightly
|
||||
value: {
|
||||
"sections-enabled": {
|
||||
"top-sites": true,
|
||||
"jump-back-in": true,
|
||||
"recently-saved": true,
|
||||
"recent-explorations": true,
|
||||
"pocket": true,
|
||||
}
|
||||
}
|
||||
nimbus-validation:
|
||||
description: "A feature that does not correspond to an application feature suitable for showing
|
||||
that Nimbus is working. This should never be used in production."
|
||||
variables:
|
||||
settings-title:
|
||||
description: The title of displayed in the Settings screen and app menu.
|
||||
type: Text
|
||||
default: browser_menu_settings
|
||||
settings-punctuation:
|
||||
description: The emoji displayed in the Settings screen title.
|
||||
type: String
|
||||
default: ""
|
||||
settings-icon:
|
||||
description: The drawable displayed in the app menu for Settings
|
||||
type: String
|
||||
default: mozac_ic_settings
|
||||
search-term-groups:
|
||||
description: A feature allowing the grouping of URLs around the search term that it came from.
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the feature shows up on the homescreen and on the new tab screen.
|
||||
type: Boolean
|
||||
default: false
|
||||
defaults:
|
||||
- channel: nightly
|
||||
value:
|
||||
enabled: true
|
||||
- channel: developer
|
||||
value:
|
||||
enabled: true
|
||||
mr2022:
|
||||
description: Features for MR 2022.
|
||||
variables:
|
||||
sections-enabled:
|
||||
description: "This property provides a lookup table of whether or not the given section should be enabled."
|
||||
type: Map<MR2022Section, Boolean>
|
||||
default:
|
||||
{
|
||||
"home-onboarding-dialog-existing-users": true,
|
||||
"sync-cfr": true,
|
||||
"wallpapers-selection-tool": true,
|
||||
"jump-back-in-cfr": true,
|
||||
"tcp-cfr": true,
|
||||
"tcp-feature": true,
|
||||
}
|
||||
defaults:
|
||||
- channel: developer
|
||||
value: {
|
||||
"sections-enabled": {
|
||||
"home-onboarding-dialog-existing-users": true,
|
||||
"sync-cfr": true,
|
||||
"wallpapers-selection-tool": true,
|
||||
"jump-back-in-cfr": true,
|
||||
}
|
||||
}
|
||||
|
||||
cookie-banners:
|
||||
description: Features for cookie banner handling.
|
||||
variables:
|
||||
sections-enabled:
|
||||
description: "This property provides a lookup table of whether or not the given section should be enabled."
|
||||
type: Map<CookieBannersSection, Int>
|
||||
default:
|
||||
{
|
||||
"feature-ui": 0,
|
||||
"feature-setting-value": 0,
|
||||
"dialog-re-engage-time": 4
|
||||
}
|
||||
defaults:
|
||||
- channel: developer
|
||||
value: {
|
||||
"sections-enabled": {
|
||||
"feature-ui": 1,
|
||||
"feature-setting-value": 0,
|
||||
"dialog-re-engage-time": 4
|
||||
}
|
||||
}
|
||||
- channel: nightly
|
||||
value: {
|
||||
"sections-enabled": {
|
||||
"feature-ui": 1,
|
||||
"feature-setting-value": 0,
|
||||
"dialog-re-engage-time": 4
|
||||
}
|
||||
}
|
||||
unified-search:
|
||||
description: A feature allowing user to easily search for specified results directly in the search bar.
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the feature shows up in the search bar.
|
||||
type: Boolean
|
||||
default: false
|
||||
defaults:
|
||||
- channel: nightly
|
||||
value:
|
||||
enabled: false
|
||||
|
||||
client-deduplication:
|
||||
description: A feature to control the sending of the client-deduplication ping.
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the ping will be sent.
|
||||
type: Boolean
|
||||
default: false
|
||||
defaults:
|
||||
- channel: nightly
|
||||
value:
|
||||
enabled: false
|
||||
- channel: developer
|
||||
value:
|
||||
enabled: false
|
||||
|
||||
client-deduplication:
|
||||
description: A feature to control the sending of the client-deduplication ping.
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the ping will be sent.
|
||||
type: Boolean
|
||||
default: false
|
||||
defaults:
|
||||
- channel: nightly
|
||||
value:
|
||||
enabled: false
|
||||
- channel: developer
|
||||
value:
|
||||
enabled: false
|
||||
|
||||
growth-data:
|
||||
description: A feature measuring campaign growth data
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the feature is active
|
||||
type: Boolean
|
||||
default: false
|
||||
defaults:
|
||||
- channel: release
|
||||
value:
|
||||
enabled: true
|
||||
|
||||
re-engagement-notification:
|
||||
description: A feature that shows the re-engagement notification if the user is inactive.
|
||||
variables:
|
||||
enabled:
|
||||
description: If true, the re-engagement notification is shown to the inactive user.
|
||||
type: Boolean
|
||||
default: false
|
||||
type:
|
||||
description: The type of re-engagement notification that is shown to the inactive user.
|
||||
type: Int
|
||||
default: 0
|
||||
|
||||
pre-permission-notification-prompt:
|
||||
description: A feature that shows the pre-permission notification prompt.
|
||||
variables:
|
||||
enabled:
|
||||
description: if true, the pre-permission notification prompt is shown to the user.
|
||||
type: Boolean
|
||||
default: true
|
||||
|
||||
onboarding:
|
||||
description: "A feature that configures the new user onboarding page.
|
||||
Note that onboarding is a **first run** feature, and should only be modified by first run experiments."
|
||||
variables:
|
||||
order:
|
||||
description: Determines the order of the onboarding page panels
|
||||
type: List<OnboardingPanel>
|
||||
default: ["themes", "toolbar-placement", "sync", "tcp", "privacy-notice"]
|
||||
|
||||
types:
|
||||
objects: {}
|
||||
|
||||
enums:
|
||||
HomeScreenSection:
|
||||
description: The identifiers for the sections of the homescreen.
|
||||
variants:
|
||||
top-sites:
|
||||
description: The frecency and pinned sites.
|
||||
recently-saved:
|
||||
description: The sites the user has bookmarked recently.
|
||||
jump-back-in:
|
||||
description: The tabs the user was looking immediately before being interrupted.
|
||||
recent-explorations:
|
||||
description: The tab groups
|
||||
pocket:
|
||||
description: The pocket section. This should only be available in the US.
|
||||
pocket-sponsored-stories:
|
||||
description: Subsection of the Pocket homescreen section which shows sponsored stories.
|
||||
|
||||
MR2022Section:
|
||||
description: The identifiers for the sections of the MR 2022.
|
||||
variants:
|
||||
home-onboarding-dialog-existing-users:
|
||||
description: Home onboarding dialog for upgraded users.
|
||||
sync-cfr:
|
||||
description: CFR for the first time you see a synced tab on the home screen.
|
||||
wallpapers-selection-tool:
|
||||
description: Wallpapers selection dialog tool for the home screen.
|
||||
jump-back-in-cfr:
|
||||
description: Jump back-in onboarding message.
|
||||
tcp-cfr:
|
||||
description: CFR for the first time you use the browse with Total Cookie Protection on the browser screen.
|
||||
tcp-feature:
|
||||
description: Controls the Total Cookie Protection feature.
|
||||
CookieBannersSection:
|
||||
description: The identifiers for the sections of the MR 2022.
|
||||
variants:
|
||||
feature-ui:
|
||||
description: An integer either 0 or 1 indicating if the UI for cookie banner handling should be visible,
|
||||
0 to hide the UI and 1 to show the UI. The actual UI is composed by cookie banner section
|
||||
in the settings page, the toolbar section and the re-engagement dialog.
|
||||
feature-setting-value:
|
||||
description: An integer either 0 or 1 indicating if cookie banner setting should be enabled or disabled,
|
||||
0 for setting the value to disabled, 1 for enabling the setting with the value reject_all.
|
||||
dialog-re-engage-time:
|
||||
description: An integer indicating the number of hours that needs to happen before
|
||||
the re-engagement dialog shows again since the last seen, for example if set to 4
|
||||
that means if the users has seen the dialog, it will see it 4 hours later.
|
||||
OnboardingPanel:
|
||||
description: The types of onboarding panels in the onboarding page
|
||||
variants:
|
||||
themes:
|
||||
description: The themes onboarding panel where users pick themes
|
||||
toolbar-placement:
|
||||
description: The onboarding panel where users choose their toolbar placement (bottom or top)
|
||||
sync:
|
||||
description: The onboarding panel where users can sign in to sync
|
||||
tcp:
|
||||
description: The onboarding panel where users can choose their total cookie protection settings
|
||||
privacy-notice:
|
||||
description: The onboarding panel where users can tap to view our privacy notice.
|
@ -0,0 +1,111 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
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.searchScreen
|
||||
|
||||
class AddToHomeScreenTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val downloadTestPage =
|
||||
"https://storage.googleapis.com/mobile_test_assets/test_app/downloads.html"
|
||||
private val pdfFileName = "washington.pdf"
|
||||
private val pdfFileURL = "storage.googleapis.com/mobile_test_assets/public/washington.pdf"
|
||||
private val pdfFileContent = "Washington Crossing the Delaware"
|
||||
|
||||
@get:Rule
|
||||
val activityIntentTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
// Verifies the Add to home screen option in a tab's 3 dot menu
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun mainMenuAddToHomeScreenTest() {
|
||||
val website = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val shortcutTitle = TestHelper.generateRandomString(5)
|
||||
|
||||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(website.url) {
|
||||
}.openThreeDotMenu {
|
||||
expandMenu()
|
||||
}.openAddToHomeScreen {
|
||||
clickCancelShortcutButton()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
expandMenu()
|
||||
}.openAddToHomeScreen {
|
||||
verifyShortcutTextFieldTitle("Test_Page_1")
|
||||
addShortcutName(shortcutTitle)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut(shortcutTitle) {
|
||||
verifyUrl(website.url.toString())
|
||||
verifyTabCounter("1")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun addPrivateBrowsingShortcutTest() {
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
}.triggerPrivateBrowsingShortcutPrompt {
|
||||
verifyNoThanksPrivateBrowsingShortcutButton()
|
||||
verifyAddPrivateBrowsingShortcutButton()
|
||||
clickAddPrivateBrowsingShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut("Private ${TestHelper.appName}") {}
|
||||
searchScreen {
|
||||
verifySearchView()
|
||||
}.dismissSearchBar {
|
||||
verifyCommonMythsLink()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun addPDFToHomeScreenTest() {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(downloadTestPage.toUri()) {
|
||||
clickLinkMatchingText(pdfFileName)
|
||||
verifyUrl(pdfFileURL)
|
||||
verifyPageContent(pdfFileContent)
|
||||
}.openThreeDotMenu {
|
||||
expandMenu()
|
||||
}.openAddToHomeScreen {
|
||||
verifyShortcutTextFieldTitle(pdfFileName)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut(pdfFileName) {
|
||||
verifyUrl(pdfFileURL)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,406 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
class AddressAutofillTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val activityIntentTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyAddressAutofillTest() {
|
||||
val addressFormPage =
|
||||
TestAssetHelper.getAddressFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
clickSelectAddressButton()
|
||||
clickAddressSuggestion("Harrison Street")
|
||||
verifyAutofilledAddress("Harrison Street")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteSavedAddressTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
clickManageAddressesButton()
|
||||
clickSavedAddress("Mozilla")
|
||||
clickDeleteAddressButton()
|
||||
clickCancelDeleteAddressButton()
|
||||
clickDeleteAddressButton()
|
||||
clickConfirmDeleteAddressButton()
|
||||
verifyAddAddressButton()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAddAddressViewTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddAddressButton()
|
||||
verifyAddAddressView()
|
||||
}.goBackToAutofillSettings {
|
||||
verifyAutofillToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyEditAddressViewTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
clickManageAddressesButton()
|
||||
clickSavedAddress("Mozilla")
|
||||
verifyEditAddressView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAddressAutofillToggleTest() {
|
||||
val addressFormPage =
|
||||
TestAssetHelper.getAddressFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
verifySelectAddressButtonExists(true)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickSaveAndAutofillAddressesOption()
|
||||
verifyAddressAutofillSection(false, true)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
verifySelectAddressButtonExists(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyManageAddressesPromptOptionTest() {
|
||||
val addressFormPage =
|
||||
TestAssetHelper.getAddressFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
clickSelectAddressButton()
|
||||
}.clickManageAddressButton {
|
||||
verifyAutofillToolbarTitle()
|
||||
}.goBackToBrowser {
|
||||
verifySaveLoginPromptIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAddressAutofillSelectionTest() {
|
||||
val addressFormPage =
|
||||
TestAssetHelper.getAddressFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
clickManageAddressesButton()
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Android",
|
||||
"Test",
|
||||
"Name",
|
||||
"Fort Street",
|
||||
"San Jose",
|
||||
"Arizona",
|
||||
"95141",
|
||||
"United States",
|
||||
"777-7777",
|
||||
"fuu@bar.org",
|
||||
)
|
||||
verifyManageAddressesToolbarTitle()
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
clickSelectAddressButton()
|
||||
clickAddressSuggestion("Harrison Street")
|
||||
verifyAutofilledAddress("Harrison Street")
|
||||
clearAddressForm()
|
||||
clickStreetAddressTextBox()
|
||||
clickSelectAddressButton()
|
||||
clickAddressSuggestion("Fort Street")
|
||||
verifyAutofilledAddress("Fort Street")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySavedAddressCanBeEditedTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
clickManageAddressesButton()
|
||||
clickSavedAddress("Mozilla")
|
||||
fillAndSaveAddress(
|
||||
"Android",
|
||||
"Test",
|
||||
"Name",
|
||||
"Fort Street",
|
||||
"San Jose",
|
||||
"Arizona",
|
||||
"95141",
|
||||
"United States",
|
||||
"777-7777",
|
||||
"fuu@bar.org",
|
||||
)
|
||||
verifyManageAddressesToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyStateFieldUpdatesInAccordanceWithCountryFieldTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
verifyCountryOption("United States")
|
||||
verifyStateOption("Alabama")
|
||||
verifyCountryOptions("Canada", "United States")
|
||||
clickCountryOption("Canada")
|
||||
verifyStateOption("Alberta")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyFormFieldCanBeFilledManuallyTest() {
|
||||
val addressFormPage =
|
||||
TestAssetHelper.getAddressFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(addressFormPage.url) {
|
||||
clickStreetAddressTextBox()
|
||||
clickSelectAddressButton()
|
||||
clickAddressSuggestion("Harrison Street")
|
||||
verifyAutofilledAddress("Harrison Street")
|
||||
setTextForApartmentTextBox("Ap. 07")
|
||||
verifyManuallyFilledAddress("Ap. 07")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAutofillAddressSectionTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyAddressAutofillSection(true, false)
|
||||
clickAddAddressButton()
|
||||
fillAndSaveAddress(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"United States",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
verifyAddressAutofillSection(true, true)
|
||||
clickManageAddressesButton()
|
||||
verifyManageAddressesSection(
|
||||
"Mozilla",
|
||||
"Fenix",
|
||||
"Firefox",
|
||||
"Harrison Street",
|
||||
"San Francisco",
|
||||
"Alaska",
|
||||
"94105",
|
||||
"US",
|
||||
"555-5555",
|
||||
"foo@bar.com",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.restartApp
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
class CookieBannerReductionTest {
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyCookieBannerReductionTest() {
|
||||
val webSite = "voetbal24.be"
|
||||
|
||||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webSite.toUri()) {
|
||||
waitForPageToLoad()
|
||||
verifyCookieBannerExists(exists = true)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCookieBannerReductionSubMenu {
|
||||
verifyCookieBannerView(isCookieBannerReductionChecked = false)
|
||||
clickCookieBannerReductionToggle()
|
||||
verifyCheckedCookieBannerReductionToggle(isCookieBannerReductionChecked = true)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
browserScreen {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}
|
||||
|
||||
TestHelper.restartApp(activityTestRule)
|
||||
|
||||
browserScreen {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCookieBannerReductionSubMenu {
|
||||
clickCookieBannerReductionToggle()
|
||||
verifyCheckedCookieBannerReductionToggle(false)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyCookieBannerReductionInPrivateBrowsingTest() {
|
||||
val webSite = "voetbal24.be"
|
||||
|
||||
homeScreen {
|
||||
}.togglePrivateBrowsingMode()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webSite.toUri()) {
|
||||
waitForPageToLoad()
|
||||
verifyCookieBannerExists(exists = true)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCookieBannerReductionSubMenu {
|
||||
verifyCookieBannerView(isCookieBannerReductionChecked = false)
|
||||
clickCookieBannerReductionToggle()
|
||||
verifyCheckedCookieBannerReductionToggle(isCookieBannerReductionChecked = true)
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}
|
||||
|
||||
restartApp(activityTestRule)
|
||||
|
||||
homeScreen {
|
||||
}.openTabDrawer {
|
||||
}.openTab("Voetbal24") {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCookieBannerReductionSubMenu {
|
||||
clickCookieBannerReductionToggle()
|
||||
verifyCheckedCookieBannerReductionToggle(false)
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyCookieBannerExists(exists = false)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,639 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.bringAppToForeground
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.putAppToBackground
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import java.time.LocalDate
|
||||
|
||||
class CreditCardAutofillTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
object MockCreditCard1 {
|
||||
const val MOCK_CREDIT_CARD_NUMBER = "5555555555554444"
|
||||
const val MOCK_LAST_CARD_DIGITS = "4444"
|
||||
const val MOCK_NAME_ON_CARD = "Mastercard"
|
||||
const val MOCK_EXPIRATION_MONTH = "February"
|
||||
val MOCK_EXPIRATION_YEAR = (LocalDate.now().year + 1).toString()
|
||||
val MOCK_EXPIRATION_MONTH_AND_YEAR = "02/${(LocalDate.now().year + 1)}"
|
||||
}
|
||||
|
||||
object MockCreditCard2 {
|
||||
const val MOCK_CREDIT_CARD_NUMBER = "2720994326581252"
|
||||
const val MOCK_LAST_CARD_DIGITS = "1252"
|
||||
const val MOCK_NAME_ON_CARD = "Mastercard"
|
||||
const val MOCK_EXPIRATION_MONTH = "March"
|
||||
val MOCK_EXPIRATION_YEAR = (LocalDate.now().year + 2).toString()
|
||||
val MOCK_EXPIRATION_MONTH_AND_YEAR = "03/${(LocalDate.now().year + 2)}"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val activityIntentTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyCreditCardAutofillTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
// Opening Manage saved cards to dismiss here the Secure your credit prompt
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
}.goBackToAutofillSettings {
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
clickCreditCardSuggestion(MockCreditCard1.MOCK_LAST_CARD_DIGITS)
|
||||
verifyAutofilledCreditCard(MockCreditCard1.MOCK_CREDIT_CARD_NUMBER)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteSavedCreditCardUsingToolbarButtonTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickSavedCreditCard()
|
||||
clickDeleteCreditCardToolbarButton()
|
||||
clickConfirmDeleteCreditCardButton()
|
||||
verifyAddCreditCardsButton()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun cancelDeleteSavedCreditCardUsingToolbarButtonTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickSavedCreditCard()
|
||||
clickDeleteCreditCardToolbarButton()
|
||||
clickCancelDeleteCreditCardButton()
|
||||
verifyEditCreditCardToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteSavedCreditCardUsingMenuButtonTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickSavedCreditCard()
|
||||
clickDeleteCreditCardMenuButton()
|
||||
clickConfirmDeleteCreditCardButton()
|
||||
verifyAddCreditCardsButton()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun cancelDeleteSavedCreditCardUsingMenuButtonTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickSavedCreditCard()
|
||||
clickDeleteCreditCardMenuButton()
|
||||
clickCancelDeleteCreditCardButton()
|
||||
verifyEditCreditCardToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCreditCardsSectionTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyManageCreditCardsPromptOptionTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
}.clickManageCreditCardsButton {
|
||||
}.goBackToBrowser {
|
||||
verifySelectCreditCardPromptExists(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCreditCardsAutofillToggleTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
verifySelectCreditCardPromptExists(true)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
clickSaveAndAutofillCreditCardsOption()
|
||||
verifyCreditCardsAutofillSection(false, true)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
verifySelectCreditCardPromptExists(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyEditCardsViewTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickSavedCreditCard()
|
||||
verifyEditCreditCardView(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
}.goBackToSavedCreditCards {
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyEditedCardIsSavedTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickSavedCreditCard()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard2.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard2.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard2.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
clickCreditCardSuggestion(MockCreditCard2.MOCK_LAST_CARD_DIGITS)
|
||||
verifyAutofilledCreditCard(MockCreditCard2.MOCK_CREDIT_CARD_NUMBER)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCreditCardCannotBeSavedWithoutCardNumberTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickSavedCreditCard()
|
||||
clearCreditCardNumber()
|
||||
clickSaveCreditCardToolbarButton()
|
||||
verifyEditCreditCardToolbarTitle()
|
||||
verifyCreditCardNumberErrorMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCreditCardCannotBeSavedWithoutNameOnCardTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickSavedCreditCard()
|
||||
clearNameOnCreditCard()
|
||||
clickSaveCreditCardToolbarButton()
|
||||
verifyEditCreditCardToolbarTitle()
|
||||
verifyNameOnCreditCardErrorMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyMultipleCreditCardsCanBeSavedTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard2.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard2.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard2.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard2.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
verifyCreditCardSuggestion(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard2.MOCK_LAST_CARD_DIGITS,
|
||||
)
|
||||
clickCreditCardSuggestion(MockCreditCard2.MOCK_LAST_CARD_DIGITS)
|
||||
verifyAutofilledCreditCard(MockCreditCard2.MOCK_CREDIT_CARD_NUMBER)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyDoNotSaveCreditCardFromFormTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickCancelCreditCardPromptButton()
|
||||
verifyUpdateOrSaveCreditCardPromptExists(exists = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySaveCreditCardFromFormTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
clickUpdateOrSaveCreditCardPromptButton()
|
||||
verifyUpdateOrSaveCreditCardPromptExists(exists = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, true)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCancelCreditCardUpdatePromptTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard2.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard2.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard2.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
// Opening Manage saved cards to dismiss here the Secure your credit prompt
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
clickCreditCardSuggestion(MockCreditCard2.MOCK_LAST_CARD_DIGITS)
|
||||
verifyAutofilledCreditCard(MockCreditCard2.MOCK_CREDIT_CARD_NUMBER)
|
||||
changeCreditCardExpiryDate(MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR)
|
||||
clickCreditCardFormSubmitButton()
|
||||
clickCancelCreditCardPromptButton()
|
||||
verifyUpdateOrSaveCreditCardPromptExists(false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, true)
|
||||
clickManageSavedCreditCardsButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard2.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyConfirmCreditCardUpdatePromptTest() {
|
||||
val creditCardFormPage = TestAssetHelper.getCreditCardFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard2.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard2.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard2.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard2.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
// Opening Manage saved cards to dismiss here the Secure your credit prompt
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(creditCardFormPage.url) {
|
||||
clickCreditCardNumberTextBox()
|
||||
clickSelectCreditCardButton()
|
||||
clickCreditCardSuggestion(MockCreditCard2.MOCK_LAST_CARD_DIGITS)
|
||||
verifyAutofilledCreditCard(MockCreditCard2.MOCK_CREDIT_CARD_NUMBER)
|
||||
changeCreditCardExpiryDate(MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR)
|
||||
clickCreditCardFormSubmitButton()
|
||||
clickUpdateOrSaveCreditCardPromptButton()
|
||||
verifyUpdateOrSaveCreditCardPromptExists(false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, true)
|
||||
clickManageSavedCreditCardsButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard2.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySavedCreditCardsRedirectionToAutofillAfterInterruptionTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
verifySavedCreditCardsSection(
|
||||
MockCreditCard1.MOCK_LAST_CARD_DIGITS,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH_AND_YEAR,
|
||||
)
|
||||
putAppToBackground()
|
||||
bringAppToForeground()
|
||||
verifyAutofillToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyEditCreditCardRedirectionToAutofillAfterInterruptionTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openAutofillSubMenu {
|
||||
verifyCreditCardsAutofillSection(true, false)
|
||||
clickAddCreditCardButton()
|
||||
fillAndSaveCreditCard(
|
||||
MockCreditCard1.MOCK_CREDIT_CARD_NUMBER,
|
||||
MockCreditCard1.MOCK_NAME_ON_CARD,
|
||||
MockCreditCard1.MOCK_EXPIRATION_MONTH,
|
||||
MockCreditCard1.MOCK_EXPIRATION_YEAR,
|
||||
)
|
||||
clickManageSavedCreditCardsButton()
|
||||
clickSecuredCreditCardsLaterButton()
|
||||
clickSavedCreditCard()
|
||||
putAppToBackground()
|
||||
bringAppToForeground()
|
||||
verifyAutofillToolbarTitle()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/* 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 android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.gleanplumb.CustomAttributeProvider
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.nimbus.Messaging
|
||||
|
||||
/**
|
||||
* This test is to test the integrity of messages hardcoded in the FML.
|
||||
*
|
||||
* It tests if the trigger expressions are valid, all the fields are complete
|
||||
* and a simple check if they are localized (don't contain `_`).
|
||||
*/
|
||||
class NimbusMessagingMessageTest {
|
||||
private lateinit var feature: Messaging
|
||||
private lateinit var mDevice: UiDevice
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
private val storage
|
||||
get() = context.components.analytics.messagingStorage
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule =
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = TestHelper.appContext
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
feature = FxNimbus.features.messaging.value()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all messages in the FML are internally consistent with the
|
||||
* rest of the FML. This check is done in the `NimbusMessagingStorage`
|
||||
* class.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessageIntegrity() = runTest {
|
||||
val messages = storage.getMessages()
|
||||
val rawMessages = feature.messages
|
||||
assertTrue(rawMessages.isNotEmpty())
|
||||
|
||||
if (messages.size != rawMessages.size) {
|
||||
val expected = rawMessages.keys.toHashSet()
|
||||
val observed = messages.map { it.id }.toHashSet()
|
||||
val missing = expected - observed
|
||||
fail("Problem with message(s) in FML: $missing")
|
||||
}
|
||||
assertEquals(messages.size, rawMessages.size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the messages' triggers are well formed JEXL.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessageTriggers() = runTest {
|
||||
val nimbus = context.components.analytics.experiments
|
||||
val helper = nimbus.createMessageHelper(
|
||||
CustomAttributeProvider.getCustomAttributes(context),
|
||||
)
|
||||
val messages = storage.getMessages()
|
||||
messages.forEach { message ->
|
||||
storage.isMessageEligible(message, helper)
|
||||
if (storage.malFormedMap.isNotEmpty()) {
|
||||
fail("${message.id} has a problem with its JEXL trigger: ${storage.malFormedMap.keys}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIsLocalized(string: String) {
|
||||
assertFalse(string.isBlank())
|
||||
// The check will almost always succeed, since the generated code
|
||||
// will not compile if this is true, and there is no resource available.
|
||||
assertFalse(string.matches(Regex("[a-z][_a-z\\d]*")))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the messages are localized.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessagesAreLocalized() {
|
||||
feature.messages.values.forEach { message ->
|
||||
message.buttonLabel?.let(::checkIsLocalized)
|
||||
message.title?.let(::checkIsLocalized)
|
||||
checkIsLocalized(message.text)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIndividualMessagesAreValid() {
|
||||
val expectedMessages = listOf(
|
||||
"default-browser",
|
||||
"default-browser-notification",
|
||||
)
|
||||
val rawMessages = feature.messages
|
||||
for (id in expectedMessages) {
|
||||
assertTrue(rawMessages.containsKey(id))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/* 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 org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.experiments.nimbus.NimbusInterface
|
||||
import org.mozilla.experiments.nimbus.internal.NimbusException
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.gleanplumb.CustomAttributeProvider
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.nimbus.Messaging
|
||||
|
||||
/**
|
||||
* Test to instantiate Nimbus and automatically test all trigger expressions shipping with the app.
|
||||
*
|
||||
* We do this as a UI test to make sure that:
|
||||
* - as much of the custom targeting and trigger attributes are recorded as possible.
|
||||
* - we can run the Rust JEXL evaluator.
|
||||
*/
|
||||
class NimbusMessagingTriggerTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
|
||||
private lateinit var feature: Messaging
|
||||
private lateinit var nimbus: NimbusInterface
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
nimbus = TestHelper.appContext.components.analytics.experiments
|
||||
feature = FxNimbus.features.messaging.value()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAllMessageTriggersAreValid() {
|
||||
val triggers = feature.triggers
|
||||
val customAttributes = CustomAttributeProvider.getCustomAttributes(TestHelper.appContext)
|
||||
val jexl = nimbus.createMessageHelper(customAttributes)
|
||||
|
||||
val failed = mutableMapOf<String, String>()
|
||||
triggers.forEach { (key, expr) ->
|
||||
try {
|
||||
jexl.evalJexl(expr)
|
||||
} catch (e: NimbusException) {
|
||||
failed.put(key, expr)
|
||||
}
|
||||
}
|
||||
if (failed.isNotEmpty()) {
|
||||
Assert.fail("Expressions failed: $failed")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBadTriggersAreDetected() {
|
||||
val jexl = nimbus.createMessageHelper()
|
||||
|
||||
val triggers = mapOf(
|
||||
"Syntax error" to "|'syntax error'|",
|
||||
"Invalid identifier" to "invalid_identifier",
|
||||
"Invalid transform" to "'string'|invalid_transform",
|
||||
"Invalid interval" to "'string'|eventLastSeen('Invalid')",
|
||||
)
|
||||
|
||||
triggers.forEach { (key, expr) ->
|
||||
try {
|
||||
jexl.evalJexl(expr)
|
||||
Assert.fail("$key expression failed to error: $expr")
|
||||
} catch (e: NimbusException) {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
/* 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 okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getStorageTestAsset
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.getStringResource
|
||||
import org.mozilla.fenix.helpers.TestHelper.mDevice
|
||||
import org.mozilla.fenix.helpers.TestHelper.restartApp
|
||||
import org.mozilla.fenix.helpers.TestHelper.setNetworkEnabled
|
||||
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.settingsScreen
|
||||
|
||||
/**
|
||||
* Tests for verifying the Settings for:
|
||||
* Delete Browsing Data
|
||||
* Delete Browsing Data on quit
|
||||
*
|
||||
*/
|
||||
|
||||
class SettingsDeleteBrowsingDataTest {
|
||||
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBrowsingDataOptionStatesTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
}
|
||||
|
||||
restartApp(activityTestRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
switchOpenTabsCheckBox()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCookiesCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
switchSitePermissionsCheckBox()
|
||||
switchDownloadsCheckBox()
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
|
||||
restartApp(activityTestRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteTabsDataWithNoOpenTabsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteTabsDataTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
clickDialogCancelButton()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsDetails("0")
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}.openTabDrawer {
|
||||
verifyNoOpenTabsInNormalBrowsing()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteBrowsingHistoryAndSiteDataTest() {
|
||||
val storageWritePage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
val storageCheckPage = getStorageTestAsset(mockWebServer, "storage_check.html").url
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageWritePage) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage has value")
|
||||
verifyPageContent("Local storage has value")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyBrowsingHistoryDetails("2")
|
||||
selectOnlyBrowsingHistoryCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
clickDialogCancelButton()
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
verifyBrowsingHistoryDetails("0")
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyEmptyHistoryView()
|
||||
mDevice.pressBack()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage empty")
|
||||
verifyPageContent("Local storage empty")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCookiesTest() {
|
||||
val genericPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val cookiesTestPage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
|
||||
// Browsing a generic page to allow GV to load on a fresh run
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericPage.url) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(cookiesTestPage) {
|
||||
verifyPageContent("No cookies set")
|
||||
clickSetCookiesButton()
|
||||
verifyPageContent("user=android")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCookiesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyPageContent("No cookies set")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCachedFilesTest() {
|
||||
val pocketTopArticles = getStringResource(R.string.pocket_pinned_top_articles)
|
||||
|
||||
homeScreen {
|
||||
verifyExistingTopSitesTabs(pocketTopArticles)
|
||||
}.openTopSiteTabWithTitle(pocketTopArticles) {
|
||||
waitForPageToLoad()
|
||||
}.openTabDrawer {
|
||||
}.openNewTab {
|
||||
}.submitQuery("about:cache") {
|
||||
// disabling wifi to prevent downloads in the background
|
||||
setNetworkEnabled(enabled = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCachedFilesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyNetworkCacheIsEmpty("memory")
|
||||
verifyNetworkCacheIsEmpty("disk")
|
||||
}
|
||||
setNetworkEnabled(enabled = true)
|
||||
}
|
||||
}
|
@ -1,736 +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 android.os.Build
|
||||
import android.view.autofill.AutofillManager
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.filters.SdkSuppress
|
||||
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.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getStorageTestAsset
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.appContext
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.generateRandomString
|
||||
import org.mozilla.fenix.helpers.TestHelper.getStringResource
|
||||
import org.mozilla.fenix.helpers.TestHelper.openAppFromExternalLink
|
||||
import org.mozilla.fenix.helpers.TestHelper.restartApp
|
||||
import org.mozilla.fenix.helpers.TestHelper.setNetworkEnabled
|
||||
import org.mozilla.fenix.ui.robots.addToHomeScreen
|
||||
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.settingsScreen
|
||||
|
||||
/**
|
||||
* Tests for verifying the main three dot menu options
|
||||
*
|
||||
*/
|
||||
|
||||
class SettingsPrivacyTest {
|
||||
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val pageShortcutName = generateRandomString(5)
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
appContext.settings().userOptOutOfReEngageCookieBannerDialog = true
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||
val autofillManager: AutofillManager =
|
||||
appContext.getSystemService(AutofillManager::class.java)
|
||||
autofillManager.disableAutofillServices()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
// Walks through settings privacy menu and sub-menus to ensure all items are present
|
||||
@Test
|
||||
fun settingsPrivacyItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
// PRIVACY
|
||||
verifyPrivacyHeading()
|
||||
|
||||
// PRIVATE BROWSING
|
||||
verifyPrivateBrowsingButton()
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyNavigationToolBarHeader()
|
||||
}.goBack {
|
||||
// HTTPS-Only Mode
|
||||
verifyHTTPSOnlyModeButton()
|
||||
verifyHTTPSOnlyModeState("Off")
|
||||
|
||||
// ENHANCED TRACKING PROTECTION
|
||||
verifyEnhancedTrackingProtectionButton()
|
||||
verifyEnhancedTrackingProtectionState("On")
|
||||
}.openEnhancedTrackingProtectionSubMenu {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyEnhancedTrackingProtectionProtectionSubMenuItems()
|
||||
|
||||
// ENHANCED TRACKING PROTECTION EXCEPTION
|
||||
}.openExceptions {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyEnhancedTrackingProtectionProtectionExceptionsSubMenuItems()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS
|
||||
verifySitePermissionsButton()
|
||||
}.openSettingsSubMenuSitePermissions {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifySitePermissionsSubMenuItems()
|
||||
|
||||
// SITE PERMISSIONS AUTOPLAY
|
||||
}.openAutoPlay {
|
||||
verifyNavigationToolBarHeader("Autoplay")
|
||||
verifySitePermissionsAutoPlaySubMenuItems()
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS CAMERA
|
||||
}.openCamera {
|
||||
verifyNavigationToolBarHeader("Camera")
|
||||
verifySitePermissionsCommonSubMenuItems()
|
||||
verifyToggleNameToON("3. Toggle Camera to ON")
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS LOCATION
|
||||
}.openLocation {
|
||||
verifyNavigationToolBarHeader("Location")
|
||||
verifySitePermissionsCommonSubMenuItems()
|
||||
verifyToggleNameToON("3. Toggle Location to ON")
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS MICROPHONE
|
||||
}.openMicrophone {
|
||||
verifyNavigationToolBarHeader("Microphone")
|
||||
verifySitePermissionsCommonSubMenuItems()
|
||||
verifyToggleNameToON("3. Toggle Microphone to ON")
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS NOTIFICATION
|
||||
}.openNotification {
|
||||
verifyNavigationToolBarHeader("Notification")
|
||||
verifySitePermissionsNotificationSubMenuItems()
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS PERSISTENT STORAGE
|
||||
}.openPersistentStorage {
|
||||
verifyNavigationToolBarHeader("Persistent Storage")
|
||||
verifySitePermissionsPersistentStorageSubMenuItems()
|
||||
}.goBack {
|
||||
// SITE PERMISSIONS EXCEPTIONS
|
||||
}.openExceptions {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifySitePermissionsExceptionSubMenuItems()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
// DELETE BROWSING DATA
|
||||
verifyDeleteBrowsingDataButton()
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyDeleteBrowsingDataSubMenuItems()
|
||||
}.goBack {
|
||||
// DELETE BROWSING DATA ON QUIT
|
||||
verifyDeleteBrowsingDataOnQuitButton()
|
||||
verifyDeleteBrowsingDataOnQuitState("Off")
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyDeleteBrowsingDataOnQuitSubMenuItems()
|
||||
}.goBack {
|
||||
// NOTIFICATIONS
|
||||
verifyNotificationsButton()
|
||||
}.openSettingsSubMenuNotifications {
|
||||
verifySystemNotificationsView()
|
||||
}.goBack {
|
||||
// DATA COLLECTION
|
||||
verifyDataCollectionButton()
|
||||
}.openSettingsSubMenuDataCollection {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyDataCollectionSubMenuItems()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
||||
|
||||
// Tests only for initial state without signing in.
|
||||
// For tests after singing in, see SyncIntegration test suite
|
||||
|
||||
@Test
|
||||
fun loginsAndPasswordsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
// Necessary to scroll a little bit for all screen sizes
|
||||
TestHelper.scrollToElementByText("Logins and passwords")
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
verifyDefaultView()
|
||||
verifyDefaultValueAutofillLogins(InstrumentationRegistry.getInstrumentation().targetContext)
|
||||
verifyDefaultValueExceptions()
|
||||
}.openSavedLogins {
|
||||
verifySecurityPromptForLogins()
|
||||
tapSetupLater()
|
||||
// Verify that logins list is empty
|
||||
// Issue #7272 nothing is shown
|
||||
}.goBack {
|
||||
}.openSyncLogins {
|
||||
verifyReadyToScanOption()
|
||||
verifyUseEmailOption()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveLoginFromPromptTest() {
|
||||
val saveLoginTest =
|
||||
TestAssetHelper.getSaveLoginAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
|
||||
verifySaveLoginPromptIsShown()
|
||||
// Click save to save the login
|
||||
saveLoginFromPrompt("Save")
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
TestHelper.scrollToElementByText("Logins and passwords")
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
verifyDefaultView()
|
||||
}.openSavedLogins {
|
||||
verifySecurityPromptForLogins()
|
||||
tapSetupLater()
|
||||
// Verify that the login appears correctly
|
||||
verifySavedLoginFromPrompt("test@example.com")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun neverSaveLoginFromPromptTest() {
|
||||
val saveLoginTest = TestAssetHelper.getSaveLoginAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
|
||||
verifySaveLoginPromptIsShown()
|
||||
// Don't save the login, add to exceptions
|
||||
saveLoginFromPrompt("Never save")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
verifyDefaultView()
|
||||
}.openSavedLogins {
|
||||
verifySecurityPromptForLogins()
|
||||
tapSetupLater()
|
||||
// Verify that the login list is empty
|
||||
verifyNotSavedLoginFromPrompt()
|
||||
}.goBack {
|
||||
}.openLoginExceptions {
|
||||
// Verify localhost was added to exceptions list
|
||||
verifyLocalhostExceptionAdded()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveLoginsAndPasswordsOptions() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
}.saveLoginsAndPasswordsOptions {
|
||||
verifySaveLoginsOptionsView()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun openWebsiteForSavedLoginTest() {
|
||||
val loginPage = "https://mozilla-mobile.github.io/testapp/loginForm"
|
||||
val originWebsite = "mozilla-mobile.github.io"
|
||||
val userName = "test"
|
||||
val password = "pass"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(loginPage.toUri()) {
|
||||
fillAndSubmitLoginCredentials(userName, password)
|
||||
saveLoginFromPrompt("Save")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
}.openSavedLogins {
|
||||
verifySecurityPromptForLogins()
|
||||
tapSetupLater()
|
||||
viewSavedLoginDetails(userName)
|
||||
}.goToSavedWebsite {
|
||||
verifyUrl(originWebsite)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyMultipleLoginsSelectionsTest() {
|
||||
val loginPage = "https://mozilla-mobile.github.io/testapp/loginForm"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(loginPage.toUri()) {
|
||||
fillAndSubmitLoginCredentials("mozilla", "firefox")
|
||||
saveLoginFromPrompt("Save")
|
||||
fillAndSubmitLoginCredentials("firefox", "mozilla")
|
||||
saveLoginFromPrompt("Save")
|
||||
clearUserNameLoginCredential()
|
||||
clickSuggestedLoginsButton()
|
||||
verifySuggestedUserName("firefox")
|
||||
verifySuggestedUserName("mozilla")
|
||||
clickLoginSuggestion("mozilla")
|
||||
verifyPrefilledLoginCredentials("mozilla")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPrivateBrowsingMenuItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyAddPrivateBrowsingShortcutButton()
|
||||
verifyOpenLinksInPrivateTab()
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}.goBack {
|
||||
verifySettingsView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openExternalLinksInPrivateTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
openAppFromExternalLink(firstWebPage.url.toString())
|
||||
|
||||
browserScreen {
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen { }
|
||||
|
||||
setOpenLinksInPrivateOff()
|
||||
|
||||
// We need to open a different link, otherwise it will open the same session
|
||||
openAppFromExternalLink(secondWebPage.url.toString())
|
||||
|
||||
browserScreen {
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun launchPageShortcutInPrivateModeTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openAddToHomeScreen {
|
||||
addShortcutName(pageShortcutName)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
verifyShortcutAdded(pageShortcutName)
|
||||
}
|
||||
|
||||
mDevice.waitForIdle()
|
||||
// We need to close the existing tab here, to open a different session
|
||||
restartApp(activityTestRule)
|
||||
browserScreen {
|
||||
}.openTabDrawer {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(pageShortcutName) {
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun launchLinksInPrivateToggleOffStateDoesntChangeTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
expandMenu()
|
||||
}.openAddToHomeScreen {
|
||||
addShortcutName(pageShortcutName)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut(pageShortcutName) {
|
||||
}.goToHomescreen { }
|
||||
|
||||
setOpenLinksInPrivateOff()
|
||||
restartApp(activityTestRule)
|
||||
mDevice.waitForIdle()
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(pageShortcutName) {
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}.closeTabDrawer {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addPrivateBrowsingShortcut() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
cancelPrivateShortcutAddition()
|
||||
addPrivateShortcutToHomescreen()
|
||||
verifyPrivateBrowsingShortcutIcon()
|
||||
}.openPrivateBrowsingShortcut {
|
||||
verifySearchView()
|
||||
}.openBrowser {
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that you can go to System settings and change app's permissions from inside the app
|
||||
@SmokeTest
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = 29)
|
||||
fun redirectToAppPermissionsSystemSettingsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuSitePermissions {
|
||||
}.openCamera {
|
||||
verifyBlockedByAndroid()
|
||||
}.goBack {
|
||||
}.openLocation {
|
||||
verifyBlockedByAndroid()
|
||||
}.goBack {
|
||||
}.openMicrophone {
|
||||
verifyBlockedByAndroid()
|
||||
clickGoToSettingsButton()
|
||||
openAppSystemPermissionsSettings()
|
||||
switchAppPermissionSystemSetting("Camera", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Camera")
|
||||
switchAppPermissionSystemSetting("Location", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Location")
|
||||
switchAppPermissionSystemSetting("Microphone", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Microphone")
|
||||
goBackToPermissionsSettingsSubMenu()
|
||||
verifyUnblockedByAndroid()
|
||||
}.goBack {
|
||||
}.openLocation {
|
||||
verifyUnblockedByAndroid()
|
||||
}.goBack {
|
||||
}.openCamera {
|
||||
verifyUnblockedByAndroid()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBrowsingDataOptionStatesTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
}
|
||||
|
||||
restartApp(activityTestRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
switchOpenTabsCheckBox()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCookiesCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
switchSitePermissionsCheckBox()
|
||||
switchDownloadsCheckBox()
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
|
||||
restartApp(activityTestRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteTabsDataWithNoOpenTabsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteTabsDataTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
clickDialogCancelButton()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsDetails("0")
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}.openTabDrawer {
|
||||
verifyNoOpenTabsInNormalBrowsing()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteBrowsingHistoryAndSiteDataTest() {
|
||||
val storageWritePage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
val storageCheckPage = getStorageTestAsset(mockWebServer, "storage_check.html").url
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageWritePage) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage has value")
|
||||
verifyPageContent("Local storage has value")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyBrowsingHistoryDetails("2")
|
||||
selectOnlyBrowsingHistoryCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
clickDialogCancelButton()
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
verifyBrowsingHistoryDetails("0")
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyEmptyHistoryView()
|
||||
mDevice.pressBack()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage empty")
|
||||
verifyPageContent("Local storage empty")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCookiesTest() {
|
||||
val genericPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val cookiesTestPage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
|
||||
// Browsing a generic page to allow GV to load on a fresh run
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericPage.url) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(cookiesTestPage) {
|
||||
verifyPageContent("No cookies set")
|
||||
clickSetCookiesButton()
|
||||
verifyPageContent("user=android")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCookiesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyPageContent("No cookies set")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCachedFilesTest() {
|
||||
val pocketTopArticles = getStringResource(R.string.pocket_pinned_top_articles)
|
||||
|
||||
homeScreen {
|
||||
verifyExistingTopSitesTabs(pocketTopArticles)
|
||||
}.openTopSiteTabWithTitle(pocketTopArticles) {
|
||||
waitForPageToLoad()
|
||||
}.openTabDrawer {
|
||||
}.openNewTab {
|
||||
}.submitQuery("about:cache") {
|
||||
// disabling wifi to prevent downloads in the background
|
||||
setNetworkEnabled(enabled = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCachedFilesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyNetworkCacheIsEmpty("memory")
|
||||
verifyNetworkCacheIsEmpty("disk")
|
||||
}
|
||||
setNetworkEnabled(enabled = true)
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun saveLoginsInPWATest() {
|
||||
val pwaPage = "https://mozilla-mobile.github.io/testapp/loginForm"
|
||||
val shortcutTitle = "TEST_APP"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pwaPage.toUri()) {
|
||||
verifyNotificationDotOnMainMenu()
|
||||
}.openThreeDotMenu {
|
||||
}.clickInstall {
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut(shortcutTitle) {
|
||||
mDevice.waitForIdle()
|
||||
fillAndSubmitLoginCredentials("mozilla", "firefox")
|
||||
verifySaveLoginPromptIsDisplayed()
|
||||
saveLoginFromPrompt("Save")
|
||||
openAppFromExternalLink(pwaPage)
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
}.openSavedLogins {
|
||||
verifySecurityPromptForLogins()
|
||||
tapSetupLater()
|
||||
verifySavedLoginFromPrompt("mozilla")
|
||||
}
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(shortcutTitle) {
|
||||
verifyPrefilledPWALoginCredentials("mozilla", shortcutTitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOpenLinksInPrivateOn() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyOpenLinksInPrivateTabEnabled()
|
||||
clickOpenLinksInPrivateTabSwitch()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOpenLinksInPrivateOff() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
clickOpenLinksInPrivateTabSwitch()
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
/* 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 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.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.mDevice
|
||||
import org.mozilla.fenix.ui.robots.addToHomeScreen
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
class SettingsPrivateBrowsingTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val pageShortcutName = TestHelper.generateRandomString(5)
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPrivateBrowsingMenuItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyAddPrivateBrowsingShortcutButton()
|
||||
verifyOpenLinksInPrivateTab()
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}.goBack {
|
||||
verifySettingsView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openExternalLinksInPrivateTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
TestHelper.openAppFromExternalLink(firstWebPage.url.toString())
|
||||
|
||||
browserScreen {
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen { }
|
||||
|
||||
setOpenLinksInPrivateOff()
|
||||
|
||||
// We need to open a different link, otherwise it will open the same session
|
||||
TestHelper.openAppFromExternalLink(secondWebPage.url.toString())
|
||||
|
||||
browserScreen {
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun launchPageShortcutInPrivateModeTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openAddToHomeScreen {
|
||||
addShortcutName(pageShortcutName)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
verifyShortcutAdded(pageShortcutName)
|
||||
}
|
||||
|
||||
mDevice.waitForIdle()
|
||||
// We need to close the existing tab here, to open a different session
|
||||
TestHelper.restartApp(activityTestRule)
|
||||
browserScreen {
|
||||
}.openTabDrawer {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(pageShortcutName) {
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun launchLinksInPrivateToggleOffStateDoesntChangeTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
setOpenLinksInPrivateOn()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
expandMenu()
|
||||
}.openAddToHomeScreen {
|
||||
addShortcutName(pageShortcutName)
|
||||
clickAddShortcutButton()
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut(pageShortcutName) {
|
||||
}.goToHomescreen { }
|
||||
|
||||
setOpenLinksInPrivateOff()
|
||||
TestHelper.restartApp(activityTestRule)
|
||||
mDevice.waitForIdle()
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(pageShortcutName) {
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}.closeTabDrawer {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addPrivateBrowsingShortcut() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
cancelPrivateShortcutAddition()
|
||||
addPrivateShortcutToHomescreen()
|
||||
verifyPrivateBrowsingShortcutIcon()
|
||||
}.openPrivateBrowsingShortcut {
|
||||
verifySearchView()
|
||||
}.openBrowser {
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOpenLinksInPrivateOn() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
verifyOpenLinksInPrivateTabEnabled()
|
||||
clickOpenLinksInPrivateTabSwitch()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOpenLinksInPrivateOff() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openPrivateBrowsingSubMenu {
|
||||
clickOpenLinksInPrivateTabSwitch()
|
||||
verifyOpenLinksInPrivateTabOff()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
|
||||
class SettingsSitePermissionsTest {
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityTestRule.withDefaultSettingsOverrides()
|
||||
|
||||
// Verifies that you can go to System settings and change app's permissions from inside the app
|
||||
@SmokeTest
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = 29)
|
||||
fun redirectToAppPermissionsSystemSettingsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuSitePermissions {
|
||||
}.openCamera {
|
||||
verifyBlockedByAndroid()
|
||||
}.goBack {
|
||||
}.openLocation {
|
||||
verifyBlockedByAndroid()
|
||||
}.goBack {
|
||||
}.openMicrophone {
|
||||
verifyBlockedByAndroid()
|
||||
clickGoToSettingsButton()
|
||||
openAppSystemPermissionsSettings()
|
||||
switchAppPermissionSystemSetting("Camera", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Camera")
|
||||
switchAppPermissionSystemSetting("Location", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Location")
|
||||
switchAppPermissionSystemSetting("Microphone", "Allow")
|
||||
goBackToSystemAppPermissionSettings()
|
||||
verifySystemGrantedPermission("Microphone")
|
||||
goBackToPermissionsSettingsSubMenu()
|
||||
verifyUnblockedByAndroid()
|
||||
}.goBack {
|
||||
}.openLocation {
|
||||
verifyUnblockedByAndroid()
|
||||
}.goBack {
|
||||
}.openCamera {
|
||||
verifyUnblockedByAndroid()
|
||||
}
|
||||
}
|
||||
}
|
@ -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 org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.assertCheckedItemWithResIdExists
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.assertItemContainingTextExists
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.checkedItemWithResId
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemWithResId
|
||||
import org.mozilla.fenix.helpers.TestHelper.getStringResource
|
||||
import org.mozilla.fenix.helpers.TestHelper.packageName
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for the settings Cookie Banner Reduction sub menu.
|
||||
*/
|
||||
class SettingsSubMenuCookieBannerReductionRobot {
|
||||
fun verifyCookieBannerView(isCookieBannerReductionChecked: Boolean) {
|
||||
assertItemContainingTextExists(cookieBannerOptionTitle, cookieBannerOptionDescription)
|
||||
assertCheckedItemWithResIdExists(checkedCookieBannerOptionToggle(isCookieBannerReductionChecked))
|
||||
}
|
||||
fun clickCookieBannerReductionToggle() = cookieBannerOptionToggle.click()
|
||||
fun verifyCheckedCookieBannerReductionToggle(isCookieBannerReductionChecked: Boolean) =
|
||||
assertCheckedItemWithResIdExists(checkedCookieBannerOptionToggle(isCookieBannerReductionChecked))
|
||||
|
||||
class Transition
|
||||
}
|
||||
|
||||
private val cookieBannerOptionTitle =
|
||||
itemContainingText(getStringResource(R.string.reduce_cookie_banner_option))
|
||||
private val cookieBannerOptionDescription =
|
||||
itemContainingText(getStringResource(R.string.reduce_cookie_banner_summary_1))
|
||||
private val cookieBannerOptionToggle =
|
||||
itemWithResId("$packageName:id/learn_more_switch")
|
||||
private fun checkedCookieBannerOptionToggle(isChecked: Boolean = false) =
|
||||
checkedItemWithResId("$packageName:id/learn_more_switch", isChecked)
|
@ -0,0 +1,108 @@
|
||||
/* 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.ViewInteraction
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.TestHelper.getStringResource
|
||||
import org.mozilla.fenix.helpers.assertIsChecked
|
||||
import org.mozilla.fenix.helpers.assertIsEnabled
|
||||
import org.mozilla.fenix.helpers.click
|
||||
import org.mozilla.fenix.helpers.isChecked
|
||||
|
||||
class SettingsSubMenuHttpsOnlyModeRobot {
|
||||
|
||||
fun verifyHttpsOnlyModeMenuHeader(): ViewInteraction =
|
||||
onView(
|
||||
allOf(
|
||||
withText(getStringResource(R.string.preferences_https_only_title)),
|
||||
hasSibling(withContentDescription("Navigate up")),
|
||||
),
|
||||
).check(matches(isDisplayed()))
|
||||
|
||||
fun verifyHttpsOnlyModeSummary() {
|
||||
onView(withId(R.id.https_only_title))
|
||||
.check(matches(withText("HTTPS-Only Mode")))
|
||||
onView(withId(R.id.https_only_summary))
|
||||
.check(matches(withText("Automatically attempts to connect to sites using HTTPS encryption protocol for increased security. Learn more")))
|
||||
}
|
||||
|
||||
fun verifyHttpsOnlyModeIsEnabled(shouldBeEnabled: Boolean) {
|
||||
httpsModeOnlySwitch.check(
|
||||
matches(
|
||||
if (shouldBeEnabled) {
|
||||
isChecked(true)
|
||||
} else {
|
||||
isChecked(false)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun clickHttpsOnlyModeSwitch() = httpsModeOnlySwitch.click()
|
||||
|
||||
fun verifyHttpsOnlyModeOptionsEnabled(shouldBeEnabled: Boolean) {
|
||||
allTabsOption.assertIsEnabled(shouldBeEnabled)
|
||||
onlyPrivateTabsOption.assertIsEnabled(shouldBeEnabled)
|
||||
}
|
||||
|
||||
fun verifyHttpsOnlyOptionSelected(allTabsOptionSelected: Boolean, privateTabsOptionSelected: Boolean) {
|
||||
if (allTabsOptionSelected) {
|
||||
allTabsOption.assertIsChecked(true)
|
||||
onlyPrivateTabsOption.assertIsChecked(false)
|
||||
} else if (privateTabsOptionSelected) {
|
||||
allTabsOption.assertIsChecked(false)
|
||||
onlyPrivateTabsOption.assertIsChecked(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun selectHttpsOnlyModeOption(allTabsOptionSelected: Boolean, privateTabsOptionSelected: Boolean) {
|
||||
if (allTabsOptionSelected) {
|
||||
allTabsOption.click()
|
||||
allTabsOption.assertIsChecked(true)
|
||||
} else if (privateTabsOptionSelected) {
|
||||
onlyPrivateTabsOption.click()
|
||||
onlyPrivateTabsOption.assertIsChecked(true)
|
||||
}
|
||||
}
|
||||
|
||||
class Transition {
|
||||
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
|
||||
goBackButton.perform(click())
|
||||
|
||||
SettingsRobot().interact()
|
||||
return SettingsRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val httpsModeOnlySwitch = onView(withId(R.id.https_only_switch))
|
||||
|
||||
private val allTabsOption =
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.https_only_all_tabs),
|
||||
withText("Enable in all tabs"),
|
||||
),
|
||||
)
|
||||
|
||||
private val onlyPrivateTabsOption =
|
||||
onView(
|
||||
allOf(
|
||||
withId(R.id.https_only_private_tabs),
|
||||
withText("Enable only in private tabs"),
|
||||
),
|
||||
)
|
||||
|
||||
private val goBackButton = onView(withContentDescription("Navigate up"))
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue