Merge remote-tracking branch 'origin/fenix/112.0' into iceraven
commit
e95c430d11
@ -1,43 +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: 9, minute: 0}
|
||||
- {hour: 12, minute: 0}
|
||||
- {hour: 18, minute: 0}
|
||||
- {hour: 22, 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,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: We are now using Bugzilla for issue tracking!
|
||||
url: https://bugzilla.mozilla.org/enter_bug.cgi?product=Fenix
|
||||
about: Please file your ticket there, and select the corresponding component from the list. If the component is not in the list, select "General". In commit messages, please reference the ticket using "Bug 1234567 - " as prefix.
|
@ -1,23 +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.
|
||||
|
||||
### QA
|
||||
<!-- Before submitting the PR, please address each item -->
|
||||
- [x] **QA Needed**
|
||||
|
||||
### To download an APK when reviewing a PR (after all CI tasks finished running):
|
||||
1. Click on `Checks` at the top of the PR page.
|
||||
2. Click on the `firefoxci-taskcluster` group on the left to expand all tasks.
|
||||
3. Click on the `build-debug` task.
|
||||
4. Click on `View task in Taskcluster` in the new `DETAILS` section.
|
||||
5. The APK links should be on the right side of the screen, named for each CPU architecture.
|
||||
|
||||
### GitHub Automation
|
||||
<!-- Do not add anything below this line -->
|
||||
|
||||
Used by GitHub Actions.
|
@ -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,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,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,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,728 +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.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())
|
||||
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("Standard")
|
||||
}.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 saveLoginsAndPasswordsOptions() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
}.openSaveLoginsAndPasswordsOptions {
|
||||
verifySaveLoginsOptionsView()
|
||||
}
|
||||
}
|
||||
|
||||
@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()
|
||||
verifySavedLoginsSectionUsername("mozilla")
|
||||
}
|
||||
|
||||
addToHomeScreen {
|
||||
}.searchAndOpenHomeScreenShortcut(shortcutTitle) {
|
||||
verifyPrefilledPWALoginCredentials("mozilla", shortcutTitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,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"))
|
@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui.robots
|
||||
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.mozilla.fenix.helpers.TestHelper.mDevice
|
||||
import org.mozilla.fenix.helpers.click
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for the Open Links In Apps sub menu.
|
||||
*/
|
||||
class SettingsSubMenuOpenLinksInAppsRobot {
|
||||
|
||||
class Transition {
|
||||
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
|
||||
mDevice.waitForIdle()
|
||||
goBackButton().perform(ViewActions.click())
|
||||
|
||||
SettingsRobot().interact()
|
||||
return SettingsRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clickAlwaysButton() = alwaysRadioButton().click()
|
||||
|
||||
private fun alwaysRadioButton() = onView(withText("Always"))
|
||||
|
||||
private fun goBackButton() =
|
||||
onView(allOf(ViewMatchers.withContentDescription("Navigate up")))
|
@ -0,0 +1,29 @@
|
||||
/* 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
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.mozilla.fenix.home.mozonline.showPrivacyPopWindow
|
||||
|
||||
/**
|
||||
* This activity is specific to the Mozilla Online build and used to display
|
||||
* a privacy notice on first run. Once the privacy notice is accepted, and for
|
||||
* all subsequent launches, it will simply launch the Fenix [HomeActivity].
|
||||
*/
|
||||
open class MozillaOnlineHomeActivity : AppCompatActivity() {
|
||||
|
||||
final override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if ((this.application as FenixApplication).shouldShowPrivacyNotice()) {
|
||||
showPrivacyPopWindow(this.applicationContext, this)
|
||||
} else {
|
||||
startActivity(Intent(this, HomeActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +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.home.intent
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.concept.engine.EngineSession
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.ext.openSetDefaultBrowserOption
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.DefaultBrowserNotificationWorker.Companion.isDefaultBrowserNotificationIntent
|
||||
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
|
||||
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker.Companion.isReEngagementNotificationIntent
|
||||
|
||||
/**
|
||||
* When the default browser notification is tapped we need to launch [openSetDefaultBrowserOption]
|
||||
*
|
||||
* This should only happens once in a user's lifetime since once the user taps on the default browser
|
||||
* notification, [settings.shouldShowDefaultBrowserNotification] will return false
|
||||
*/
|
||||
class DefaultBrowserIntentProcessor(
|
||||
private val activity: HomeActivity,
|
||||
) : HomeIntentProcessor {
|
||||
|
||||
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
|
||||
return when {
|
||||
isDefaultBrowserNotificationIntent(intent) -> {
|
||||
Events.defaultBrowserNotifTapped.record(NoExtras())
|
||||
|
||||
activity.openSetDefaultBrowserOption()
|
||||
true
|
||||
}
|
||||
isReEngagementNotificationIntent(intent) -> {
|
||||
Events.reEngagementNotifTapped.record(NoExtras())
|
||||
|
||||
activity.browsingModeManager.mode = BrowsingMode.Private
|
||||
activity.openToBrowserAndLoad(
|
||||
ReEngagementNotificationWorker.NOTIFICATION_TARGET_URL,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromGlobal,
|
||||
flags = EngineSession.LoadUrlFlags.external(),
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* 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.home.intent
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.navOptions
|
||||
import mozilla.components.concept.engine.EngineSession
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
|
||||
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker.Companion.isReEngagementNotificationIntent
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* Handle when the re-engagement notification is tapped
|
||||
*
|
||||
* This should only happens once in a user's lifetime notification,
|
||||
* [settings.shouldShowReEngagementNotification] will return false if the user already seen the
|
||||
* notification.
|
||||
*/
|
||||
class ReEngagementIntentProcessor(
|
||||
private val activity: HomeActivity,
|
||||
private val settings: Settings,
|
||||
) : HomeIntentProcessor {
|
||||
|
||||
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
|
||||
return when {
|
||||
isReEngagementNotificationIntent(intent) -> {
|
||||
Events.reEngagementNotifTapped.record(NoExtras())
|
||||
|
||||
when (settings.reEngagementNotificationType) {
|
||||
ReEngagementNotificationWorker.NOTIFICATION_TYPE_B -> {
|
||||
val directions = NavGraphDirections.actionGlobalSearchDialog(sessionId = null)
|
||||
val options = navOptions {
|
||||
popUpTo(R.id.homeFragment)
|
||||
}
|
||||
navController.nav(null, directions, options)
|
||||
}
|
||||
else -> {
|
||||
activity.browsingModeManager.mode = BrowsingMode.Private
|
||||
activity.openToBrowserAndLoad(
|
||||
ReEngagementNotificationWorker.NOTIFICATION_TARGET_URL,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromGlobal,
|
||||
flags = EngineSession.LoadUrlFlags.external(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +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.onboarding
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import mozilla.components.support.base.ids.SharedIdsHelper
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.utils.IntentUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.mozilla.fenix.utils.createBaseNotification
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class DefaultBrowserNotificationWorker(
|
||||
context: Context,
|
||||
workerParameters: WorkerParameters,
|
||||
) : Worker(context, workerParameters) {
|
||||
|
||||
override fun doWork(): Result {
|
||||
val channelId = ensureMarketingChannelExists(applicationContext)
|
||||
|
||||
NotificationManagerCompat.from(applicationContext)
|
||||
.notify(
|
||||
NOTIFICATION_TAG,
|
||||
DEFAULT_BROWSER_NOTIFICATION_ID,
|
||||
buildNotification(channelId),
|
||||
)
|
||||
|
||||
Events.defaultBrowserNotifShown.record(NoExtras())
|
||||
|
||||
// default browser notification should only happen once
|
||||
applicationContext.settings().defaultBrowserNotificationDisplayed = true
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the default browser notification.
|
||||
*/
|
||||
private fun buildNotification(channelId: String): Notification {
|
||||
val intent = Intent(applicationContext, HomeActivity::class.java)
|
||||
intent.putExtra(INTENT_DEFAULT_BROWSER_NOTIFICATION, true)
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
SharedIdsHelper.getNextIdForTag(applicationContext, NOTIFICATION_PENDING_INTENT_TAG),
|
||||
intent,
|
||||
IntentUtils.defaultIntentPendingFlags,
|
||||
)
|
||||
|
||||
with(applicationContext) {
|
||||
val appName = getString(R.string.app_name)
|
||||
return createBaseNotification(
|
||||
this,
|
||||
channelId,
|
||||
getString(R.string.notification_default_browser_title, appName),
|
||||
getString(R.string.notification_default_browser_text, appName),
|
||||
pendingIntent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NOTIFICATION_PENDING_INTENT_TAG = "org.mozilla.fenix.default.browser"
|
||||
private const val INTENT_DEFAULT_BROWSER_NOTIFICATION = "org.mozilla.fenix.default.browser.intent"
|
||||
private const val NOTIFICATION_TAG = "org.mozilla.fenix.default.browser.tag"
|
||||
private const val NOTIFICATION_WORK_NAME = "org.mozilla.fenix.default.browser.work"
|
||||
private const val NOTIFICATION_DELAY = Settings.THREE_DAYS_MS
|
||||
|
||||
fun isDefaultBrowserNotificationIntent(intent: Intent) =
|
||||
intent.extras?.containsKey(INTENT_DEFAULT_BROWSER_NOTIFICATION) ?: false
|
||||
|
||||
fun setDefaultBrowserNotificationIfNeeded(context: Context) {
|
||||
val instanceWorkManager = WorkManager.getInstance(context)
|
||||
|
||||
if (!context.settings().shouldShowDefaultBrowserNotification()) {
|
||||
// cancel notification work if already default browser
|
||||
instanceWorkManager.cancelUniqueWork(NOTIFICATION_WORK_NAME)
|
||||
return
|
||||
}
|
||||
|
||||
val notificationWork = OneTimeWorkRequest.Builder(DefaultBrowserNotificationWorker::class.java)
|
||||
.setInitialDelay(NOTIFICATION_DELAY, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
instanceWorkManager.beginUniqueWork(
|
||||
NOTIFICATION_WORK_NAME,
|
||||
ExistingWorkPolicy.KEEP,
|
||||
notificationWork,
|
||||
).enqueue()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,297 +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.onboarding.view
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.insets.navigationBarsPadding
|
||||
import com.google.accompanist.insets.statusBarsPadding
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.button.PrimaryButton
|
||||
import org.mozilla.fenix.compose.button.SecondaryButton
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding as OnboardingMetrics
|
||||
|
||||
/**
|
||||
* Enum that represents the onboarding screen that is displayed.
|
||||
*/
|
||||
private enum class OnboardingState {
|
||||
Welcome,
|
||||
SyncSignIn,
|
||||
}
|
||||
|
||||
/**
|
||||
* A screen for displaying a welcome and sync sign in onboarding.
|
||||
*
|
||||
* @param isSyncSignIn Whether or not the user is signed into their Firefox Sync account.
|
||||
* @param onDismiss Invoked when the user clicks on the close or "Skip" button.
|
||||
* @param onSignInButtonClick Invoked when the user clicks on the "Sign In" button
|
||||
*/
|
||||
@Composable
|
||||
fun Onboarding(
|
||||
isSyncSignIn: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onSignInButtonClick: () -> Unit,
|
||||
) {
|
||||
var onboardingState by remember { mutableStateOf(OnboardingState.Welcome) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(FirefoxTheme.colors.layer1)
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 32.dp)
|
||||
.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (onboardingState == OnboardingState.Welcome) {
|
||||
OnboardingMetrics.welcomeCloseClicked.record(NoExtras())
|
||||
} else {
|
||||
OnboardingMetrics.syncCloseClicked.record(NoExtras())
|
||||
}
|
||||
onDismiss()
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.mozac_ic_close),
|
||||
contentDescription = stringResource(R.string.onboarding_home_content_description_close_button),
|
||||
tint = FirefoxTheme.colors.iconPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (onboardingState == OnboardingState.Welcome) {
|
||||
OnboardingWelcomeContent()
|
||||
|
||||
OnboardingWelcomeBottomContent(
|
||||
onboardingState = onboardingState,
|
||||
isSyncSignIn = isSyncSignIn,
|
||||
onGetStartedButtonClick = {
|
||||
OnboardingMetrics.welcomeGetStartedClicked.record(NoExtras())
|
||||
if (isSyncSignIn) {
|
||||
onDismiss()
|
||||
} else {
|
||||
onboardingState = OnboardingState.SyncSignIn
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
OnboardingMetrics.welcomeCardImpression.record(NoExtras())
|
||||
} else if (onboardingState == OnboardingState.SyncSignIn) {
|
||||
OnboardingSyncSignInContent()
|
||||
|
||||
OnboardingSyncSignInBottomContent(
|
||||
onboardingState = onboardingState,
|
||||
onSignInButtonClick = {
|
||||
OnboardingMetrics.syncSignInClicked.record(NoExtras())
|
||||
onSignInButtonClick()
|
||||
},
|
||||
onSkipButtonClick = {
|
||||
OnboardingMetrics.syncSkipClicked.record(NoExtras())
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
|
||||
OnboardingMetrics.syncCardImpression.record(NoExtras())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnboardingWelcomeBottomContent(
|
||||
onboardingState: OnboardingState,
|
||||
isSyncSignIn: Boolean,
|
||||
onGetStartedButtonClick: () -> Unit,
|
||||
) {
|
||||
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
PrimaryButton(
|
||||
text = stringResource(id = R.string.onboarding_home_get_started_button),
|
||||
onClick = onGetStartedButtonClick,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
if (isSyncSignIn) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
} else {
|
||||
Indicators(onboardingState = onboardingState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnboardingWelcomeContent() {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_onboarding_welcome),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_home_welcome_title_2),
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = FirefoxTheme.typography.headline5,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_home_welcome_description),
|
||||
color = FirefoxTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = FirefoxTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnboardingSyncSignInContent() {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_onboarding_sync),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_home_sync_title_3),
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = FirefoxTheme.typography.headline5,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_home_sync_description),
|
||||
color = FirefoxTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = FirefoxTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnboardingSyncSignInBottomContent(
|
||||
onboardingState: OnboardingState,
|
||||
onSignInButtonClick: () -> Unit,
|
||||
onSkipButtonClick: () -> Unit,
|
||||
) {
|
||||
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
PrimaryButton(
|
||||
text = stringResource(id = R.string.onboarding_home_sign_in_button),
|
||||
onClick = onSignInButtonClick,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
SecondaryButton(
|
||||
text = stringResource(id = R.string.onboarding_home_skip_button),
|
||||
onClick = onSkipButtonClick,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Indicators(onboardingState = onboardingState)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Indicators(onboardingState: OnboardingState) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Indicator(
|
||||
color = if (onboardingState == OnboardingState.Welcome) {
|
||||
FirefoxTheme.colors.indicatorActive
|
||||
} else {
|
||||
FirefoxTheme.colors.indicatorInactive
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Indicator(
|
||||
color = if (onboardingState == OnboardingState.SyncSignIn) {
|
||||
FirefoxTheme.colors.indicatorActive
|
||||
} else {
|
||||
FirefoxTheme.colors.indicatorInactive
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Indicator(color: Color) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.clip(CircleShape)
|
||||
.background(color),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun OnboardingPreview() {
|
||||
FirefoxTheme {
|
||||
Onboarding(
|
||||
isSyncSignIn = false,
|
||||
onDismiss = {},
|
||||
onSignInButtonClick = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/* 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.onboarding.view
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding as OnboardingMetrics
|
||||
|
||||
/**
|
||||
* Enum that represents the onboarding screen that is displayed.
|
||||
*/
|
||||
private enum class UpgradeOnboardingState {
|
||||
Welcome,
|
||||
SyncSignIn,
|
||||
}
|
||||
|
||||
/**
|
||||
* A screen for displaying a welcome and sync sign in onboarding.
|
||||
*
|
||||
* @param isSyncSignIn Whether or not the user is signed into their Firefox Sync account.
|
||||
* @param onDismiss Invoked when the user clicks on the close or "Skip" button.
|
||||
* @param onSignInButtonClick Invoked when the user clicks on the "Sign In" button
|
||||
*/
|
||||
@Composable
|
||||
fun UpgradeOnboarding(
|
||||
isSyncSignIn: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onSignInButtonClick: () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides layoutDirection()) {
|
||||
UpgradeOnboardingContent(
|
||||
isSyncSignIn = isSyncSignIn,
|
||||
onDismiss = onDismiss,
|
||||
onSignInButtonClick = onSignInButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpgradeOnboardingContent(
|
||||
isSyncSignIn: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onSignInButtonClick: () -> Unit,
|
||||
) {
|
||||
var onboardingState by remember { mutableStateOf(UpgradeOnboardingState.Welcome) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(FirefoxTheme.colors.layer1)
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 32.dp)
|
||||
.statusBarsPadding()
|
||||
.navigationBarsPadding(),
|
||||
) {
|
||||
OnboardingPage(
|
||||
pageState = when (onboardingState) {
|
||||
UpgradeOnboardingState.Welcome -> OnboardingPageState(
|
||||
image = R.drawable.ic_onboarding_welcome,
|
||||
title = stringResource(id = R.string.onboarding_home_welcome_title_2),
|
||||
description = stringResource(id = R.string.onboarding_home_welcome_description),
|
||||
primaryButtonText = stringResource(id = R.string.onboarding_home_get_started_button),
|
||||
onRecordImpressionEvent = {
|
||||
OnboardingMetrics.welcomeCardImpression.record(NoExtras())
|
||||
},
|
||||
)
|
||||
UpgradeOnboardingState.SyncSignIn -> OnboardingPageState(
|
||||
image = R.drawable.ic_onboarding_sync,
|
||||
title = stringResource(id = R.string.onboarding_home_sync_title_3),
|
||||
description = stringResource(id = R.string.onboarding_home_sync_description),
|
||||
primaryButtonText = stringResource(id = R.string.onboarding_home_sign_in_button),
|
||||
secondaryButtonText = stringResource(id = R.string.onboarding_home_skip_button),
|
||||
onRecordImpressionEvent = {
|
||||
OnboardingMetrics.syncCardImpression.record(NoExtras())
|
||||
},
|
||||
)
|
||||
},
|
||||
onDismiss = {
|
||||
when (onboardingState) {
|
||||
UpgradeOnboardingState.Welcome -> OnboardingMetrics.welcomeCloseClicked.record(NoExtras())
|
||||
UpgradeOnboardingState.SyncSignIn -> OnboardingMetrics.syncCloseClicked.record(NoExtras())
|
||||
}
|
||||
onDismiss()
|
||||
},
|
||||
onPrimaryButtonClick = {
|
||||
when (onboardingState) {
|
||||
UpgradeOnboardingState.Welcome -> {
|
||||
OnboardingMetrics.welcomeGetStartedClicked.record(NoExtras())
|
||||
if (isSyncSignIn) {
|
||||
onDismiss()
|
||||
} else {
|
||||
onboardingState = UpgradeOnboardingState.SyncSignIn
|
||||
}
|
||||
}
|
||||
UpgradeOnboardingState.SyncSignIn -> {
|
||||
OnboardingMetrics.syncSignInClicked.record(NoExtras())
|
||||
onSignInButtonClick()
|
||||
}
|
||||
}
|
||||
},
|
||||
onSecondaryButtonClick = {
|
||||
when (onboardingState) {
|
||||
UpgradeOnboardingState.Welcome -> {
|
||||
// Welcome does not have a secondary button.
|
||||
}
|
||||
UpgradeOnboardingState.SyncSignIn -> {
|
||||
OnboardingMetrics.syncSkipClicked.record(NoExtras())
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
if (isSyncSignIn) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
} else {
|
||||
Indicators(onboardingState = onboardingState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Indicators(
|
||||
onboardingState: UpgradeOnboardingState,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Indicator(
|
||||
color = if (onboardingState == UpgradeOnboardingState.Welcome) {
|
||||
FirefoxTheme.colors.indicatorActive
|
||||
} else {
|
||||
FirefoxTheme.colors.indicatorInactive
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Indicator(
|
||||
color = if (onboardingState == UpgradeOnboardingState.SyncSignIn) {
|
||||
FirefoxTheme.colors.indicatorActive
|
||||
} else {
|
||||
FirefoxTheme.colors.indicatorInactive
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Indicator(color: Color) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(6.dp)
|
||||
.clip(CircleShape)
|
||||
.background(color),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@LightDarkPreview
|
||||
private fun OnboardingPreview() {
|
||||
FirefoxTheme {
|
||||
UpgradeOnboarding(
|
||||
isSyncSignIn = false,
|
||||
onDismiss = {},
|
||||
onSignInButtonClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force Left to Right layout direction when running on Android API level < 23 (Android 5.1).
|
||||
* Bug with compose and RTL views causing crash in the Onboarding screen in Android 5.1.
|
||||
* Bugzilla link: https://bugzilla.mozilla.org/show_bug.cgi?id=1792796
|
||||
*/
|
||||
@Composable
|
||||
private fun layoutDirection() = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
LocalLayoutDirection.current
|
||||
} else {
|
||||
LayoutDirection.Ltr
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue