For #26884 - Merge FeatureSettingsHelper with activity rules

This commit is contained in:
Mugurell 2022-09-24 18:13:55 +03:00 committed by mergify[bot]
parent ddf0dc3b4d
commit 0442695972
5 changed files with 386 additions and 68 deletions

View File

@ -4,72 +4,84 @@
package org.mozilla.fenix.helpers
import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import org.mozilla.fenix.ext.settings
class FeatureSettingsHelper {
private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private val settings = context.settings()
/**
* Helper for querying the status and modifying various features and settings in the application.
*/
interface FeatureSettingsHelper {
/**
* Whether the onboarding for existing users should be shown or not.
* It should appear only once on the first visit to homescreen.
*/
var isHomeOnboardingDialogEnabled: Boolean
// saving default values of feature flags
private var isPocketEnabled: Boolean = settings.showPocketRecommendationsFeature
private var isJumpBackInCFREnabled: Boolean = settings.shouldShowJumpBackInCFR
private var isRecentTabsFeatureEnabled: Boolean = settings.showRecentTabsFeature
private var isRecentlyVisitedFeatureEnabled: Boolean = settings.historyMetadataUIFeature
private var isUserKnowsAboutPwasTrue: Boolean = settings.userKnowsAboutPwas
private var isTCPCFREnabled: Boolean = settings.shouldShowTotalCookieProtectionCFR
private var isWallpaperOnboardingEnabled: Boolean = settings.showWallpaperOnboarding
/**
* Whether the Pocket stories feature is enabled or not.
*/
var isPocketEnabled: Boolean
fun setPocketEnabled(enabled: Boolean) {
settings.showPocketRecommendationsFeature = enabled
}
/**
* Whether the "Jump back in" CFR should be shown or not.
* It should appear on the first visit to homescreen given that there is a tab opened.
*/
var isJumpBackInCFREnabled: Boolean
fun setJumpBackCFREnabled(enabled: Boolean) {
settings.shouldShowJumpBackInCFR = enabled
}
/**
* Whether the onboarding dialog for choosing wallpapers should be shown or not.
*/
var isWallpaperOnboardingEnabled: Boolean
fun setShowWallpaperOnboarding(enabled: Boolean) {
settings.showWallpaperOnboarding = enabled
}
/**
* Whether the "Jump back in" homescreen section is enabled or not.
* It shows the last visited tab on this device and on other synced devices.
*/
var isRecentTabsFeatureEnabled: Boolean
fun setRecentTabsFeatureEnabled(enabled: Boolean) {
settings.showRecentTabsFeature = enabled
}
/**
* Whether the "Recently visited" homescreen section is enabled or not.
* It can show up to 9 history highlights and history groups.
*/
var isRecentlyVisitedFeatureEnabled: Boolean
fun setRecentlyVisitedFeatureEnabled(enabled: Boolean) {
settings.historyMetadataUIFeature = enabled
}
/**
* Whether the onboarding dialog for PWAs should be shown or not.
* It can show the first time a website that can be installed as a PWA is accessed.
*/
var isPWAsPromptEnabled: Boolean
fun setStrictETPEnabled() {
settings.setStrictETP()
}
fun disablePwaCFR(disable: Boolean) {
settings.userKnowsAboutPwas = disable
}
fun deleteSitePermissions(delete: Boolean) {
settings.deleteSitePermissions = delete
}
/**
* Whether the "Site permissions" option is checked in the "Delete browsing data" screen or not.
*/
var isDeleteSitePermissionsEnabled: Boolean
/**
* Enable or disable showing the TCP CFR when accessing a webpage for the first time.
*/
fun setTCPCFREnabled(shouldShowCFR: Boolean) {
settings.shouldShowTotalCookieProtectionCFR = shouldShowCFR
}
var isTCPCFREnabled: Boolean
// Important:
// Use this after each test if you have modified these feature settings
// to make sure the app goes back to the default state
fun resetAllFeatureFlags() {
settings.showPocketRecommendationsFeature = isPocketEnabled
settings.shouldShowJumpBackInCFR = isJumpBackInCFREnabled
settings.showRecentTabsFeature = isRecentTabsFeatureEnabled
settings.historyMetadataUIFeature = isRecentlyVisitedFeatureEnabled
settings.userKnowsAboutPwas = isUserKnowsAboutPwasTrue
settings.shouldShowTotalCookieProtectionCFR = isTCPCFREnabled
settings.showWallpaperOnboarding = isWallpaperOnboardingEnabled
/**
* The current "Enhanced Tracking Protection" policy.
* @see ETPPolicy
*/
var etpPolicy: ETPPolicy
fun applyFlagUpdates()
fun resetAllFeatureFlags()
companion object {
val settings = InstrumentationRegistry.getInstrumentation().targetContext.settings()
}
}
/**
* All "Enhanced Tracking Protection" modes.
*/
enum class ETPPolicy {
STANDARD,
STRICT,
CUSTOM,
;
}

View File

@ -0,0 +1,159 @@
/* 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.helpers
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.helpers.ETPPolicy.CUSTOM
import org.mozilla.fenix.helpers.ETPPolicy.STANDARD
import org.mozilla.fenix.helpers.ETPPolicy.STRICT
import org.mozilla.fenix.helpers.FeatureSettingsHelper.Companion.settings
import org.mozilla.fenix.helpers.TestHelper.appContext
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.utils.Settings
/**
* Helper for querying the status and modifying various features and settings in the application.
*/
class FeatureSettingsHelperDelegate : FeatureSettingsHelper {
/**
* The current feature flags used inside the app before the tests start.
* These will be restored when the tests end.
*/
private val initialFeatureFlags = FeatureFlags(
isHomeOnboardingDialogEnabled = settings.showHomeOnboardingDialog,
homeOnboardingDialogVersion = getHomeOnboardingVersion(),
isPocketEnabled = settings.showPocketRecommendationsFeature,
isJumpBackInCFREnabled = settings.shouldShowJumpBackInCFR,
isRecentTabsFeatureEnabled = settings.showRecentTabsFeature,
isRecentlyVisitedFeatureEnabled = settings.historyMetadataUIFeature,
isPWAsPromptEnabled = !settings.userKnowsAboutPwas,
isTCPCFREnabled = settings.shouldShowTotalCookieProtectionCFR,
isWallpaperOnboardingEnabled = settings.showWallpaperOnboarding,
isDeleteSitePermissionsEnabled = settings.deleteSitePermissions,
etpPolicy = getETPPolicy(settings),
)
/**
* The current feature flags updated in tests.
*/
private var updatedFeatureFlags = initialFeatureFlags.copy()
override var isHomeOnboardingDialogEnabled: Boolean
get() = updatedFeatureFlags.isHomeOnboardingDialogEnabled &&
FenixOnboarding(appContext).userHasBeenOnboarded()
set(value) {
updatedFeatureFlags.isHomeOnboardingDialogEnabled = value
updatedFeatureFlags.homeOnboardingDialogVersion = when (value) {
true -> FenixOnboarding.CURRENT_ONBOARDING_VERSION
false -> 0
}
}
override var isPocketEnabled: Boolean by updatedFeatureFlags::isPocketEnabled
override var isJumpBackInCFREnabled: Boolean by updatedFeatureFlags::isJumpBackInCFREnabled
override var isWallpaperOnboardingEnabled: Boolean by updatedFeatureFlags::isWallpaperOnboardingEnabled
override var isRecentTabsFeatureEnabled: Boolean by updatedFeatureFlags::isRecentTabsFeatureEnabled
override var isRecentlyVisitedFeatureEnabled: Boolean by updatedFeatureFlags::isRecentlyVisitedFeatureEnabled
override var isPWAsPromptEnabled: Boolean by updatedFeatureFlags::isPWAsPromptEnabled
override var isTCPCFREnabled: Boolean by updatedFeatureFlags::isTCPCFREnabled
override var etpPolicy: ETPPolicy by updatedFeatureFlags::etpPolicy
override fun applyFlagUpdates() {
applyFeatureFlags(updatedFeatureFlags)
}
override fun resetAllFeatureFlags() {
applyFeatureFlags(initialFeatureFlags)
}
override var isDeleteSitePermissionsEnabled: Boolean by updatedFeatureFlags::isDeleteSitePermissionsEnabled
private fun applyFeatureFlags(featureFlags: FeatureFlags) {
settings.showHomeOnboardingDialog = featureFlags.isHomeOnboardingDialogEnabled
setHomeOnboardingVersion(featureFlags.homeOnboardingDialogVersion)
settings.showPocketRecommendationsFeature = featureFlags.isPocketEnabled
settings.shouldShowJumpBackInCFR = featureFlags.isJumpBackInCFREnabled
settings.showRecentTabsFeature = featureFlags.isRecentTabsFeatureEnabled
settings.historyMetadataUIFeature = featureFlags.isRecentlyVisitedFeatureEnabled
settings.userKnowsAboutPwas = !featureFlags.isPWAsPromptEnabled
settings.shouldShowTotalCookieProtectionCFR = featureFlags.isTCPCFREnabled
settings.showWallpaperOnboarding = featureFlags.isWallpaperOnboardingEnabled
settings.deleteSitePermissions = featureFlags.isDeleteSitePermissionsEnabled
setETPPolicy(featureFlags.etpPolicy)
}
}
private data class FeatureFlags(
var isHomeOnboardingDialogEnabled: Boolean,
var homeOnboardingDialogVersion: Int,
var isPocketEnabled: Boolean,
var isJumpBackInCFREnabled: Boolean,
var isRecentTabsFeatureEnabled: Boolean,
var isRecentlyVisitedFeatureEnabled: Boolean,
var isPWAsPromptEnabled: Boolean,
var isTCPCFREnabled: Boolean,
var isWallpaperOnboardingEnabled: Boolean,
var isDeleteSitePermissionsEnabled: Boolean,
var etpPolicy: ETPPolicy,
)
internal fun getETPPolicy(settings: Settings): ETPPolicy {
return when {
settings.useStrictTrackingProtection -> STRICT
settings.useCustomTrackingProtection -> CUSTOM
else -> STANDARD
}
}
private fun setETPPolicy(policy: ETPPolicy) {
when (policy) {
STRICT -> settings.setStrictETP()
// The following two cases update ETP in the same way "setStrictETP" does.
STANDARD -> {
settings.preferences.edit()
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict_default),
false,
)
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_option),
false,
)
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option),
true,
)
.commit()
}
CUSTOM -> {
settings.preferences.edit()
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict_default),
false,
)
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option),
true,
)
.putBoolean(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_option),
true,
)
.commit()
}
}
}
private fun getHomeOnboardingVersion(): Int {
return FenixOnboarding(appContext)
.preferences
.getInt(FenixOnboarding.LAST_VERSION_ONBOARDING_KEY, 0)
}
private fun setHomeOnboardingVersion(version: Int) {
FenixOnboarding(appContext)
.preferences.edit()
.putInt(FenixOnboarding.LAST_VERSION_ONBOARDING_KEY, version)
.commit()
}

