From de23193c12d20dfb93f950078ce2dc6163ab2d2c Mon Sep 17 00:00:00 2001 From: pollymce Date: Tue, 13 Oct 2026 11:41:19 +0100 Subject: [PATCH] Bug 2071822 - remove duplicate classes from androidTest dir. These break obfuscation and were not being used any more. They were previously used by SyncIntegrationTest, which is not currently run, so if this is ever restored we will have to figure out a minification-friendly way of doing this. some more details: - The androidTest copies break once the build is minified. R8 rewrites the app's version, so the test copy no longer matches it. - The test `AppRequestInterceptor` had drifted from the real one. It was missing about:home handling, the story UTM interceptor, interceptsAppInitiatedRequests and the content-URI telemetry. - the test version of AppRequestInterceptor had different behaviour to the app one. The app one overrode interceptsAppInitiatedRequests to set it to true and the test one didn't, so it defaulted to false. This means that using the app one, you get a "Open this link in xxx app?" dialog which the tests didn't previously expect. I've fixed this by dismissing the dialog in MainMenuTest. --- .../mozilla/fenix/AppRequestInterceptor.kt | 239 ------------------ .../org/mozilla/fenix/components/FxaServer.kt | 20 -- .../java/org/mozilla/fenix/ui/MainMenuTest.kt | 3 + 3 files changed, 3 insertions(+), 259 deletions(-) delete mode 100644 mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt delete mode 100644 mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/components/FxaServer.kt diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt deleted file mode 100644 index 1333c39e75917..0000000000000 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt +++ /dev/null @@ -1,239 +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 - -import android.content.Context -import android.net.ConnectivityManager -import androidx.annotation.VisibleForTesting -import androidx.core.content.getSystemService -import androidx.core.net.toUri -import androidx.navigation.NavController -import java.lang.ref.WeakReference -import mozilla.components.browser.errorpages.ErrorPages -import mozilla.components.browser.state.state.selectedOrDefaultSearchEngine -import mozilla.components.concept.engine.EngineSession -import mozilla.components.concept.engine.request.ErrorType -import mozilla.components.concept.engine.request.RequestInterceptor -import mozilla.components.feature.search.ext.buildSearchUrl -import org.mozilla.fenix.GleanMetrics.ErrorPage -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.isOnline -import org.mozilla.fenix.helpers.TestHelper.appContext - -/** - * This class overrides the application's request interceptor to deactivate the FxA web channel which is not supported - * on the staging servers. - */ -class AppRequestInterceptor( - private val context: Context, - private val isPrivateForSession: (EngineSession) -> Boolean = { false }, -) : RequestInterceptor { - - private var navController: WeakReference? = null - - fun setNavigationController(navController: NavController) { - this.navController = WeakReference(navController) - } - - override fun onLoadRequest( - engineSession: EngineSession, - uri: String, - lastUri: String?, - hasUserGesture: Boolean, - isSameDomain: Boolean, - isRedirect: Boolean, - isDirectNavigation: Boolean, - isSubframeRequest: Boolean, - ): RequestInterceptor.InterceptionResponse? { - interceptErrorPageAction(uri)?.let { - return it - } - - interceptFxaRequest( - engineSession, - uri, - lastUri, - hasUserGesture, - isSameDomain, - isRedirect, - isDirectNavigation, - isSubframeRequest, - ) - ?.let { response -> - return response - } - return context.components.services.appLinksInterceptor.onLoadRequest( - engineSession, - uri, - lastUri, - hasUserGesture, - isSameDomain, - isRedirect, - isDirectNavigation, - isSubframeRequest, - ) - } - - override fun onErrorRequest( - session: EngineSession, - errorType: ErrorType, - uri: String?, - ): RequestInterceptor.ErrorResponse? { - val improvedErrorType = improveErrorType(errorType) - val riskLevel = getRiskLevel(improvedErrorType) - - ErrorPage.visitedError.record(ErrorPage.VisitedErrorExtra(improvedErrorType.name)) - - val isPrivate = isPrivateForSession(session) - val archiveActionEnabled = context.components.settings.isWaybackMachineEnabled - - val errorPageUri = - ErrorPages.createUrlEncodedErrorPage( - context = context, - errorType = improvedErrorType, - uri = uri, - htmlResource = riskLevel.htmlRes, - errorStringsProvider = FenixErrorStringsProvider(), - isPrivate = isPrivate, - archiveActionEnabled = archiveActionEnabled, - ) - - return RequestInterceptor.ErrorResponse(errorPageUri) - } - - // This method is the only difference from the production code. - // Otherwise the code should be kept identical - @Suppress("LongParameterList") // Mirrors the production RequestInterceptor.onLoadRequest signature. - private fun interceptFxaRequest( - engineSession: EngineSession, - uri: String, - lastUri: String?, - hasUserGesture: Boolean, - isSameDomain: Boolean, - isRedirect: Boolean, - isDirectNavigation: Boolean, - isSubframeRequest: Boolean, - ): RequestInterceptor.InterceptionResponse? { - return appContext.components.services.accountsAuthFeature.interceptor.onLoadRequest( - engineSession, - uri, - lastUri, - hasUserGesture, - isSameDomain, - isRedirect, - isDirectNavigation, - isSubframeRequest, - ) - } - - /** - * Intercepts navigations the error page makes to the [ERROR_PAGE_ACTION_SCHEME] sentinel scheme to hand archive - * actions back to native code: searching the web for the failed page with the user's default search engine, or - * opening a located archived copy. Returns `null` for any other [uri] so normal navigation proceeds. - */ - private fun interceptErrorPageAction(uri: String): RequestInterceptor.InterceptionResponse? { - if (!uri.startsWith("$ERROR_PAGE_ACTION_SCHEME://")) { - return null - } - - val parsed = uri.toUri() - return when (parsed.host) { - ERROR_PAGE_ACTION_ATTEMPT -> { - ErrorPage.archiveButtonClicked.record() - RequestInterceptor.InterceptionResponse.Deny - } - - ERROR_PAGE_ACTION_SEARCH -> { - val query = parsed.getQueryParameter("q").orEmpty() - val searchEngine = context.components.core.store.state.search.selectedOrDefaultSearchEngine - if (query.isEmpty() || searchEngine == null) { - RequestInterceptor.InterceptionResponse.Deny - } else { - ErrorPage.archiveSearchWebSelected.record() - RequestInterceptor.InterceptionResponse.Url(searchEngine.buildSearchUrl(query)) - } - } - - ERROR_PAGE_ACTION_OPEN -> { - val archiveUrl = parsed.getQueryParameter("url").orEmpty() - if (archiveUrl.isEmpty()) { - RequestInterceptor.InterceptionResponse.Deny - } else { - ErrorPage.archivedVersionOpened.record() - RequestInterceptor.InterceptionResponse.Url(archiveUrl) - } - } - - else -> RequestInterceptor.InterceptionResponse.Deny - } - } - - /** Where possible, this will make the error type more accurate by including information not available to AC. */ - private fun improveErrorType(errorType: ErrorType): ErrorType { - // This is not an ideal solution. For context, see: - // https://github.com/mozilla-mobile/android-components/pull/5068#issuecomment-558415367 - - val isConnected: Boolean = context.getSystemService()!!.isOnline() - - return when { - errorType == ErrorType.ERROR_UNKNOWN_HOST && !isConnected -> ErrorType.ERROR_NO_INTERNET - errorType == ErrorType.ERROR_HTTPS_ONLY -> ErrorType.ERROR_HTTPS_ONLY - else -> errorType - } - } - - private fun getRiskLevel(errorType: ErrorType): RiskLevel = - when (errorType) { - ErrorType.UNKNOWN, - ErrorType.ERROR_NET_INTERRUPT, - ErrorType.ERROR_NET_TIMEOUT, - ErrorType.ERROR_CONNECTION_REFUSED, - ErrorType.ERROR_LOCAL_NETWORK_ACCESS_DENIED, - ErrorType.ERROR_UNKNOWN_SOCKET_TYPE, - ErrorType.ERROR_REDIRECT_LOOP, - ErrorType.ERROR_OFFLINE, - ErrorType.ERROR_NET_RESET, - ErrorType.ERROR_UNSAFE_CONTENT_TYPE, - ErrorType.ERROR_CORRUPTED_CONTENT, - ErrorType.ERROR_CONTENT_CRASHED, - ErrorType.ERROR_INVALID_CONTENT_ENCODING, - ErrorType.ERROR_UNKNOWN_HOST, - ErrorType.ERROR_MALFORMED_URI, - ErrorType.ERROR_FILE_NOT_FOUND, - ErrorType.ERROR_FILE_ACCESS_DENIED, - ErrorType.ERROR_PROXY_CONNECTION_REFUSED, - ErrorType.ERROR_UNKNOWN_PROXY_HOST, - ErrorType.ERROR_NO_INTERNET, - ErrorType.ERROR_HTTPS_ONLY, - ErrorType.ERROR_BAD_HSTS_CERT, - ErrorType.ERROR_UNKNOWN_PROTOCOL -> RiskLevel.Low - - ErrorType.ERROR_SECURITY_BAD_CERT, - ErrorType.ERROR_SECURITY_SSL, - ErrorType.ERROR_PORT_BLOCKED -> RiskLevel.Medium - - ErrorType.ERROR_SAFEBROWSING_HARMFUL_URI, - ErrorType.ERROR_SAFEBROWSING_MALWARE_URI, - ErrorType.ERROR_SAFEBROWSING_PHISHING_URI, - ErrorType.ERROR_SAFEBROWSING_UNWANTED_URI, - ErrorType.ERROR_HARMFULADDON_URI -> RiskLevel.High - } - - internal enum class RiskLevel(val htmlRes: String) { - Low(LOW_AND_MEDIUM_RISK_ERROR_PAGES), - Medium(LOW_AND_MEDIUM_RISK_ERROR_PAGES), - High(HIGH_RISK_ERROR_PAGES), - } - - companion object { - internal const val LOW_AND_MEDIUM_RISK_ERROR_PAGES = "low_and_medium_risk_error_pages.html" - internal const val HIGH_RISK_ERROR_PAGES = "high_risk_error_pages.html" - - @VisibleForTesting internal const val ERROR_PAGE_ACTION_SCHEME = "firefox-error-action" - private const val ERROR_PAGE_ACTION_ATTEMPT = "attempt" - private const val ERROR_PAGE_ACTION_SEARCH = "search" - private const val ERROR_PAGE_ACTION_OPEN = "open" - } -} diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/components/FxaServer.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/components/FxaServer.kt deleted file mode 100644 index ad0661e208a47..0000000000000 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/components/FxaServer.kt +++ /dev/null @@ -1,20 +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.components - -import android.content.Context -import mozilla.appservices.fxaclient.FxaConfig -import mozilla.appservices.fxaclient.FxaServer - -/** Utility to configure Firefox Account stage servers. */ -object FxaServer { - private const val CLIENT_ID = "a2270f727f45f648" - private const val REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob:oauth-redirect-webchannel" - - @Suppress("UNUSED_PARAMETER") - fun config(context: Context): FxaConfig { - return FxaConfig(FxaServer.Stage, CLIENT_ID, REDIRECT_URL) - } -} diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt index 9333e1abc97f0..1564b9f14d733 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt @@ -132,6 +132,7 @@ class MainMenuTest { navigationToolbar(composeTestRule) {} .enterURLAndEnterToBrowser(pdfPage.url) { + clickPageObject(composeTestRule, itemContainingText("Stay in")) waitForPageToLoad() verifyPageContent(pdfPage.content) } @@ -604,6 +605,7 @@ class MainMenuTest { navigationToolbar(composeTestRule) {} .enterURLAndEnterToBrowser(youtubeURL) { + clickPageObject(composeTestRule, itemContainingText("Stay in")) waitForPageToLoad(waitingTime) } .openThreeDotMenu { @@ -1669,6 +1671,7 @@ class MainMenuTest { ) browserScreen(composeTestRule) { + clickPageObject(composeTestRule, itemContainingText("Stay in")) verifyPageContent(customTabPDF.content) }