tor-browser

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

commit b596f41b60902b26eb9bf3f4cf7735fbae4bd7dd
parent a20df35f4aa93c6d111a9482927db93ecb642b84
Author: AndiAJ <andiaj@users.noreply.github.com>
Date:   Thu, 30 Oct 2025 11:53:55 +0000

Bug 1996247 - New Unified Trust Panel related UI smoke tests r=aaronmt,mcarare

New "Unified Trust Panel" related smoke tests.

All 10 UI tests successfully passed 50x on Firebase ✅

I've encountered a problem and had to make some changes to the TrustPanelMiddleware because while automating these scenarios, after clicking the "Enhanced tracking protection" option, the tests failed with this error:

```
java.lang.IllegalThreadStateException: Expected thread 2 ("main"), but running on thread 167 ("pool-32-thread-1")
at org.mozilla.gecko.util.ThreadUtils.assertOnThreadComparison(ThreadUtils.java:127)
at org.mozilla.gecko.util.ThreadUtils.assertOnThread(ThreadUtils.java:93)
at org.mozilla.gecko.util.ThreadUtils.assertOnUiThread(ThreadUtils.java:84)
at org.mozilla.geckoview.GeckoRuntime.getStorageController(GeckoRuntime.java:1119)
at mozilla.components.browser.engine.gecko.GeckoTrackingProtectionExceptionStorage.add(GeckoTrackingProtectionExceptionStorage.kt:66)
at mozilla.components.feature.session.TrackingProtectionUseCases$AddExceptionUseCase.invoke(TrackingProtectionUseCases.kt:48)
at mozilla.components.feature.session.TrackingProtectionUseCases$AddExceptionUseCase.invoke$default(TrackingProtectionUseCases.kt:44)
at org.mozilla.fenix.settings.trustpanel.middleware.TrustPanelMiddleware$toggleTrackingProtection$1.invokeSuspend(TrustPanelMiddleware.kt:92)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at androidx.compose.ui.test.ApplyingContinuationInterceptor$SendApplyContinuation.resumeWith(ApplyingContinuationInterceptor.kt:52)
at androidx.compose.ui.test.FrameDeferringContinuationInterceptor$FrameDeferredContinuation.resumeWith(FrameDeferringContinuationInterceptor.jvm.kt:187)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:279)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:358)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:134)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:53)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:44)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
at org.mozilla.fenix.settings.trustpanel.middleware.TrustPanelMiddleware.toggleTrackingProtection(TrustPanelMiddleware.kt:89)
at org.mozilla.fenix.settings.trustpanel.middleware.TrustPanelMiddleware.invoke(TrustPanelMiddleware.kt:73)
at org.mozilla.fenix.settings.trustpanel.middleware.TrustPanelMiddleware.invoke(TrustPanelMiddleware.kt:49)
at mozilla.components.lib.state.internal.ReducerChainBuilder.build$lambda$2$0(ReducerChainBuilder.kt:62)
at mozilla.components.lib.state.internal.ReducerChainBuilder.$r8$lambda$qHkQY5kClmN8JcchHLSDzKdcDSM(Unknown Source:0)
at mozilla.components.lib.state.internal.ReducerChainBuilder$$ExternalSyntheticLambda2.invoke(D8$$SyntheticClass:0)
at mozilla.components.lib.state.internal.ReducerChainBuilder.build$lambda$1(ReducerChainBuilder.kt:57)
at mozilla.components.lib.state.internal.ReducerChainBuilder.$r8$lambda$6MYQUAWlO75P4JJe4GYUjKauyKM(Unknown Source:0)
at mozilla.components.lib.state.internal.ReducerChainBuilder$$ExternalSyntheticLambda1.invoke(D8$$SyntheticClass:0)
at mozilla.components.lib.state.internal.ReducerChainBuilder.build$lambda$2$0(ReducerChainBuilder.kt:62)
at mozilla.components.lib.state.internal.ReducerChainBuilder.$r8$lambda$qHkQY5kClmN8JcchHLSDzKdcDSM(Unknown Source:0)
at mozilla.components.lib.state.internal.ReducerChainBuilder$$ExternalSyntheticLambda2.invoke(D8$$SyntheticClass:0)
at mozilla.components.lib.state.Store$dispatch$1.invokeSuspend(Store.kt:96)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
```

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

