For #25381 - Add a setting allowing to toggle Pocket sponsored stories

This commit is contained in:
Mugurell 2022-05-25 12:50:38 +03:00 committed by mergify[bot]
parent 13c34f16dd
commit df5786f1e3
7 changed files with 213 additions and 10 deletions

View File

@ -150,9 +150,15 @@ internal object AppStoreReducer {
pocketStories = emptyList(),
pocketSponsoredStories = emptyList()
)
is AppAction.PocketSponsoredStoriesChange -> state.copy(
pocketSponsoredStories = action.sponsoredStories
)
is AppAction.PocketSponsoredStoriesChange -> {
val updatedStoriesState = state.copy(
pocketSponsoredStories = action.sponsoredStories,
)
updatedStoriesState.copy(
pocketStories = updatedStoriesState.getFilteredStories()
)
}
is AppAction.PocketStoriesShown -> {
var updatedCategories = state.pocketStoriesCategories
action.storiesShown.filterIsInstance<PocketRecommendedStory>().forEach { shownStory ->

View File

@ -13,6 +13,8 @@ import androidx.preference.SwitchPreference
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.GleanMetrics.CustomizeHome
import org.mozilla.fenix.R
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.utils.view.addToRadioGroup
@ -116,6 +118,28 @@ class HomeSettingsFragment : PreferenceFragmentCompat() {
}
}
requirePreference<CheckBoxPreference>(R.string.pref_key_pocket_sponsored_stories).apply {
isVisible = FeatureFlags.isPocketSponsoredStoriesFeatureEnabled(context)
isChecked = context.settings().showPocketSponsoredStories
onPreferenceChangeListener = object : SharedPreferenceUpdater() {
override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
when (newValue) {
true -> {
context.components.core.pocketStoriesService.startPeriodicSponsoredStoriesRefresh()
}
false -> {
context.components.core.pocketStoriesService.deleteProfile()
context.components.appStore.dispatch(
AppAction.PocketSponsoredStoriesChange(emptyList())
)
}
}
return super.onPreferenceChange(preference, newValue)
}
}
}
requirePreference<SwitchPreference>(R.string.pref_key_history_metadata_feature).apply {
isVisible = FeatureFlags.historyMetadataUIFeature
isChecked = context.settings().historyMetadataUIFeature

View File

@ -1269,6 +1269,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = true
)
/**
* Indicates if the Pocket recommended stories homescreen section should be shown.
*/
var showPocketRecommendationsFeature by lazyFeatureFlagPreference(
appContext.getPreferenceKey(R.string.pref_key_pocket_homescreen_recommendations),
featureFlag = FeatureFlags.isPocketRecommendationsFeatureEnabled(appContext),

View File

@ -409,6 +409,8 @@
<string name="customize_toggle_recently_visited">Recently visited</string>
<!-- Title for the customize home screen section with Pocket. -->
<string name="customize_toggle_pocket">Pocket</string>
<!-- Title for the customize home screen section with sponsored Pocket stories. -->
<string name="customize_toggle_pocket_sponsored">Sponsored stories</string>
<!-- Title for the opening wallpaper settings screen -->
<string name="customize_wallpapers">Wallpapers</string>
<!-- Title for the customize home screen section with sponsored shortcuts. -->

View File

@ -35,6 +35,12 @@
android:title="@string/customize_toggle_pocket"
app:isPreferenceVisible="false" />
<androidx.preference.CheckBoxPreference
android:dependency="@string/pref_key_pocket_homescreen_recommendations"
android:layout="@layout/checkbox_left_sub_preference"
android:key="@string/pref_key_pocket_sponsored_stories"
android:title="@string/customize_toggle_pocket_sponsored" />
<androidx.preference.Preference
android:key="@string/pref_key_wallpapers"
android:title="@string/customize_wallpapers" />

View File

@ -15,6 +15,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.pocket.PocketStory
import mozilla.components.service.pocket.PocketStory.PocketRecommendedStory
import mozilla.components.service.pocket.PocketStory.PocketSponsoredStory
import mozilla.components.service.pocket.PocketStory.PocketSponsoredStoryCaps
import org.junit.Assert.assertEquals
@ -364,7 +365,7 @@ class AppStoreTest {
}
@Test
fun `Test updating the list of Pocket sponsored stories`() = runTest {
fun `Test updating the list of Pocket sponsored stories also updates the list of stories to show`() = runTest {
val story1 = PocketSponsoredStory(
id = 3,
title = "title",
@ -379,13 +380,20 @@ class AppStoreTest {
appStore = AppStore(AppState())
appStore.dispatch(AppAction.PocketSponsoredStoriesChange(listOf(story1, story2)))
.join()
assertTrue(appStore.state.pocketSponsoredStories.containsAll(listOf(story1, story2)))
mockkStatic("org.mozilla.fenix.ext.AppStateKt") {
val firstFilteredStories = listOf(mockk<PocketSponsoredStory>())
every { any<AppState>().getFilteredStories() } returns firstFilteredStories
appStore.dispatch(AppAction.PocketSponsoredStoriesChange(listOf(story1, story2))).join()
assertTrue(appStore.state.pocketSponsoredStories.containsAll(listOf(story1, story2)))
assertEquals(firstFilteredStories, appStore.state.pocketStories)
val updatedStories = listOf(story2.copy(title = "title3"))
appStore.dispatch(AppAction.PocketSponsoredStoriesChange(updatedStories)).join()
assertTrue(updatedStories.containsAll(appStore.state.pocketSponsoredStories))
val secondFilteredStories = firstFilteredStories + mockk<PocketRecommendedStory>()
every { any<AppState>().getFilteredStories() } returns secondFilteredStories
val updatedStories = listOf(story2.copy(title = "title3"))
appStore.dispatch(AppAction.PocketSponsoredStoriesChange(updatedStories)).join()
assertTrue(updatedStories.containsAll(appStore.state.pocketSponsoredStories))
assertEquals(secondFilteredStories, appStore.state.pocketStories)
}
}
@Test

View File

@ -0,0 +1,154 @@
/* 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.settings
import android.content.Context
import android.content.SharedPreferences
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.preference.CheckBoxPreference
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import io.mockk.verify
import mozilla.components.service.pocket.PocketStoriesService
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.Settings
import org.robolectric.Robolectric
@RunWith(FenixRobolectricTestRunner::class)
internal class HomeSettingsFragmentTest {
private lateinit var homeSettingsFragment: HomeSettingsFragment
private lateinit var sponsoredStoriesSetting: CheckBoxPreference
private lateinit var appSettings: Settings
private lateinit var appPrefs: SharedPreferences
private lateinit var appPrefsEditor: SharedPreferences.Editor
private lateinit var pocketService: PocketStoriesService
private lateinit var store: AppStore
@Before
fun setup() {
mockkStatic("org.mozilla.fenix.ext.FragmentKt")
every { any<Fragment>().showToolbar(any()) } just Runs
mockkStatic("org.mozilla.fenix.ext.ContextKt")
appPrefsEditor = mockk(relaxed = true)
appPrefs = mockk(relaxed = true) {
every { edit() } returns appPrefsEditor
}
appSettings = mockk(relaxed = true) {
every { preferences } returns appPrefs
}
every { any<Context>().settings() } returns appSettings
store = mockk(relaxed = true)
pocketService = mockk(relaxed = true)
every { any<Context>().components } returns mockk {
every { appStore } returns store
every { core.pocketStoriesService } returns pocketService
}
homeSettingsFragment = HomeSettingsFragment()
val activity = Robolectric.buildActivity(FragmentActivity::class.java).create().get()
activity.supportFragmentManager.beginTransaction()
.add(homeSettingsFragment, "HomeSettingFragmentTest")
.commitNow()
sponsoredStoriesSetting = homeSettingsFragment.findPreference(
homeSettingsFragment.getPreferenceKey(R.string.pref_key_pocket_sponsored_stories)
)!!
}
@After
fun teardown() {
unmockkStatic("org.mozilla.fenix.ext.ContextKt")
unmockkStatic("org.mozilla.fenix.ext.FragmentKt")
}
@Test
fun `GIVEN the Pocket sponsored stories feature is disabled for the app WHEN accessing settings THEN the settings for it are not visible`() {
every { any<Context>().settings() } returns mockk(relaxed = true)
mockkObject(FeatureFlags) {
every { FeatureFlags.isPocketSponsoredStoriesFeatureEnabled(any()) } returns false
homeSettingsFragment.onResume()
assertFalse(sponsoredStoriesSetting.isVisible)
}
}
@Test
fun `GIVEN the Pocket sponsored stories feature is enabled for the app WHEN accessing settings THEN the settings for it are visible`() {
every { any<Context>().settings() } returns mockk(relaxed = true)
mockkObject(FeatureFlags) {
every { FeatureFlags.isPocketSponsoredStoriesFeatureEnabled(any()) } returns true
homeSettingsFragment.onResume()
assertTrue(sponsoredStoriesSetting.isVisible)
}
}
@Test
fun `GIVEN the Pocket sponsored stories preference is false WHEN accessing settings THEN the setting for it is unchecked`() {
every { appSettings.showPocketSponsoredStories } returns false
homeSettingsFragment.onResume()
assertFalse(sponsoredStoriesSetting.isChecked)
}
@Test
fun `GIVEN the Pocket sponsored stories preference is true WHEN accessing settings THEN the setting for it is checked`() {
every { appSettings.showPocketSponsoredStories } returns true
homeSettingsFragment.onResume()
assertTrue(sponsoredStoriesSetting.isChecked)
}
@Test
fun `GIVEN the setting for Pocket sponsored stories is unchecked WHEN tapping it THEN toggle it and start downloading stories`() {
homeSettingsFragment.onResume()
val result = sponsoredStoriesSetting.callChangeListener(true)
assertTrue(result)
verify { appPrefsEditor.putBoolean(testContext.getString(R.string.pref_key_pocket_sponsored_stories), true) }
verify { pocketService.startPeriodicSponsoredStoriesRefresh() }
}
@Test
fun `GIVEN the setting for Pocket sponsored stories is checked WHEN tapping it THEN toggle it, delete Pocket profile and remove sponsored stories from showing`() {
homeSettingsFragment.onResume()
val result = sponsoredStoriesSetting.callChangeListener(false)
assertTrue(result)
verify { appPrefsEditor.putBoolean(testContext.getString(R.string.pref_key_pocket_sponsored_stories), false) }
verify { pocketService.deleteProfile() }
verify { store.dispatch(AppAction.PocketSponsoredStoriesChange(emptyList())) }
}
}