View File

@ -6,12 +6,12 @@
package org.mozilla.fenix.helpers
import android.app.Activity
import android.view.ViewConfiguration.getLongPressTimeout
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiSelector
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.helpers.FeatureSettingsHelper.Companion.settings
import org.mozilla.fenix.helpers.TestHelper.appContext
import org.mozilla.fenix.helpers.TestHelper.mDevice
import org.mozilla.fenix.onboarding.FenixOnboarding
@ -27,34 +27,91 @@ class HomeActivityTestRule(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
private val skipOnboarding: Boolean = false,
) :
ActivityTestRule<HomeActivity>(HomeActivity::class.java, initialTouchMode, launchActivity) {
) : ActivityTestRule<HomeActivity>(HomeActivity::class.java, initialTouchMode, launchActivity),
FeatureSettingsHelper by FeatureSettingsHelperDelegate() {
// Using a secondary constructor allows us to easily delegate the settings to FeatureSettingsHelperDelegate.
// Otherwise if wanting to use the same names we would have to override these settings in the primary
// constructor and in that elide the FeatureSettingsHelperDelegate.
constructor(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
skipOnboarding: Boolean = false,
isHomeOnboardingDialogEnabled: Boolean = settings.showHomeOnboardingDialog &&
FenixOnboarding(appContext).userHasBeenOnboarded(),
isPocketEnabled: Boolean = settings.showPocketRecommendationsFeature,
isJumpBackInCFREnabled: Boolean = settings.shouldShowJumpBackInCFR,
isRecentTabsFeatureEnabled: Boolean = settings.showRecentTabsFeature,
isRecentlyVisitedFeatureEnabled: Boolean = settings.historyMetadataUIFeature,
isPWAsPromptEnabled: Boolean = !settings.userKnowsAboutPwas,
isTCPCFREnabled: Boolean = settings.shouldShowTotalCookieProtectionCFR,
isWallpaperOnboardingEnabled: Boolean = settings.showWallpaperOnboarding,
isDeleteSitePermissionsEnabled: Boolean = settings.deleteSitePermissions,
etpPolicy: ETPPolicy = getETPPolicy(settings),
) : this(initialTouchMode, launchActivity, skipOnboarding) {
this.isHomeOnboardingDialogEnabled = isHomeOnboardingDialogEnabled
this.isPocketEnabled = isPocketEnabled
this.isJumpBackInCFREnabled = isJumpBackInCFREnabled
this.isRecentTabsFeatureEnabled = isRecentTabsFeatureEnabled
this.isRecentlyVisitedFeatureEnabled = isRecentlyVisitedFeatureEnabled
this.isPWAsPromptEnabled = isPWAsPromptEnabled
this.isTCPCFREnabled = isTCPCFREnabled
this.isWallpaperOnboardingEnabled = isWallpaperOnboardingEnabled
this.isDeleteSitePermissionsEnabled = isDeleteSitePermissionsEnabled
this.etpPolicy = etpPolicy
}
/**
* Helper for updating various app settings that could interfere with the tests.
* Tests that use [HomeActivityTestRule] are expected to rely on this [FeatureSettingsHelper]
* instead of instantiating their own.
*
* The main benefit this brings is better ordering of operations with the settings cleanup
* automatically happening just before the [Activity] under test finishes which may as opposed to
* cleanup happening earlier and modifying the app behavior.
* Update settings after the activity was created.
*/
val featureSettingsHelper = FeatureSettingsHelper()
fun applySettingsExceptions(settings: (FeatureSettingsHelper) -> Unit) {
FeatureSettingsHelperDelegate().also {
settings(it)
applyFlagUpdates()
}
}
private val longTapUserPreference = getLongPressTimeout()
override fun beforeActivityLaunched() {
super.beforeActivityLaunched()
setLongTapTimeout(3000)
applyFlagUpdates()
if (skipOnboarding) { skipOnboardingBeforeLaunch() }
}
override fun afterActivityFinished() {
super.afterActivityFinished()
setLongTapTimeout(longTapUserPreference)
featureSettingsHelper.resetAllFeatureFlags()
resetAllFeatureFlags()
closeNotificationShade()
}
companion object {
/**
* Create a new instance of [HomeActivityTestRule] which by default will disable specific
* app features that would otherwise negatively impact most tests.
*
* The disabled features are:
* - the Jump back in CFR,
* - the Total Cookie Protection CFR,
* - the PWA prompt dialog,
* - the wallpaper onboarding.
*/
fun withDefaultSettingsOverrides(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
skipOnboarding: Boolean = false,
) = HomeActivityTestRule(
initialTouchMode = initialTouchMode,
launchActivity = launchActivity,
skipOnboarding = skipOnboarding,
isJumpBackInCFREnabled = false,
isPWAsPromptEnabled = false,
isTCPCFREnabled = false,
isWallpaperOnboardingEnabled = false,
)
}
}
/**
@ -65,17 +122,59 @@ class HomeActivityTestRule(
* @param launchActivity See [IntentsTestRule]
*/
class HomeActivityIntentTestRule(
class HomeActivityIntentTestRule internal constructor(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
private val skipOnboarding: Boolean = false,
) :
IntentsTestRule<HomeActivity>(HomeActivity::class.java, initialTouchMode, launchActivity) {
) : IntentsTestRule<HomeActivity>(HomeActivity::class.java, initialTouchMode, launchActivity),
FeatureSettingsHelper by FeatureSettingsHelperDelegate() {
// Using a secondary constructor allows us to easily delegate the settings to FeatureSettingsHelperDelegate.
// Otherwise if wanting to use the same names we would have to override these settings in the primary
// constructor and in that elide the FeatureSettingsHelperDelegate.
constructor(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
skipOnboarding: Boolean = false,
isHomeOnboardingDialogEnabled: Boolean = settings.showHomeOnboardingDialog &&
FenixOnboarding(appContext).userHasBeenOnboarded(),
isPocketEnabled: Boolean = settings.showPocketRecommendationsFeature,
isJumpBackInCFREnabled: Boolean = settings.shouldShowJumpBackInCFR,
isRecentTabsFeatureEnabled: Boolean = settings.showRecentTabsFeature,
isRecentlyVisitedFeatureEnabled: Boolean = settings.historyMetadataUIFeature,
isPWAsPromptEnabled: Boolean = !settings.userKnowsAboutPwas,
isTCPCFREnabled: Boolean = settings.shouldShowTotalCookieProtectionCFR,
isWallpaperOnboardingEnabled: Boolean = settings.showWallpaperOnboarding,
isDeleteSitePermissionsEnabled: Boolean = settings.deleteSitePermissions,
etpPolicy: ETPPolicy = getETPPolicy(settings),
) : this(initialTouchMode, launchActivity, skipOnboarding) {
this.isHomeOnboardingDialogEnabled = isHomeOnboardingDialogEnabled
this.isPocketEnabled = isPocketEnabled
this.isJumpBackInCFREnabled = isJumpBackInCFREnabled
this.isRecentTabsFeatureEnabled = isRecentTabsFeatureEnabled
this.isRecentlyVisitedFeatureEnabled = isRecentlyVisitedFeatureEnabled
this.isPWAsPromptEnabled = isPWAsPromptEnabled
this.isTCPCFREnabled = isTCPCFREnabled
this.isWallpaperOnboardingEnabled = isWallpaperOnboardingEnabled
this.isDeleteSitePermissionsEnabled = isDeleteSitePermissionsEnabled
this.etpPolicy = etpPolicy
}
private val longTapUserPreference = getLongPressTimeout()
/**
* Update settings after the activity was created.
*/
fun applySettingsExceptions(settings: (FeatureSettingsHelper) -> Unit) {
FeatureSettingsHelperDelegate().apply {
settings(this)
applyFlagUpdates()
}
}
override fun beforeActivityLaunched() {
super.beforeActivityLaunched()
setLongTapTimeout(3000)
applyFlagUpdates()
if (skipOnboarding) { skipOnboardingBeforeLaunch() }
}
@ -83,6 +182,53 @@ class HomeActivityIntentTestRule(
super.afterActivityFinished()
setLongTapTimeout(longTapUserPreference)
closeNotificationShade()
resetAllFeatureFlags()
}
/**
* Update the settings values from when this rule was first instantiated to account for any changes
* done while running the tests.
* Useful in the scenario about the activity being restarted which would otherwise set the initial
* settings and override any changes made in the meantime.
*/
fun updateCachedSettings() {
isHomeOnboardingDialogEnabled =
settings.showHomeOnboardingDialog && FenixOnboarding(appContext).userHasBeenOnboarded()
isPocketEnabled = settings.showPocketRecommendationsFeature
isJumpBackInCFREnabled = settings.shouldShowJumpBackInCFR
isRecentTabsFeatureEnabled = settings.showRecentTabsFeature
isRecentlyVisitedFeatureEnabled = settings.historyMetadataUIFeature
isPWAsPromptEnabled = !settings.userKnowsAboutPwas
isTCPCFREnabled = settings.shouldShowTotalCookieProtectionCFR
isWallpaperOnboardingEnabled = settings.showWallpaperOnboarding
isDeleteSitePermissionsEnabled = settings.deleteSitePermissions
etpPolicy = getETPPolicy(settings)
}
companion object {
/**
* Create a new instance of [HomeActivityIntentTestRule] which by default will disable specific
* app features that would otherwise negatively impact most tests.
*
* The disabled features are:
* - the Jump back in CFR,
* - the Total Cookie Protection CFR,
* - the PWA prompt dialog,
* - the wallpaper onboarding.
*/
fun withDefaultSettingsOverrides(
initialTouchMode: Boolean = false,
launchActivity: Boolean = true,
skipOnboarding: Boolean = false,
) = HomeActivityIntentTestRule(
initialTouchMode = initialTouchMode,
launchActivity = launchActivity,
skipOnboarding = skipOnboarding,
isJumpBackInCFREnabled = false,
isPWAsPromptEnabled = false,
isTCPCFREnabled = false,
isWallpaperOnboardingEnabled = false,
)
}
}

View File

@ -95,6 +95,7 @@ object TestHelper {
fun restartApp(activity: HomeActivityIntentTestRule) {
with(activity) {
updateCachedSettings()
finishActivity()
mDevice.waitForIdle()
launchActivity(null)

View File

@ -33,7 +33,7 @@ import org.mozilla.fenix.helpers.HomeActivityTestRule
*
* Say no to main thread IO! 🙅
*/
private const val EXPECTED_SUPPRESSION_COUNT = 17
private const val EXPECTED_SUPPRESSION_COUNT = 19
/**
* The number of times we call the `runBlocking` coroutine method on the main thread during this