@ -7,6 +7,7 @@ package org.mozilla.fenix.ext
import io.mockk.mockk
import mozilla.components.service.pocket.PocketStory.PocketRecommendedStory
import mozilla.components.service.pocket.PocketStory.PocketSponsoredStory
import mozilla.components.service.pocket.PocketStory.PocketSponsoredStoryCaps
import mozilla.components.service.pocket.PocketStory.PocketSponsoredStoryShim
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
@ -18,6 +19,7 @@ import org.mozilla.fenix.home.pocket.POCKET_STORIES_DEFAULT_CATEGORY_NAME
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesSelectedCategory
import org.mozilla.fenix.home.recenttabs.RecentTab
import java.util.concurrent.TimeUnit
import kotlin.random.Random
class AppStateTest {
@ -96,7 +98,7 @@ class AppStateTest {
POCKET _STORIES _DEFAULT _CATEGORY _NAME ,
getFakePocketStories ( POCKET _STORIES _TO _SHOW _COUNT )
)
val sponsoredStories = getFakeSponsoredStories ( 2 )
val sponsoredStories = getFakeSponsoredStories ( 4 )
val state = AppState (
pocketStoriesCategories = listOf (
otherStoriesCategory , anotherStoriesCategory , defaultStoriesCategoryWithManyStories
@ -108,9 +110,8 @@ class AppStateTest {
assertEquals ( POCKET _STORIES _TO _SHOW _COUNT , result . size )
// second story should be a sponsored one
assertEquals ( sponsoredStories [ 0 ] , result [ 1 ] )
// last story should be a sponsored one
assertEquals ( sponsoredStories [ 1 ] , result [ POCKET _STORIES _TO _SHOW _COUNT - 1 ] )
assertEquals ( sponsoredStories [ 1 ] , result [ 1 ] )
assertEquals ( sponsoredStories [ 3 ] , result [ POCKET _STORIES _TO _SHOW _COUNT - 1 ] )
// remove the sponsored stories to hopefully only remain with general recommendations
result . removeAt ( 7 )
result . removeAt ( 1 )
@ -121,6 +122,91 @@ class AppStateTest {
)
}
@Test
fun `GIVEN a list of sponsored stories WHEN filtering them THEN have them ordered by priority` ( ) {
val stories = getFakeSponsoredStories ( 4 ) . mapIndexed { index , story ->
story . copy ( priority = index )
}
val result = getFilteredSponsoredStories ( stories , 10 )
assertEquals ( 4 , result . size )
assertEquals ( stories . reversed ( ) , result )
}
@Test
fun `GIVEN a list of sponsored stories WHEN filtering them THEN drop the ones already shown for the maximum number of times in lifetime` ( ) {
val stories = getFakeSponsoredStories ( 4 ) . mapIndexed { index , story ->
when ( index % 2 == 0 ) {
true -> story . copy (
caps = story . caps . copy (
currentImpressions = listOf ( 1 , 2 , 3 ) ,
lifetimeCount = 3
)
)
false -> story
}
}
val result = getFilteredSponsoredStories ( stories , 10 )
assertEquals ( 2 , result . size )
assertEquals ( stories [ 1 ] , result [ 0 ] )
assertEquals ( stories [ 3 ] , result [ 1 ] )
}
@Test
fun `GIVEN a list of sponsored stories WHEN filtering them THEN drop the ones already shown for the maximum number of times in flight` ( ) {
val stories = getFakeSponsoredStories ( 4 ) . mapIndexed { index , story ->
when ( index % 2 == 0 ) {
true -> story
false -> story . copy (
caps = story . caps . copy (
currentImpressions = listOf (
TimeUnit . MILLISECONDS . toSeconds ( System . currentTimeMillis ( ) ) ,
TimeUnit . MILLISECONDS . toSeconds ( System . currentTimeMillis ( ) ) ,
TimeUnit . MILLISECONDS . toSeconds ( System . currentTimeMillis ( ) )
) ,
flightCount = 3
)
)
}
}
val result = getFilteredSponsoredStories ( stories , 10 )
assertEquals ( 2 , result . size )
assertEquals ( stories [ 0 ] , result [ 0 ] )
assertEquals ( stories [ 2 ] , result [ 1 ] )
}
@Test
fun `GIVEN a list of sponsored stories WHEN filtering them THEN return up to limit of stories asked` ( ) {
val stories = getFakeSponsoredStories ( 4 )
val result = getFilteredSponsoredStories ( stories , 2 )
assertEquals ( 2 , result . size )
}
@Test
fun `GIVEN multiple stories of both types WHEN combining them THEN show sponsored stories at positionn 2 and 8` ( ) {
val recommendedStories = getFakePocketStories ( POCKET _STORIES _TO _SHOW _COUNT , " other " )
val sponsoredStories = getFakeSponsoredStories ( 4 )
val result = combineRecommendedAndSponsoredStories ( recommendedStories , sponsoredStories )
assertEquals ( POCKET _STORIES _TO _SHOW _COUNT , result . size )
assertEquals ( recommendedStories [ 0 ] , result [ 0 ] )
assertEquals ( sponsoredStories [ 0 ] , result [ 1 ] )
assertEquals ( recommendedStories [ 1 ] , result [ 2 ] )
assertEquals ( recommendedStories [ 2 ] , result [ 3 ] )
assertEquals ( recommendedStories [ 3 ] , result [ 4 ] )
assertEquals ( recommendedStories [ 4 ] , result [ 5 ] )
assertEquals ( recommendedStories [ 5 ] , result [ 6 ] )
assertEquals ( sponsoredStories [ 1 ] , result [ POCKET _STORIES _TO _SHOW _COUNT - 1 ] )
}
@Test
fun `GIVEN a category is selected WHEN getFilteredStories is called THEN only stories from that category are returned` ( ) {
val state = AppState (
@ -384,6 +470,7 @@ private fun getFakeSponsoredStories(limit: Int) = mutableListOf<PocketSponsoredS
for ( index in 0 until limit ) {
add (
PocketSponsoredStory (
id = index ,
title = " Story title $index " ,
url = " https://sponsored.story " ,
imageUrl = " https://sponsored.image " ,
@ -391,7 +478,13 @@ private fun getFakeSponsoredStories(limit: Int) = mutableListOf<PocketSponsoredS
shim = PocketSponsoredStoryShim (
click = " Story title $index click shim " ,
impression = " Story title $index impression shim "
)
) ,
priority = 2 + index % 2 ,
caps = PocketSponsoredStoryCaps (
lifetimeCount = 1 + index * 5 ,
flightCount = 1 + index * 2 ,
flightPeriod = 1 + index * 3 ,
) ,
)
)
}