tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit a673edaafd6c35ce6946c5df0a1baeb25d186581
parent 091adff761737eff6a8f1d099bb3921022dbe822
Author: Roger Yang <royang@mozilla.com>
Date:   Thu, 16 Oct 2025 20:08:59 +0000

Bug 1993601 - Add more UI automated tests for AppLinks. r=android-reviewers,tthibaud

Differential Revision: https://phabricator.services.mozilla.com/D268229

Diffstat:
Amobile/android/fenix/app/src/androidTest/assets/pages/appLinksLinks.html | 31+++++++++++++++++++++++++++++++
Amobile/android/fenix/app/src/androidTest/assets/pages/appLinksPostNavigationPhone.html | 10++++++++++
Mmobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt | 6++++++
Amobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/AppLinksTest.kt | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt | 16++++++++++++++++
5 files changed, 302 insertions(+), 0 deletions(-)

diff --git a/mobile/android/fenix/app/src/androidTest/assets/pages/appLinksLinks.html b/mobile/android/fenix/app/src/androidTest/assets/pages/appLinksLinks.html @@ -0,0 +1,31 @@ +<!doctype html> +<html> + <head> + <title>Html_Control_Form</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + </head> + + <p>Misc Link Types</p> + <section> + <a href="tel://1234567890">Telephone link</a> + </section> + + <section> + <a href="intent://com.example.app">Intent schema link</a> + </section> + + <section> + <a href="vnd.youtube://@Mozilla">Youtube schema link</a> + </section> + + <section> + <a href="https://m.youtube.com/user/mozilla">Youtube link</a> + </section> + + <section> + <a href="appLinksPostNavigationPhone.html" target="_blank"> + Telephone post navigation link + </a> + </section> +</html> diff --git a/mobile/android/fenix/app/src/androidTest/assets/pages/appLinksPostNavigationPhone.html b/mobile/android/fenix/app/src/androidTest/assets/pages/appLinksPostNavigationPhone.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> +This is the page with the auto-submitting form that redirects to an phone +intent. +<form action="tel://1234567890" method="POST"></form> +<script> + const f = document.forms[0]; + f.submit(); +</script> diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/helpers/TestAssetHelper.kt @@ -101,6 +101,12 @@ object TestAssetHelper { return TestAsset(url, "", "") } + fun getAppLinksRedirectAsset(server: MockWebServer): TestAsset { + val url = server.url("pages/appLinksLinks.html").toString().toUri()!! + + return TestAsset(url, "", "") + } + fun getCreditCardFormAsset(server: MockWebServer): TestAsset { val url = server.url("pages/creditCardForm.html").toString().toUri()!! diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/AppLinksTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/AppLinksTest.kt @@ -0,0 +1,239 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.ui + +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.helpers.AppAndSystemHelper.assertNativeAppOpens +import org.mozilla.fenix.helpers.Constants +import org.mozilla.fenix.helpers.HomeActivityIntentTestRule +import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText +import org.mozilla.fenix.helpers.MatcherHelper.itemWithResIdAndText +import org.mozilla.fenix.helpers.OpenLinksInApp +import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestHelper.mDevice +import org.mozilla.fenix.helpers.TestSetup +import org.mozilla.fenix.helpers.perf.DetectMemoryLeaksRule +import org.mozilla.fenix.ui.robots.clickPageObject +import org.mozilla.fenix.ui.robots.navigationToolbar + +class AppLinksTest : TestSetup() { + private val youtubeSchemaUrlLink = itemContainingText("Youtube schema link") + private val youtubeUrlLink = itemContainingText("Youtube link") + private val intentSchemaUrlLink = itemContainingText("Intent schema link") + private val phoneUrlLink = itemContainingText("Telephone link") + private val formRedirectLink = itemContainingText("Telephone post navigation link") + + private val phoneSchemaLink = "tel://1234567890" + + @get:Rule + val composeTestRule = + AndroidComposeTestRule( + HomeActivityIntentTestRule(openLinksInExternalApp = OpenLinksInApp.ASK), + ) { it.activity } + + @get:Rule + val memoryLeaksRule = DetectMemoryLeaksRule() + + lateinit var externalLinksPage: TestAssetHelper.TestAsset + + @Before + override fun setUp() { + super.setUp() + externalLinksPage = TestAssetHelper.getAppLinksRedirectAsset(mockWebServer) + } + + @Test + fun askBeforeOpeningLinkInAppYoutubeSchemeCancelTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeSchemaUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun askBeforeOpeningLinkWithIntentSchemeTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(intentSchemaUrlLink) + mDevice.waitForIdle() + verifyOpenLinkInAnotherAppPromptIsNotShown() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun askBeforeOpeningLinkInAppYoutubeSchemeCancelMultiTapTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeSchemaUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + clickPageObject(youtubeSchemaUrlLink) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + verifyOpenLinkInAnotherAppPromptIsNotShown() + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + verifyOpenLinkInAnotherAppPromptIsNotShown() + } + } + + @Test + fun askBeforeOpeningLinkInAppYoutubeSchemeCancelOnlyAffectCurrentTabTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeSchemaUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + }.openTabDrawer(composeTestRule) { + }.openNewTab { + }.submitQuery(externalLinksPage.url.toString()) { + clickPageObject(youtubeSchemaUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun neverOpeningLinkInAppYoutubeTest() { + composeTestRule.activityRule.applySettingsExceptions { + it.openLinksInExternalApp = OpenLinksInApp.NEVER + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeUrlLink) + mDevice.waitForIdle() + verifyOpenLinkInAnotherAppPromptIsNotShown() + verifyUrl("youtube.com") + } + } + + @Test + fun neverOpeningLinkInAppYoutubeSchemeCancelTest() { + composeTestRule.activityRule.applySettingsExceptions { + it.openLinksInExternalApp = OpenLinksInApp.NEVER + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeSchemaUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun askBeforeOpeningLinkInAppYoutubeCancelTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(youtubeUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "YouTube") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl("youtube.com") + } + } + + @Test + fun appLinksRedirectPhoneLinkPromptTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(phoneUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "Phone") + clickPageObject(itemWithResIdAndText("android:id/button2", "Cancel")) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun askBeforeOpeningLinkInAppPhoneCancelTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(phoneUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "Phone") + } + } + + @Test + fun alwaysOpenPhoneLinkInAppTest() { + composeTestRule.activityRule.applySettingsExceptions { + it.openLinksInExternalApp = OpenLinksInApp.ALWAYS + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(phoneUrlLink) + mDevice.waitForIdle() + assertNativeAppOpens(Constants.PackageName.PHONE_APP, phoneSchemaLink) + } + } + + @Test + fun askBeforeOpeningPhoneLinkInAcceptTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(phoneUrlLink) + verifyOpenLinkInAnotherAppPrompt(appName = "Phone") + clickPageObject(itemWithResIdAndText("android:id/button1", "Open")) + mDevice.waitForIdle() + assertNativeAppOpens(Constants.PackageName.PHONE_APP, phoneSchemaLink) + mDevice.waitForIdle() + verifyUrl(externalLinksPage.url.toString()) + } + } + + @Test + fun appLinksNewTabRedirectAskTest() { + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(formRedirectLink) + verifyOpenLinkInAnotherAppPrompt(appName = "Phone") + } + } + + @Test + fun appLinksNewTabRedirectAlwaysTest() { + composeTestRule.activityRule.applySettingsExceptions { + it.openLinksInExternalApp = OpenLinksInApp.ALWAYS + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(formRedirectLink) + mDevice.waitForIdle() + assertNativeAppOpens(Constants.PackageName.PHONE_APP, phoneSchemaLink) + } + } + + @Test + fun appLinksNewTabRedirectNeverTest() { + composeTestRule.activityRule.applySettingsExceptions { + it.openLinksInExternalApp = OpenLinksInApp.NEVER + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(externalLinksPage.url) { + clickPageObject(formRedirectLink) + verifyOpenLinkInAnotherAppPrompt(appName = "Phone") + } + } +} diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -932,6 +932,22 @@ class BrowserRobot { ) } + fun verifyOpenLinkInAnotherAppPromptIsNotShown() { + assertUIObjectIsGone( + itemContainingText( + getStringResource( + applinksR.string.mozac_feature_applinks_normal_confirm_dialog_title_with_app_name, + appName, + ), + ), + itemContainingText( + getStringResource( + applinksR.string.mozac_feature_applinks_normal_confirm_dialog_message, + ), + ), + ) + } + fun verifyPrivateBrowsingOpenLinkInAnotherAppPrompt(appName: String, url: String, pageObject: UiObject) { for (i in 1..RETRY_COUNT) { try {