Diffstat:
Amobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/UnifiedTrustPanelTest.kt | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt | 13+++++++++++++
Mmobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt | 15+++++++++++++++
Amobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/UnifiedTrustPanelRobot.kt | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/middleware/TrustPanelMiddleware.kt | 4++--
5 files changed, 604 insertions(+), 2 deletions(-)

diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/UnifiedTrustPanelTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/UnifiedTrustPanelTest.kt @@ -0,0 +1,416 @@ +/* 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/. */ + +@file:Suppress("DEPRECATION") + +package org.mozilla.fenix.ui + +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.core.net.toUri +import androidx.test.rule.ActivityTestRule +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.IntentReceiverActivity +import org.mozilla.fenix.customannotations.SmokeTest +import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.helpers.DataGenerationHelper.createCustomTabIntent +import org.mozilla.fenix.helpers.HomeActivityIntentTestRule +import org.mozilla.fenix.helpers.TestAssetHelper.getEnhancedTrackingProtectionAsset +import org.mozilla.fenix.helpers.TestAssetHelper.getGenericAsset +import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeLong +import org.mozilla.fenix.helpers.TestHelper.appContext +import org.mozilla.fenix.helpers.TestSetup +import org.mozilla.fenix.helpers.perf.DetectMemoryLeaksRule +import org.mozilla.fenix.ui.robots.browserScreen +import org.mozilla.fenix.ui.robots.customTabScreen +import org.mozilla.fenix.ui.robots.navigationToolbar + +class UnifiedTrustPanelTest : TestSetup() { + @get:Rule + val composeTestRule = + AndroidComposeTestRule( + HomeActivityIntentTestRule( + isUnifiedTrustPanelEnabled = true, + isComposableToolbarEnabled = false, + isPWAsPromptEnabled = false, + ), + ) { it.activity } + + @get:Rule + val intentReceiverActivityTestRule = ActivityTestRule( + IntentReceiverActivity::class.java, + true, + false, + ) + + @get:Rule + val memoryLeaksRule = DetectMemoryLeaksRule() + + // TestRail: https://mozilla.testrail.io/index.php?/cases/view/3186718 + @SmokeTest + @Test + fun verifySecurePageConnectionFromQuickSettingsWithNoTrackersTest() { + val firstPage = "https://mozilla-mobile.github.io/testapp" + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstPage.toUri()) { + verifyPageContent("Lets test!") + } + navigationToolbar { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = "Test App", + webSiteURL = firstPage.toUri().host.toString(), + isTheWebSiteSecure = true, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = false, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = "Test App", + webSiteURL = firstPage.toUri().host.toString(), + isEnhancedTrackingProtectionEnabled = false, + isTheWebSiteSecure = true, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186721 + @SmokeTest + @Test + fun verifyInsecurePageConnectionFromQuickSettingsWithTrackersTest() { + appContext.settings().setStrictETP() + val genericPage = getGenericAsset(mockWebServer, 1) + val trackingProtectionPage = getEnhancedTrackingProtectionAsset(mockWebServer).url + + // browsing a generic page to allow GV to load on a fresh run + navigationToolbar { + }.enterURLAndEnterToBrowser(genericPage.url) { + }.openTabDrawer(composeTestRule) { + closeTab() + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(trackingProtectionPage) { + verifyTrackingProtectionWebContent("social blocked") + verifyTrackingProtectionWebContent("ads blocked") + verifyTrackingProtectionWebContent("analytics blocked") + verifyTrackingProtectionWebContent("Fingerprinting blocked") + verifyTrackingProtectionWebContent("Cryptomining blocked") + } + navigationToolbar { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = trackingProtectionPage.host.toString(), + webSiteURL = trackingProtectionPage.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = true, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = trackingProtectionPage.host.toString(), + webSiteURL = trackingProtectionPage.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = false, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186723 + @SmokeTest + @Test + fun verifyClearCookiesAndSiteDataFromQuickSettingsTest() { + val loginPage = "https://mozilla-mobile.github.io/testapp/loginForm" + val originWebsite = "mozilla-mobile.github.io" + + navigationToolbar { + }.enterURLAndEnterToBrowser(loginPage.toUri()) { + waitForPageToLoad(waitingTimeLong) + } + navigationToolbar { + }.openUnifiedTrustPanel { + clickTheClearCookiesAndSiteDataButton(composeTestRule) + verifyTheClearCookiesAndSiteDataDialog(composeTestRule, originWebsite) + } + } + + // TestRail: https://mozilla.testrail.io/index.php?/cases/view/3186719 + @Test + fun verifySecurePageConnectionFromQuickSettingsWithTrackersTest() { + appContext.settings().setStrictETP() + val genericPage = getGenericAsset(mockWebServer, 1) + val trackingProtectionPage = "https://senglehardt.com/test/trackingprotection/test_pages/tracking_protection" + + // browsing a generic page to allow GV to load on a fresh run + navigationToolbar { + }.enterURLAndEnterToBrowser(genericPage.url) { + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(trackingProtectionPage.toUri()) { + verifyPageContent("Tracker Blocking") + } + navigationToolbar { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = trackingProtectionPage.toUri().host.toString(), + webSiteURL = trackingProtectionPage.toUri().host.toString(), + isTheWebSiteSecure = true, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = true, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = trackingProtectionPage.toUri().host.toString(), + webSiteURL = trackingProtectionPage.toUri().host.toString(), + isEnhancedTrackingProtectionEnabled = false, + isTheWebSiteSecure = true, + isTrackerBlockingEnabled = false, + areTrackersBlocked = true, + ) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186720 + @Test + fun verifyInsecurePageConnectionFromQuickSettingsWithNoTrackersTest() { + val genericPage = getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericPage.url) { + verifyPageContent(genericPage.content) + } + navigationToolbar { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = genericPage.title, + webSiteURL = genericPage.url.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = false, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = genericPage.title, + webSiteURL = genericPage.url.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = false, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } + + // TestRail: https://mozilla.testrail.io/index.php?/cases/view/3186709 + @Test + fun verifySecurePageConnectionFromQuickSettingsWithNoTrackersInCustomTabsTest() { + val customTabPage = "https://mozilla-mobile.github.io/testapp" + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = customTabPage, + ), + ) + + browserScreen { + verifyPageContent("Lets test!") + } + + customTabScreen { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = "Test App", + webSiteURL = customTabPage.toUri().host.toString(), + isTheWebSiteSecure = true, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = false, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = "Test App", + webSiteURL = customTabPage.toUri().host.toString(), + isEnhancedTrackingProtectionEnabled = false, + isTheWebSiteSecure = true, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } + + // TestRail: https://mozilla.testrail.io/index.php?/cases/view/3186710 + @SmokeTest + @Test + fun verifySecurePageConnectionFromQuickSettingsWithTrackersInCustomTabsTest() { + appContext.settings().setStrictETP() + val genericPage = getGenericAsset(mockWebServer, 1) + val customTabPage = "https://senglehardt.com/test/trackingprotection/test_pages/tracking_protection" + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = genericPage.url.toString(), + ), + ) + + browserScreen { + verifyPageContent(genericPage.content) + } + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = customTabPage, + ), + ) + + browserScreen { + verifyPageContent("Tracker Blocking") + } + customTabScreen { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.toUri().host.toString(), + webSiteURL = customTabPage.toUri().host.toString(), + isTheWebSiteSecure = true, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = true, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.toUri().host.toString(), + webSiteURL = customTabPage.toUri().host.toString(), + isEnhancedTrackingProtectionEnabled = false, + isTheWebSiteSecure = true, + isTrackerBlockingEnabled = false, + areTrackersBlocked = true, + ) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186711 + @SmokeTest + @Test + fun verifyInsecurePageConnectionFromQuickSettingsWithNoTrackersInCustomTabsTest() { + val customTabPage = getGenericAsset(mockWebServer, 1) + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = customTabPage.url.toString(), + ), + ) + + browserScreen { + verifyPageContent(customTabPage.content) + } + + customTabScreen { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.title, + webSiteURL = customTabPage.url.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = false, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.title, + webSiteURL = customTabPage.url.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = false, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186714 + @SmokeTest + @Test + fun verifyClearCookiesAndSiteDataFromQuickSettingsInCustomTabsTest() { + val customTabPage = "https://mozilla-mobile.github.io/testapp/loginForm" + val originWebsite = "mozilla-mobile.github.io" + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = customTabPage, + ), + ) + + customTabScreen { + waitForPageToLoad(waitingTimeLong) + }.openUnifiedTrustPanel { + clickTheClearCookiesAndSiteDataButton(composeTestRule) + verifyTheClearCookiesAndSiteDataDialog(composeTestRule, originWebsite) + } + } + + // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3186712 + @Test + fun verifyInsecurePageConnectionFromQuickSettingsWithTrackersInCustomTabsTest() { + appContext.settings().setStrictETP() + + val customTabPage = getEnhancedTrackingProtectionAsset(mockWebServer).url + + intentReceiverActivityTestRule.launchActivity( + createCustomTabIntent( + pageUrl = customTabPage.toString(), + ), + ) + + browserScreen { + verifyTrackingProtectionWebContent("social blocked") + verifyTrackingProtectionWebContent("ads blocked") + verifyTrackingProtectionWebContent("analytics blocked") + verifyTrackingProtectionWebContent("Fingerprinting blocked") + verifyTrackingProtectionWebContent("Cryptomining blocked") + } + customTabScreen { + }.openUnifiedTrustPanel { + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.host.toString(), + webSiteURL = customTabPage.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = true, + isTrackerBlockingEnabled = true, + areTrackersBlocked = true, + ) + clickTheEnhancedTrackingProtectionOption(composeTestRule) + verifyUnifiedTrustPanelItems( + composeTestRule = composeTestRule, + webSite = customTabPage.host.toString(), + webSiteURL = customTabPage.host.toString(), + isTheWebSiteSecure = false, + isEnhancedTrackingProtectionEnabled = false, + isTrackerBlockingEnabled = false, + areTrackersBlocked = false, + ) + } + } +} diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.ui.robots import android.net.Uri import android.util.Log +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.ComposeTestRule @@ -388,6 +389,18 @@ class CustomTabRobot { BrowserRobot().interact() return BrowserRobot.Transition() } + + fun openUnifiedTrustPanel(interact: UnifiedTrustPanelRobot.() -> Unit): UnifiedTrustPanelRobot.Transition { + Log.i(TAG, "openUnifiedTrustPanel: Waiting for $waitingTime ms for site security button to exist") + itemWithResId("$packageName:id/mozac_browser_toolbar_site_info_indicator").waitForExists(waitingTime) + Log.i(TAG, "openUnifiedTrustPanel: Waited for $waitingTime ms for site security button to exist") + Log.i(TAG, "openUnifiedTrustPanel: Trying to click site security button") + itemWithResId("$packageName:id/mozac_browser_toolbar_site_info_indicator").click() + Log.i(TAG, "openUnifiedTrustPanel: Clicked site security button") + + UnifiedTrustPanelRobot().interact() + return UnifiedTrustPanelRobot.Transition() + } } } diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt @@ -647,6 +647,21 @@ class NavigationToolbarRobot { SearchRobot().interact() return SearchRobot.Transition() } + + fun openUnifiedTrustPanel(interact: UnifiedTrustPanelRobot.() -> Unit): UnifiedTrustPanelRobot.Transition { + Log.i(TAG, "openUnifiedTrustPanel: Waiting for $waitingTime ms for site security button to exist") + itemWithResId("$packageName:id/mozac_browser_toolbar_site_info_indicator").waitForExists(waitingTime) + Log.i(TAG, "openUnifiedTrustPanel: Waited for $waitingTime ms for site security button to exist") + Log.i(TAG, "openUnifiedTrustPanel: Trying to click site security button") + itemWithResId("$packageName:id/mozac_browser_toolbar_site_info_indicator").click() + Log.i(TAG, "openUnifiedTrustPanel: Clicked site security button") + Log.i(TAG, "openUnifiedTrustPanel: Waiting for $waitingTime for the unified trust panel to exist") + itemWithResId("$packageName:id/design_bottom_sheet").waitForExists(waitingTime) + Log.i(TAG, "openUnifiedTrustPanel: Waited for $waitingTime for the unified trust panel to exist") + + UnifiedTrustPanelRobot().interact() + return UnifiedTrustPanelRobot.Transition() + } } } diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/UnifiedTrustPanelRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/UnifiedTrustPanelRobot.kt @@ -0,0 +1,158 @@ +package org.mozilla.fenix.ui.robots + +import android.util.Log +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.ComposeTestRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.core.text.HtmlCompat +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.Constants.TAG +import org.mozilla.fenix.helpers.DataGenerationHelper.getStringResource +import org.mozilla.fenix.helpers.TestHelper.appName +import org.mozilla.fenix.helpers.TestHelper.waitForAppWindowToBeUpdated + +class UnifiedTrustPanelRobot { + + fun verifyUnifiedTrustPanelItems( + composeTestRule: ComposeTestRule, + webSite: String, + webSiteURL: String, + isTheWebSiteSecure: Boolean, + isEnhancedTrackingProtectionEnabled: Boolean, + isTrackerBlockingEnabled: Boolean, + areTrackersBlocked: Boolean, + ) { + waitForAppWindowToBeUpdated() + Log.i(TAG, "verifyUnifiedTrustPanelItems: Trying to verify that the web site title: $webSite is displayed") + composeTestRule.onNodeWithText(webSite, useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyUnifiedTrustPanelItems: Verified that the web site title: $webSite is displayed") + Log.i(TAG, "verifyUnifiedTrustPanelItems: Trying to verify that the web site url: $webSiteURL is displayed") + composeTestRule.onNodeWithText(webSiteURL, useUnmergedTree = true, substring = true).assertIsDisplayed() + Log.i(TAG, "verifyUnifiedTrustPanelItems: Verified that the web site url: $webSiteURL is displayed") + verifyTheEnhancedTrackingProtectionState(composeTestRule, isEnhancedTrackingProtectionEnabled, isTheWebSiteSecure) + verifyTheTrackersBlockedOptionState(composeTestRule, isTrackerBlockingEnabled, areTrackersBlocked) + verifyTheSiteSecurityOption(composeTestRule, isTheWebSiteSecure) + Log.i(TAG, "verifyUnifiedTrustPanelItems: Trying to verify that the \"Clear cookies and site data\" option is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.clear_site_data), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyUnifiedTrustPanelItems: Verified that the \"Clear cookies and site data\" option is displayed") + Log.i(TAG, "verifyUnifiedTrustPanelItems: Trying to verify that the \"Privacy settings\" hyperlink is displayed") + composeTestRule.onNodeWithContentDescription("Privacy Settings Links available", useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyUnifiedTrustPanelItems: Verified that the \"Privacy settings\" hyperlink is displayed") + } + + fun verifyTheEnhancedTrackingProtectionState(composeTestRule: ComposeTestRule, isEnhancedTrackingProtectionEnabled: Boolean, isTheWebSiteSecure: Boolean) { + if (isEnhancedTrackingProtectionEnabled) { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that \"Enhanced tracking protection\" is enabled") + if (isTheWebSiteSecure) { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the website is secure") + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"$appName is on guard\" banner title and message are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_protected_title, argument = appName), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_protected_description), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"$appName is on guard\" banner title and message are displayed") + } else { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the website is not secure") + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"Be careful on this site\" banner title and message are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_secure_title, argument = appName), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_secure_description), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"Be careful on this site\" banner title and message are displayed") + } + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"Enhanced tracking protection\" option, message and \"On\" state are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_label), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_enabled_description_2), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_on), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"Enhanced tracking protection\" option, message and \"On\" state are displayed") + } else { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that \"Enhanced tracking protection\" is not enabled") + if (isTheWebSiteSecure) { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the website is secure") + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"You turned off protection\" banner title and message are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_protected_title), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_protected_description, argument = appName), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"You turned off protection\" banner title and message are displayed") + } else { + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the website is not secure") + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"Be careful on this site\" banner title and message are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_secure_title, argument = appName), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_banner_not_secure_description), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"Be careful on this site\" banner title and message are displayed") + } + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Trying to verify that the \"Enhanced tracking protection\" option, message and \"Off\" state are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_label), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_disabled_description_2), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_off), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheEnhancedTrackingProtectionState: Verified that the \"Enhanced tracking protection\" option, message and \"Off\" state are displayed") + } + } + + fun verifyTheTrackersBlockedOptionState(composeTestRule: ComposeTestRule, isTrackerBlockingEnabled: Boolean, areTrackersBlocked: Boolean) { + if (isTrackerBlockingEnabled) { + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that the tracker blocking is enabled") + if (areTrackersBlocked) { + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that trackers are blocked") + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Trying to verify that the \"Trackers blocked\" option is displayed") + composeTestRule.onNodeWithText("Trackers blocked:", useUnmergedTree = true, substring = true).assertIsDisplayed() + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that the \"Trackers blocked\" option is displayed") + } else { + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Trying to verify that the \"No trackers found\" option is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_no_trackers_blocked), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that the \"No trackers found\" option is displayed") + } + } else { + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that the tracker blocking is not enabled") + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Trying to verify that the \"Trackers aren't blocked\" option is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_disabled_no_trackers_blocked), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheTrackersBlockedOptionState: Verified that the \"Trackers aren't blocked\" option is displayed") + } + } + + fun verifyTheSiteSecurityOption(composeTestRule: ComposeTestRule, isTheWebSiteSecure: Boolean) { + if (isTheWebSiteSecure) { + Log.i(TAG, "verifyTheSiteSecurityOption: Verified that the web site is secure") + Log.i(TAG, "verifyTheSiteSecurityOption: Trying to verify that the \"Secure connection\" option and \"Verified by\" description are displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.connection_security_panel_secure), useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onNodeWithText("Verified by", useUnmergedTree = true, substring = true).assertIsDisplayed() + Log.i(TAG, "verifyTheSiteSecurityOption: Verified that the \"Secure connection\" option and \"Verified by\" description are displayed") + } else { + Log.i(TAG, "verifyTheSiteSecurityOption: Verified that the web site is not secure") + Log.i(TAG, "verifyTheSiteSecurityOption: Trying to verify that the \"Connection not secure\" option is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.connection_security_panel_not_secure), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheSiteSecurityOption: Verified that the \"Connection not secure\" option is displayed") + } + } + + fun clickTheEnhancedTrackingProtectionOption(composeTestRule: ComposeTestRule) { + Log.i(TAG, "clickTheEnhancedTrackingProtectionOption: Trying to click the ETP option") + composeTestRule.onNodeWithText(getStringResource(R.string.protection_panel_etp_toggle_label), useUnmergedTree = true).performClick() + Log.i(TAG, "clickTheEnhancedTrackingProtectionOption: Clicked the ETP option") + composeTestRule.waitForIdle() + } + + fun clickTheClearCookiesAndSiteDataButton(composeTestRule: ComposeTestRule) { + Log.i(TAG, "clickTheClearCookiesAndSiteDataButton: Trying to click the \"Clear cookies and site data\" button") + composeTestRule.onNodeWithText(getStringResource(R.string.clear_site_data), useUnmergedTree = true).performClick() + Log.i(TAG, "clickTheClearCookiesAndSiteDataButton: Clicked the \"Clear cookies and site data\" button") + } + + fun verifyTheClearCookiesAndSiteDataDialog(composeTestRule: ComposeTestRule, webSite: String) { + // convert HTML-formatted string resource to plain text + val clearCookiesAndSiteDataDialogRawDescription = getStringResource(R.string.clear_site_data_dialog_description, argument = webSite) + val clearCookiesAndSiteDataDialogConvertedDescription = HtmlCompat.fromHtml(clearCookiesAndSiteDataDialogRawDescription, HtmlCompat.FROM_HTML_MODE_LEGACY).toString() + + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Trying to verify that the \"Clear cookies and site data\" dialog title is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.clear_site_data), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Verified that the \"Clear cookies and site data\" dialog title is displayed") + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Trying to verify that the \"Clear cookies and site data\" dialog message is displayed") + composeTestRule.onNodeWithText(clearCookiesAndSiteDataDialogConvertedDescription, useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Verified that the \"Clear cookies and site data\" dialog message is displayed") + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Trying to verify that the \"Clear\" dialog button is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.clear_site_data_dialog_positive_button_text), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Verified that the \"Clear\" dialog button is displayed") + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Trying to verify that the \"Cancel\" dialog button is displayed") + composeTestRule.onNodeWithText(getStringResource(R.string.clear_site_data_dialog_negative_button_text), useUnmergedTree = true).assertIsDisplayed() + Log.i(TAG, "verifyTheClearCookiesAndSiteDataDialog: Verified that the \"Cancel\" dialog button is displayed") + } + + class Transition +} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/middleware/TrustPanelMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/middleware/TrustPanelMiddleware.kt @@ -56,7 +56,7 @@ class TrustPanelMiddleware( private val permissionStorage: PermissionStorage, private val requestPermissionsLauncher: ActivityResultLauncher<Array<String>>, private val onDismiss: suspend () -> Unit, - private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO), + private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main), ) : Middleware<TrustPanelState, TrustPanelAction> { override fun invoke( @@ -86,7 +86,7 @@ class TrustPanelMiddleware( private fun toggleTrackingProtection( currentState: TrustPanelState, - ) = scope.launch { + ) = scope.launch(Dispatchers.Main) { currentState.sessionState?.let { session -> if (currentState.isTrackingProtectionEnabled) { trackingProtectionUseCases.addException(session.id)