diff --git a/app/src/main/java/org/mozilla/fenix/ext/AppState.kt b/app/src/main/java/org/mozilla/fenix/ext/AppState.kt index 1c1805eb3..aae24096a 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/AppState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/AppState.kt @@ -37,41 +37,45 @@ internal const val POCKET_SPONSORED_STORIES_TO_SHOW_COUNT = 2 * @return a list of [PocketStory]es from the currently selected categories. */ fun AppState.getFilteredStories(): List { - if (pocketStoriesCategoriesSelections.isEmpty()) { - val recommendedStories = pocketStoriesCategories - .find { - it.name == POCKET_STORIES_DEFAULT_CATEGORY_NAME - }?.stories - ?.sortedBy { it.timesShown } - ?.take(POCKET_STORIES_TO_SHOW_COUNT) ?: emptyList() - - val sponsoredStories = getFilteredSponsoredStories( - stories = pocketSponsoredStories, - limit = POCKET_SPONSORED_STORIES_TO_SHOW_COUNT, - ) + val recommendedStories = when (pocketStoriesCategoriesSelections.isEmpty()) { + true -> { + pocketStoriesCategories + .find { it.name == POCKET_STORIES_DEFAULT_CATEGORY_NAME } + ?.stories + ?.sortedBy { it.timesShown } + ?.take(POCKET_STORIES_TO_SHOW_COUNT) ?: emptyList() + } + false -> { + val oldestSortedCategories = pocketStoriesCategoriesSelections + .sortedByDescending { it.selectionTimestamp } + .mapNotNull { selectedCategory -> + pocketStoriesCategories.find { + it.name == selectedCategory.name + } + } - return combineRecommendedAndSponsoredStories( - recommendedStories = recommendedStories, - sponsoredStories = sponsoredStories - ) - } + val filteredStoriesCount = getFilteredStoriesCount( + oldestSortedCategories, POCKET_STORIES_TO_SHOW_COUNT + ) - val oldestSortedCategories = pocketStoriesCategoriesSelections - .sortedByDescending { it.selectionTimestamp } - .mapNotNull { selectedCategory -> - pocketStoriesCategories.find { - it.name == selectedCategory.name - } + oldestSortedCategories + .flatMap { category -> + category.stories + .sortedBy { it.timesShown } + .take(filteredStoriesCount[category.name]!!) + }.take(POCKET_STORIES_TO_SHOW_COUNT) } + } - val filteredStoriesCount = getFilteredStoriesCount( - oldestSortedCategories, POCKET_STORIES_TO_SHOW_COUNT + val sponsoredStories = getFilteredSponsoredStories( + stories = pocketSponsoredStories, + limit = POCKET_SPONSORED_STORIES_TO_SHOW_COUNT, ) - return oldestSortedCategories - .flatMap { category -> - category.stories.sortedBy { it.timesShown }.take(filteredStoriesCount[category.name]!!) - }.take(POCKET_STORIES_TO_SHOW_COUNT) + return combineRecommendedAndSponsoredStories( + recommendedStories = recommendedStories, + sponsoredStories = sponsoredStories + ) } /** diff --git a/app/src/test/java/org/mozilla/fenix/ext/AppStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/AppStateTest.kt index 59cbf6d56..789fdee44 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/AppStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/AppStateTest.kt @@ -208,14 +208,20 @@ class AppStateTest { } @Test - fun `GIVEN a category is selected WHEN getFilteredStories is called THEN only stories from that category are returned`() { + fun `GIVEN a category is selected and 1 sponsored story is available WHEN getFilteredStories is called THEN only stories from that category and the sponsored story are returned`() { + val sponsoredStories = getFakeSponsoredStories(1) val state = AppState( pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory, defaultStoriesCategory), - pocketStoriesCategoriesSelections = listOf(PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name)) + pocketStoriesCategoriesSelections = listOf(PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name)), + pocketSponsoredStories = sponsoredStories ) - val result = state.getFilteredStories() + val result = state.getFilteredStories().toMutableList() + assertEquals(4, result.size) + assertEquals(sponsoredStories[0], result[1]) // second story should be a sponsored one + // remove the sponsored story to hopefully only remain with stories from the selected category + result.removeAt(1) assertNull( result.firstOrNull { it is PocketRecommendedStory && it.category != otherStoriesCategory.name @@ -223,6 +229,70 @@ class AppStateTest { ) } + @Test + fun `GIVEN two categories selected and 1 sponsored story available WHEN getFilteredStories is called THEN only stories from the selected categories and the sponsored story are returned`() { + val sponsoredStories = getFakeSponsoredStories(1) + val yetAnotherStoriesCategory = + PocketRecommendedStoriesCategory("yetAnother", getFakePocketStories(3, "yetAnother")) + val state = AppState( + pocketStoriesCategories = listOf( + otherStoriesCategory, anotherStoriesCategory, yetAnotherStoriesCategory, defaultStoriesCategory, + ), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name), + PocketRecommendedStoriesSelectedCategory(anotherStoriesCategory.name) + ), + pocketSponsoredStories = sponsoredStories + ) + + val result = state.getFilteredStories().toMutableList() + + // Only 7 stories available: 3*2 stories from the selected categories plus one sponsored story + assertEquals(7, result.size) + assertEquals(sponsoredStories[0], result[1]) // second story should be a sponsored one + // remove the sponsored story to hopefully only remain with stories from the selected category + result.removeAt(1) + assertNull( + result.firstOrNull { + (it !is PocketRecommendedStory) || + (it.category != otherStoriesCategory.name && it.category != anotherStoriesCategory.name) + } + ) + } + + @Test + fun `GIVEN two categories selected and 2 sponsored stories available WHEN getFilteredStories is called THEN no more than the default stories number are returned`() { + val sponsoredStories = getFakeSponsoredStories(4) + val yetAnotherStoriesCategory = + PocketRecommendedStoriesCategory("yetAnother", getFakePocketStories(10, "yetAnother")) + val state = AppState( + pocketStoriesCategories = listOf( + otherStoriesCategory, anotherStoriesCategory, yetAnotherStoriesCategory, defaultStoriesCategory, + ), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name), + PocketRecommendedStoriesSelectedCategory(yetAnotherStoriesCategory.name) + ), + pocketSponsoredStories = sponsoredStories + ) + + val result = state.getFilteredStories().toMutableList() + + assertEquals(POCKET_STORIES_TO_SHOW_COUNT, result.size) + // second and penultimate story should be sponsored stories + assertEquals(sponsoredStories[1], result[1]) + assertEquals(sponsoredStories[3], result[POCKET_STORIES_TO_SHOW_COUNT - 1]) + // remove the sponsored stories to hopefully only remain with stories from the selected categories + result.removeAt(7) + result.removeAt(1) + assertNull( + result.firstOrNull { + (it !is PocketRecommendedStory) || + (it.category != otherStoriesCategory.name && it.category != yetAnotherStoriesCategory.name) + } + ) + } + @Test fun `GIVEN a category is selected WHEN getFilteredStories is called THEN no more than the default stories number are returned from the selected category`() { val otherStoriesCategoryWithManyStories =