Bug 1880476 — Messaging: promote NimbusMessagingController to components.nimbus.messaging

fenix/125.0
James Hugman 5 months ago committed by mergify[bot]
parent f106a41c3d
commit b5c5527556

@ -33,8 +33,8 @@ class NimbusMessagingMessageTest {
private lateinit var context: Context private lateinit var context: Context
private val storage private val messaging
get() = context.components.nimbus.messagingStorage get() = context.components.nimbus.messaging
@get:Rule @get:Rule
val activityTestRule = val activityTestRule =
@ -54,7 +54,7 @@ class NimbusMessagingMessageTest {
*/ */
@Test @Test
fun testAllMessageIntegrity() = runTest { fun testAllMessageIntegrity() = runTest {
val messages = storage.getMessages() val messages = messaging.getMessages()
val rawMessages = feature.messages val rawMessages = feature.messages
assertTrue(rawMessages.isNotEmpty()) assertTrue(rawMessages.isNotEmpty())
@ -67,22 +67,6 @@ class NimbusMessagingMessageTest {
assertEquals(messages.size, rawMessages.size) assertEquals(messages.size, rawMessages.size)
} }
/**
* Check if the messages' triggers are well formed JEXL.
*/
@Test
fun testAllMessageTriggers() = runTest {
val helper = context.components.nimbus.createJexlHelper()
val messages = storage.getMessages()
messages.forEach { message ->
storage.isMessageEligible(message, helper)
if (storage.malFormedMap.isNotEmpty()) {
fail("${message.id} has a problem with its JEXL trigger: ${storage.malFormedMap.keys}")
}
}
helper.destroy()
}
private fun checkIsLocalized(string: String) { private fun checkIsLocalized(string: String) {
assertFalse(string.isBlank()) assertFalse(string.isBlank())
// The check will almost always succeed, since the generated code // The check will almost always succeed, since the generated code

@ -121,7 +121,6 @@ import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor
import org.mozilla.fenix.home.intent.StartSearchIntentProcessor import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
import org.mozilla.fenix.library.bookmarks.DesktopFolders import org.mozilla.fenix.library.bookmarks.DesktopFolders
import org.mozilla.fenix.messaging.FenixMessageSurfaceId import org.mozilla.fenix.messaging.FenixMessageSurfaceId
import org.mozilla.fenix.messaging.FenixNimbusMessagingController
import org.mozilla.fenix.messaging.MessageNotificationWorker import org.mozilla.fenix.messaging.MessageNotificationWorker
import org.mozilla.fenix.nimbus.FxNimbus import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker import org.mozilla.fenix.onboarding.ReEngagementNotificationWorker
@ -1221,13 +1220,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
} }
private suspend fun showFullscreenMessageIfNeeded(context: Context) { private suspend fun showFullscreenMessageIfNeeded(context: Context) {
val messagingStorage = context.components.nimbus.messagingStorage val messaging = context.components.nimbus.messaging
val messages = messagingStorage.getMessages() val nextMessage = messaging.getNextMessage(FenixMessageSurfaceId.SURVEY) ?: return
val nextMessage =
messagingStorage.getNextMessage(FenixMessageSurfaceId.SURVEY, messages)
?: return
val fenixNimbusMessagingController = FenixNimbusMessagingController(messagingStorage)
val researchSurfaceDialogFragment = ResearchSurfaceDialogFragment.newInstance( val researchSurfaceDialogFragment = ResearchSurfaceDialogFragment.newInstance(
keyMessageText = nextMessage.text, keyMessageText = nextMessage.text,
keyAcceptButtonText = nextMessage.buttonLabel, keyAcceptButtonText = nextMessage.buttonLabel,
@ -1235,7 +1229,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
) )
researchSurfaceDialogFragment.onAccept = { researchSurfaceDialogFragment.onAccept = {
processIntent(fenixNimbusMessagingController.getIntentForMessage(nextMessage)) processIntent(messaging.getIntentForMessage(nextMessage))
components.appStore.dispatch(AppAction.MessagingAction.MessageClicked(nextMessage)) components.appStore.dispatch(AppAction.MessagingAction.MessageClicked(nextMessage))
} }
@ -1252,15 +1246,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// Update message as displayed. // Update message as displayed.
val currentBootUniqueIdentifier = BootUtils.getBootIdentifier(context) val currentBootUniqueIdentifier = BootUtils.getBootIdentifier(context)
val updatedMessage =
fenixNimbusMessagingController.updateMessageAsDisplayed(
nextMessage,
currentBootUniqueIdentifier,
)
fenixNimbusMessagingController.onMessageDisplayed(updatedMessage)
return messaging.onMessageDisplayed(nextMessage, currentBootUniqueIdentifier)
} }
companion object { companion object {

@ -232,7 +232,7 @@ class Components(private val context: Context) {
context.pocketStoriesSelectedCategoriesDataStore, context.pocketStoriesSelectedCategoriesDataStore,
), ),
MessagingMiddleware( MessagingMiddleware(
messagingStorage = nimbus.messagingStorage, controller = nimbus.messaging,
), ),
MetricsMiddleware(metrics = analytics.metrics), MetricsMiddleware(metrics = analytics.metrics),
), ),

@ -7,6 +7,8 @@ package org.mozilla.fenix.components
import android.content.Context import android.content.Context
import mozilla.components.service.nimbus.NimbusApi import mozilla.components.service.nimbus.NimbusApi
import mozilla.components.service.nimbus.messaging.FxNimbusMessaging import mozilla.components.service.nimbus.messaging.FxNimbusMessaging
import mozilla.components.service.nimbus.messaging.NimbusMessagingController
import mozilla.components.service.nimbus.messaging.NimbusMessagingControllerInterface
import mozilla.components.service.nimbus.messaging.NimbusMessagingStorage import mozilla.components.service.nimbus.messaging.NimbusMessagingStorage
import mozilla.components.service.nimbus.messaging.OnDiskMessageMetadataStorage import mozilla.components.service.nimbus.messaging.OnDiskMessageMetadataStorage
import org.mozilla.experiments.nimbus.NimbusEventStore import org.mozilla.experiments.nimbus.NimbusEventStore
@ -71,12 +73,23 @@ class NimbusComponents(private val context: Context) {
fun createJexlHelper(): NimbusMessagingHelperInterface = fun createJexlHelper(): NimbusMessagingHelperInterface =
messagingStorage.createMessagingHelper() messagingStorage.createMessagingHelper()
/**
* The main entry point for UI surfaces to interact with (get, click, dismiss) messages
* from the Nimbus Messaging component.
*/
val messaging: NimbusMessagingControllerInterface by lazyMonitored {
NimbusMessagingController(
messagingStorage = messagingStorage,
deepLinkScheme = BuildConfig.DEEP_LINK_SCHEME,
)
}
/** /**
* Low level access to the messaging component. * Low level access to the messaging component.
* *
* The app should access this through a [mozilla.components.service.nimbus.messaging.NimbusMessagingController]. * The app should access this through a [mozilla.components.service.nimbus.messaging.NimbusMessagingController].
*/ */
val messagingStorage by lazyMonitored { private val messagingStorage by lazyMonitored {
NimbusMessagingStorage( NimbusMessagingStorage(
context = context, context = context,
metadataStorage = OnDiskMessageMetadataStorage(context), metadataStorage = OnDiskMessageMetadataStorage(context),

@ -117,7 +117,6 @@ import org.mozilla.fenix.home.toolbar.SearchSelectorBinding
import org.mozilla.fenix.home.toolbar.SearchSelectorMenuBinding import org.mozilla.fenix.home.toolbar.SearchSelectorMenuBinding
import org.mozilla.fenix.home.topsites.DefaultTopSitesView import org.mozilla.fenix.home.topsites.DefaultTopSitesView
import org.mozilla.fenix.messaging.DefaultMessageController import org.mozilla.fenix.messaging.DefaultMessageController
import org.mozilla.fenix.messaging.FenixNimbusMessagingController
import org.mozilla.fenix.messaging.MessagingFeature import org.mozilla.fenix.messaging.MessagingFeature
import org.mozilla.fenix.nimbus.FxNimbus import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.perf.MarkersFragmentLifecycleCallbacks import org.mozilla.fenix.perf.MarkersFragmentLifecycleCallbacks
@ -360,7 +359,7 @@ class HomeFragment : Fragment() {
engine = components.core.engine, engine = components.core.engine,
messageController = DefaultMessageController( messageController = DefaultMessageController(
appStore = components.appStore, appStore = components.appStore,
messagingController = FenixNimbusMessagingController(components.nimbus.messagingStorage), messagingController = components.nimbus.messaging,
homeActivity = activity, homeActivity = activity,
), ),
store = store, store = store,

@ -4,9 +4,8 @@
package org.mozilla.fenix.messaging package org.mozilla.fenix.messaging
import android.content.Intent
import mozilla.components.service.nimbus.messaging.Message import mozilla.components.service.nimbus.messaging.Message
import mozilla.components.service.nimbus.messaging.NimbusMessagingController import mozilla.components.service.nimbus.messaging.NimbusMessagingControllerInterface
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.components.AppStore import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.MessageClicked import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.MessageClicked
@ -17,13 +16,13 @@ import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.MessageDi
*/ */
class DefaultMessageController( class DefaultMessageController(
private val appStore: AppStore, private val appStore: AppStore,
private val messagingController: NimbusMessagingController, private val messagingController: NimbusMessagingControllerInterface,
private val homeActivity: HomeActivity, private val homeActivity: HomeActivity,
) : MessageController { ) : MessageController {
override fun onMessagePressed(message: Message) { override fun onMessagePressed(message: Message) {
val actionUri = messagingController.processMessageActionToUri(message) val intent = messagingController.getIntentForMessage(message)
homeActivity.processIntent(Intent(Intent.ACTION_VIEW, actionUri)) homeActivity.processIntent(intent)
appStore.dispatch(MessageClicked(message)) appStore.dispatch(MessageClicked(message))
} }

@ -1,23 +0,0 @@
/* 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.messaging
import mozilla.components.service.nimbus.messaging.NimbusMessagingController
import mozilla.components.service.nimbus.messaging.NimbusMessagingStorage
import org.mozilla.fenix.BuildConfig
/**
* Bookkeeping for message actions in terms of Glean messages and the messaging store.
* Specialized implementation of NimbusMessagingController defining the deepLinkScheme
* used by Fenix
*/
class FenixNimbusMessagingController(
messagingStorage: NimbusMessagingStorage,
now: () -> Long = { System.currentTimeMillis() },
) : NimbusMessagingController(
messagingStorage = messagingStorage,
deepLinkScheme = BuildConfig.DEEP_LINK_SCHEME,
now = now,
)

@ -45,10 +45,10 @@ class MessageNotificationWorker(
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
val context = applicationContext val context = applicationContext
val messagingStorage = context.components.nimbus.messagingStorage val messaging = context.components.nimbus.messaging
val messages = messagingStorage.getMessages()
val nextMessage = val nextMessage =
messagingStorage.getNextMessage(FenixMessageSurfaceId.NOTIFICATION, messages) messaging.getNextMessage(FenixMessageSurfaceId.NOTIFICATION)
?: return Result.success() ?: return Result.success()
val currentBootUniqueIdentifier = BootUtils.getBootIdentifier(context) val currentBootUniqueIdentifier = BootUtils.getBootIdentifier(context)
@ -57,23 +57,15 @@ class MessageNotificationWorker(
return Result.success() return Result.success()
} }
val nimbusMessagingController = FenixNimbusMessagingController(messagingStorage)
// Update message as displayed. // Update message as displayed.
val updatedMessage = messaging.onMessageDisplayed(nextMessage, currentBootUniqueIdentifier)
nimbusMessagingController.updateMessageAsDisplayed(
nextMessage,
currentBootUniqueIdentifier,
)
nimbusMessagingController.onMessageDisplayed(updatedMessage)
context.components.notificationsDelegate.notify( context.components.notificationsDelegate.notify(
MESSAGE_TAG, MESSAGE_TAG,
SharedIdsHelper.getIdForTag(context, updatedMessage.id), SharedIdsHelper.getIdForTag(context, nextMessage.id),
buildNotification( buildNotification(
context, context,
updatedMessage, nextMessage,
), ),
) )
@ -177,18 +169,17 @@ class NotificationDismissedService : LifecycleService() {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
if (intent != null) { if (intent != null) {
val nimbusMessagingController = val messaging = applicationContext.components.nimbus.messaging
FenixNimbusMessagingController(applicationContext.components.nimbus.messagingStorage)
lifecycleScope.launch { lifecycleScope.launch {
// Get the relevant message. // Get the relevant message.
val message = intent.getStringExtra(DISMISSED_MESSAGE_ID)?.let { messageId -> val message = intent.getStringExtra(DISMISSED_MESSAGE_ID)?.let { messageId ->
nimbusMessagingController.getMessage(messageId) messaging.getMessage(messageId)
} }
if (message != null) { if (message != null) {
// Update message as 'dismissed'. // Update message as 'dismissed'.
nimbusMessagingController.onMessageDismissed(message) messaging.onMessageDismissed(message)
} }
} }
} }
@ -208,21 +199,20 @@ class NotificationClickedReceiverActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val nimbusMessagingController = val messaging = applicationContext.components.nimbus.messaging
FenixNimbusMessagingController(components.nimbus.messagingStorage)
lifecycleScope.launch { lifecycleScope.launch {
// Get the relevant message. // Get the relevant message.
val message = intent.getStringExtra(CLICKED_MESSAGE_ID)?.let { messageId -> val message = intent.getStringExtra(CLICKED_MESSAGE_ID)?.let { messageId ->
nimbusMessagingController.getMessage(messageId) messaging.getMessage(messageId)
} }
if (message != null) { if (message != null) {
// Update message as 'clicked'. // Update message as 'clicked'.
nimbusMessagingController.onMessageClicked(message) messaging.onMessageClicked(message)
// Create the intent. // Create the intent.
val intent = nimbusMessagingController.getIntentForMessage(message) val intent = messaging.getIntentForMessage(message)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)

@ -10,8 +10,7 @@ import kotlinx.coroutines.launch
import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.MiddlewareContext import mozilla.components.lib.state.MiddlewareContext
import mozilla.components.service.nimbus.messaging.Message import mozilla.components.service.nimbus.messaging.Message
import mozilla.components.service.nimbus.messaging.NimbusMessagingController import mozilla.components.service.nimbus.messaging.NimbusMessagingControllerInterface
import mozilla.components.service.nimbus.messaging.NimbusMessagingStorage
import org.mozilla.fenix.components.appstate.AppAction import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.ConsumeMessageToShow import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.ConsumeMessageToShow
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.Evaluate import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.Evaluate
@ -21,13 +20,11 @@ import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.Restore
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.UpdateMessageToShow import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.UpdateMessageToShow
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.UpdateMessages import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.UpdateMessages
import org.mozilla.fenix.components.appstate.AppState import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.messaging.FenixNimbusMessagingController
typealias AppStoreMiddlewareContext = MiddlewareContext<AppState, AppAction> typealias AppStoreMiddlewareContext = MiddlewareContext<AppState, AppAction>
class MessagingMiddleware( class MessagingMiddleware(
private val messagingStorage: NimbusMessagingStorage, private val controller: NimbusMessagingControllerInterface,
private val controller: NimbusMessagingController = FenixNimbusMessagingController(messagingStorage),
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO), private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO),
) : Middleware<AppState, AppAction> { ) : Middleware<AppState, AppAction> {
@ -39,13 +36,13 @@ class MessagingMiddleware(
when (action) { when (action) {
is Restore -> { is Restore -> {
coroutineScope.launch { coroutineScope.launch {
val messages = messagingStorage.getMessages() val messages = controller.getMessages()
context.store.dispatch(UpdateMessages(messages)) context.store.dispatch(UpdateMessages(messages))
} }
} }
is Evaluate -> { is Evaluate -> {
val message = messagingStorage.getNextMessage( val message = controller.getNextMessage(
action.surface, action.surface,
context.state.messaging.messages, context.state.messaging.messages,
) )
@ -72,16 +69,15 @@ class MessagingMiddleware(
oldMessage: Message, oldMessage: Message,
context: AppStoreMiddlewareContext, context: AppStoreMiddlewareContext,
) { ) {
val newMessage = controller.updateMessageAsDisplayed(oldMessage)
val newMessages = if (!newMessage.isExpired) {
updateMessage(context, oldMessage, newMessage)
} else {
consumeMessageToShowIfNeeded(context, oldMessage)
removeMessage(context, oldMessage)
}
context.dispatch(UpdateMessages(newMessages))
coroutineScope.launch { coroutineScope.launch {
controller.onMessageDisplayed(newMessage) val newMessage = controller.onMessageDisplayed(oldMessage)
val newMessages = if (!newMessage.isExpired) {
updateMessage(context, oldMessage, newMessage)
} else {
consumeMessageToShowIfNeeded(context, oldMessage)
removeMessage(context, oldMessage)
}
context.store.dispatch(UpdateMessages(newMessages))
} }
} }
@ -136,7 +132,7 @@ class MessagingMiddleware(
val actualMessageToShow = context.state.messaging.messageToShow[updatedMessage.surface] val actualMessageToShow = context.state.messaging.messageToShow[updatedMessage.surface]
if (actualMessageToShow?.id == oldMessage.id) { if (actualMessageToShow?.id == oldMessage.id) {
context.dispatch(UpdateMessageToShow(updatedMessage)) context.store.dispatch(UpdateMessageToShow(updatedMessage))
} }
val oldMessageIndex = context.state.messaging.messages.indexOfFirst { it.id == updatedMessage.id } val oldMessageIndex = context.state.messaging.messages.indexOfFirst { it.id == updatedMessage.id }
val newList = context.state.messaging.messages.toMutableList() val newList = context.state.messaging.messages.toMutableList()

@ -4,13 +4,11 @@
package org.mozilla.fenix.messaging package org.mozilla.fenix.messaging
import androidx.core.net.toUri
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import mozilla.components.service.nimbus.messaging.Message import mozilla.components.service.nimbus.messaging.Message
import mozilla.components.service.nimbus.messaging.MessageData import mozilla.components.service.nimbus.messaging.MessageData
import mozilla.components.service.nimbus.messaging.NimbusMessagingController import mozilla.components.service.nimbus.messaging.NimbusMessagingControllerInterface
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import mozilla.telemetry.glean.testing.GleanTestRule import mozilla.telemetry.glean.testing.GleanTestRule
import org.junit.Before import org.junit.Before
@ -30,7 +28,7 @@ class DefaultMessageControllerTest {
val gleanTestRule = GleanTestRule(testContext) val gleanTestRule = GleanTestRule(testContext)
private val homeActivity: HomeActivity = mockk(relaxed = true) private val homeActivity: HomeActivity = mockk(relaxed = true)
private val messagingController: NimbusMessagingController = mockk(relaxed = true) private val messagingController: NimbusMessagingControllerInterface = mockk(relaxed = true)
private lateinit var defaultMessageController: DefaultMessageController private lateinit var defaultMessageController: DefaultMessageController
private val appStore: AppStore = mockk(relaxed = true) private val appStore: AppStore = mockk(relaxed = true)
@ -46,12 +44,10 @@ class DefaultMessageControllerTest {
@Test @Test
fun `WHEN calling onMessagePressed THEN process the action intent and update the app store`() { fun `WHEN calling onMessagePressed THEN process the action intent and update the app store`() {
val message = mockMessage() val message = mockMessage()
val uri = "action".toUri()
every { messagingController.processMessageActionToUri(message) }.returns(uri)
defaultMessageController.onMessagePressed(message) defaultMessageController.onMessagePressed(message)
verify { messagingController.processMessageActionToUri(message) } verify { messagingController.getIntentForMessage(message) }
verify { homeActivity.processIntent(any()) } verify { homeActivity.processIntent(any()) }
verify { appStore.dispatch(MessageClicked(message)) } verify { appStore.dispatch(MessageClicked(message)) }
} }

@ -12,13 +12,13 @@ import kotlinx.coroutines.test.advanceUntilIdle
import mozilla.components.service.nimbus.messaging.Message import mozilla.components.service.nimbus.messaging.Message
import mozilla.components.service.nimbus.messaging.MessageData import mozilla.components.service.nimbus.messaging.MessageData
import mozilla.components.service.nimbus.messaging.NimbusMessagingController import mozilla.components.service.nimbus.messaging.NimbusMessagingController
import mozilla.components.service.nimbus.messaging.NimbusMessagingStorage
import mozilla.components.service.nimbus.messaging.StyleData import mozilla.components.service.nimbus.messaging.StyleData
import mozilla.components.support.test.ext.joinBlocking import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.libstate.ext.waitUntilIdle import mozilla.components.support.test.libstate.ext.waitUntilIdle
import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.rule.MainCoroutineRule
import mozilla.components.support.test.rule.runTestOnMain import mozilla.components.support.test.rule.runTestOnMain
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -30,20 +30,17 @@ import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.MessageDi
import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.Restore import org.mozilla.fenix.components.appstate.AppAction.MessagingAction.Restore
import org.mozilla.fenix.components.appstate.AppState import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.messaging.FenixMessageSurfaceId import org.mozilla.fenix.messaging.FenixMessageSurfaceId
import org.mozilla.fenix.messaging.FenixNimbusMessagingController
import org.mozilla.fenix.messaging.MessagingState import org.mozilla.fenix.messaging.MessagingState
class MessagingMiddlewareTest { class MessagingMiddlewareTest {
@get:Rule @get:Rule
val coroutinesTestRule = MainCoroutineRule() val coroutinesTestRule = MainCoroutineRule()
private val coroutineScope = coroutinesTestRule.scope private val coroutineScope = coroutinesTestRule.scope
private lateinit var messagingStorage: NimbusMessagingStorage
private lateinit var controller: NimbusMessagingController private lateinit var controller: NimbusMessagingController
@Before @Before
fun setUp() { fun setUp() {
messagingStorage = mockk(relaxed = true) controller = mockk(relaxed = true)
controller = FenixNimbusMessagingController(messagingStorage) { 0 }
} }
@Test @Test
@ -55,12 +52,12 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
val message = createMessage() val message = createMessage()
coEvery { messagingStorage.getMessages() } returns listOf(message) coEvery { controller.getMessages() } returns listOf(message)
store.dispatch(Restore).joinBlocking() store.dispatch(Restore).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
@ -81,12 +78,12 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
every { every {
messagingStorage.getNextMessage( controller.getNextMessage(
FenixMessageSurfaceId.HOMESCREEN, FenixMessageSurfaceId.HOMESCREEN,
any(), any(),
) )
@ -111,7 +108,7 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
@ -121,7 +118,7 @@ class MessagingMiddlewareTest {
store.waitUntilIdle() store.waitUntilIdle()
assertTrue(store.state.messaging.messages.isEmpty()) assertTrue(store.state.messaging.messages.isEmpty())
coVerify { messagingStorage.updateMetadata(createMetadata(pressed = true)) } coVerify { controller.onMessageClicked(message = message) }
} }
@Test @Test
@ -135,14 +132,14 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
store.dispatch(MessageDismissed(message)).joinBlocking() store.dispatch(MessageDismissed(message)).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
assertTrue(store.state.messaging.messages.isEmpty()) assertTrue(store.state.messaging.messages.isEmpty())
coVerify { messagingStorage.updateMetadata(metadata.copy(dismissed = true)) } coVerify { controller.onMessageDismissed(message = message) }
} }
@Test @Test
@ -158,7 +155,7 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
@ -184,7 +181,7 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
@ -198,6 +195,7 @@ class MessagingMiddlewareTest {
@Test @Test
fun `WHEN updateMessage THEN update available messages`() = runTestOnMain { fun `WHEN updateMessage THEN update available messages`() = runTestOnMain {
val message = createMessage() val message = createMessage()
val messageDisplayed = message.copy(metadata = createMetadata(displayCount = 1))
val store = AppStore( val store = AppStore(
AppState( AppState(
messaging = MessagingState( messaging = MessagingState(
@ -207,20 +205,25 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
every { every {
messagingStorage.getNextMessage( controller.getNextMessage(
FenixMessageSurfaceId.HOMESCREEN, FenixMessageSurfaceId.HOMESCREEN,
any(), any(),
) )
} returns message } returns message
store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)) coEvery {
controller.onMessageDisplayed(eq(message), any())
} returns messageDisplayed
store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
assertEquals(1, store.state.messaging.messages.count())
assertEquals(1, store.state.messaging.messages.first().displayCount) assertEquals(1, store.state.messaging.messages.first().displayCount)
} }
@ -240,17 +243,21 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
every { every {
messagingStorage.getNextMessage( controller.getNextMessage(
FenixMessageSurfaceId.HOMESCREEN, FenixMessageSurfaceId.HOMESCREEN,
any(), any(),
) )
} returns message1 } returns message1
coEvery {
controller.onMessageDisplayed(eq(message1), any())
} returns messageDisplayed1
store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking() store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
@ -272,17 +279,21 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
every { every {
messagingStorage.getNextMessage( controller.getNextMessage(
FenixMessageSurfaceId.HOMESCREEN, FenixMessageSurfaceId.HOMESCREEN,
any(), any(),
) )
} returns message } returns message
coEvery {
controller.onMessageDisplayed(eq(message), any())
} returns messageDisplayed
store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking() store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
@ -291,7 +302,11 @@ class MessagingMiddlewareTest {
@Test @Test
fun `GIVEN a message with that surpassed the maxDisplayCount WHEN onMessagedDisplayed THEN remove the message and consume it`() = runTestOnMain { fun `GIVEN a message with that surpassed the maxDisplayCount WHEN onMessagedDisplayed THEN remove the message and consume it`() = runTestOnMain {
val message = createMessage(createMetadata(displayCount = 6)) val message = createMessage(createMetadata(displayCount = 4))
val messageDisplayed = createMessage(createMetadata(displayCount = 5))
assertFalse(message.isExpired)
assertTrue(messageDisplayed.isExpired)
val store = AppStore( val store = AppStore(
AppState( AppState(
messaging = MessagingState( messaging = MessagingState(
@ -302,17 +317,21 @@ class MessagingMiddlewareTest {
), ),
), ),
listOf( listOf(
MessagingMiddleware(messagingStorage, controller, coroutineScope), MessagingMiddleware(controller, coroutineScope),
), ),
) )
every { every {
messagingStorage.getNextMessage( controller.getNextMessage(
FenixMessageSurfaceId.HOMESCREEN, FenixMessageSurfaceId.HOMESCREEN,
any(), any(),
) )
} returns message } returns message
coEvery {
controller.onMessageDisplayed(eq(message), any())
} returns messageDisplayed
store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking() store.dispatch(Evaluate(FenixMessageSurfaceId.HOMESCREEN)).joinBlocking()
store.waitUntilIdle() store.waitUntilIdle()
@ -322,7 +341,7 @@ class MessagingMiddlewareTest {
} }
private fun createMessage( private fun createMessage(
metadata: Message.Metadata = createMetadata(), metadata: Message.Metadata = createMetadata(),
messageId: String = "control-id", messageId: String = "message-id",
data: MessageData = mockk(relaxed = true), data: MessageData = mockk(relaxed = true),
action: String = "action", action: String = "action",
styleData: StyleData = StyleData(), styleData: StyleData = StyleData(),

Loading…
Cancel
Save