Browse Source

Pinned events: add tests on PinnedMessagesBannerPresenter

pull/3275/head
ganfra 1 month ago
parent
commit
0390ede2ad
  1. 16
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt
  2. 157
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt

16
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt

@ -52,15 +52,15 @@ class PinnedMessagesBannerPresenter @Inject constructor(
@Composable @Composable
override fun present(): PinnedMessagesBannerState { override fun present(): PinnedMessagesBannerState {
val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents).collectAsState(initial = false) val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents).collectAsState(initial = false)
var timelineFailed by rememberSaveable { mutableStateOf(false) } var hasTimelineFailedToLoad by rememberSaveable { mutableStateOf(false) }
var pinnedItems by remember { var currentPinnedMessageIndex by rememberSaveable { mutableIntStateOf(0) }
mutableStateOf<ImmutableList<PinnedMessagesBannerItem>>(persistentListOf())
}
val knownPinnedMessagesCount by remember { val knownPinnedMessagesCount by remember {
room.roomInfoFlow.map { roomInfo -> roomInfo.pinnedEventIds.size } room.roomInfoFlow.map { roomInfo -> roomInfo.pinnedEventIds.size }
}.collectAsState(initial = null) }.collectAsState(initial = null)
var currentPinnedMessageIndex by rememberSaveable { mutableIntStateOf(0) } var pinnedItems by remember {
mutableStateOf<ImmutableList<PinnedMessagesBannerItem>>(persistentListOf())
}
PinnedMessagesBannerItemsEffect( PinnedMessagesBannerItemsEffect(
isFeatureEnabled = isFeatureEnabled, isFeatureEnabled = isFeatureEnabled,
@ -72,7 +72,7 @@ class PinnedMessagesBannerPresenter @Inject constructor(
pinnedItems = newItems pinnedItems = newItems
}, },
onTimelineFail = { hasTimelineFailed -> onTimelineFail = { hasTimelineFailed ->
timelineFailed = hasTimelineFailed hasTimelineFailedToLoad = hasTimelineFailed
} }
) )
@ -90,7 +90,7 @@ class PinnedMessagesBannerPresenter @Inject constructor(
return pinnedMessagesBannerState( return pinnedMessagesBannerState(
isFeatureEnabled = isFeatureEnabled, isFeatureEnabled = isFeatureEnabled,
hasTimelineFailed = timelineFailed, hasTimelineFailed = hasTimelineFailedToLoad,
realPinnedMessagesCount = knownPinnedMessagesCount, realPinnedMessagesCount = knownPinnedMessagesCount,
pinnedItems = pinnedItems, pinnedItems = pinnedItems,
currentPinnedMessageIndex = currentPinnedMessageIndex, currentPinnedMessageIndex = currentPinnedMessageIndex,
@ -117,7 +117,7 @@ class PinnedMessagesBannerPresenter @Inject constructor(
PinnedMessagesBannerState.Loaded( PinnedMessagesBannerState.Loaded(
currentPinnedMessage = currentPinnedMessage, currentPinnedMessage = currentPinnedMessage,
currentPinnedMessageIndex = currentPinnedMessageIndex, currentPinnedMessageIndex = currentPinnedMessageIndex,
knownPinnedMessagesCount = realPinnedMessagesCount, knownPinnedMessagesCount = pinnedItems.size,
eventSink = eventSink eventSink = eventSink
) )
} }

157
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt

@ -17,13 +17,23 @@
package io.element.android.features.messages.impl.pinned.banner package io.element.android.features.messages.impl.pinned.banner
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -31,23 +41,146 @@ import org.junit.Test
class PinnedMessagesBannerPresenterTest { class PinnedMessagesBannerPresenterTest {
@Test @Test
fun `present - initial state`() = runTest { fun `present - initial state`() = runTest {
val presenter = createPinnedMessagesBannerPresenter() val presenter = createPinnedMessagesBannerPresenter(isFeatureEnabled = true)
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.pinnedMessagesCount).isEqualTo(0) assertThat(initialState).isEqualTo(PinnedMessagesBannerState.Hidden)
assertThat(initialState.currentPinnedMessageIndex).isEqualTo(0) cancelAndIgnoreRemainingEvents()
assertThat(initialState.currentPinnedMessage).isNull()
} }
} }
@Test @Test
fun `present - move to next pinned message when there is no pinned events`() = runTest { fun `present - feature disabled`() = runTest {
val presenter = createPinnedMessagesBannerPresenter() val presenter = createPinnedMessagesBannerPresenter(isFeatureEnabled = false)
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned) assertThat(initialState).isEqualTo(PinnedMessagesBannerState.Hidden)
// Nothing is emitted }
ensureAllEventsConsumed() }
@Test
fun `present - loading state`() = runTest {
val room = FakeMatrixRoom(
pinnedEventsTimelineResult = { Result.success(FakeTimeline()) }
).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
}
val presenter = createPinnedMessagesBannerPresenter(room = room)
presenter.test {
skipItems(1)
val loadingState = awaitItem()
assertThat(loadingState).isEqualTo(PinnedMessagesBannerState.Loading(1))
assertThat(loadingState.pinnedMessagesCount()).isEqualTo(1)
assertThat(loadingState.currentPinnedMessageIndex()).isEqualTo(0)
}
}
@Test
fun `present - loaded state`() = runTest {
val messageContent = aMessageContent("A message")
val pinnedEventsTimeline = FakeTimeline(
timelineItems = flowOf(
listOf(
MatrixTimelineItem.Event(
uniqueId = "FAKE_UNIQUE_ID",
event = anEventTimelineItem(
eventId = AN_EVENT_ID,
content = messageContent,
),
)
)
)
)
val room = FakeMatrixRoom(
pinnedEventsTimelineResult = { Result.success(pinnedEventsTimeline) }
).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
}
val presenter = createPinnedMessagesBannerPresenter(room = room)
presenter.test {
skipItems(2)
val loadedState = awaitItem() as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(1)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent.toString())
}
}
@Test
fun `present - loaded state - multiple pinned messages`() = runTest {
val messageContent1 = aMessageContent("A message")
val messageContent2 = aMessageContent("Another message")
val pinnedEventsTimeline = FakeTimeline(
timelineItems = flowOf(
listOf(
MatrixTimelineItem.Event(
uniqueId = "FAKE_UNIQUE_ID",
event = anEventTimelineItem(
eventId = AN_EVENT_ID,
content = messageContent1,
),
),
MatrixTimelineItem.Event(
uniqueId = "FAKE_UNIQUE_ID_2",
event = anEventTimelineItem(
eventId = AN_EVENT_ID_2,
content = messageContent2,
),
)
)
)
)
val room = FakeMatrixRoom(
pinnedEventsTimelineResult = { Result.success(pinnedEventsTimeline) }
).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
}
val presenter = createPinnedMessagesBannerPresenter(room = room)
presenter.test {
skipItems(2)
awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString())
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}
awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(1)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent2.toString())
loadedState.eventSink(PinnedMessagesBannerEvents.MoveToNextPinned)
}
awaitItem().also { loadedState ->
loadedState as PinnedMessagesBannerState.Loaded
assertThat(loadedState.currentPinnedMessageIndex).isEqualTo(0)
assertThat(loadedState.knownPinnedMessagesCount).isEqualTo(2)
assertThat(loadedState.currentPinnedMessage.formatted.text).isEqualTo(messageContent1.toString())
}
}
}
@Test
fun `present - timeline failed`() = runTest {
val room = FakeMatrixRoom(
pinnedEventsTimelineResult = { Result.failure(Exception()) }
).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
}
val presenter = createPinnedMessagesBannerPresenter(room = room)
presenter.test {
skipItems(1)
awaitItem().also { loadingState ->
assertThat(loadingState).isEqualTo(PinnedMessagesBannerState.Loading(1))
assertThat(loadingState.pinnedMessagesCount()).isEqualTo(1)
assertThat(loadingState.currentPinnedMessageIndex()).isEqualTo(0)
}
awaitItem().also { failedState ->
assertThat(failedState).isEqualTo(PinnedMessagesBannerState.Hidden)
}
} }
} }
@ -56,9 +189,10 @@ class PinnedMessagesBannerPresenterTest {
itemFactory: PinnedMessagesBannerItemFactory = PinnedMessagesBannerItemFactory( itemFactory: PinnedMessagesBannerItemFactory = PinnedMessagesBannerItemFactory(
coroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers = testCoroutineDispatchers(),
formatter = FakePinnedMessagesBannerFormatter( formatter = FakePinnedMessagesBannerFormatter(
formatLambda = { event -> "Content ${event.content}" } formatLambda = { event -> "${event.content}" }
) )
), ),
networkMonitor: NetworkMonitor = FakeNetworkMonitor(),
isFeatureEnabled: Boolean = true, isFeatureEnabled: Boolean = true,
): PinnedMessagesBannerPresenter { ): PinnedMessagesBannerPresenter {
val featureFlagService = FakeFeatureFlagService( val featureFlagService = FakeFeatureFlagService(
@ -69,7 +203,8 @@ class PinnedMessagesBannerPresenterTest {
return PinnedMessagesBannerPresenter( return PinnedMessagesBannerPresenter(
room = room, room = room,
itemFactory = itemFactory, itemFactory = itemFactory,
featureFlagService = featureFlagService featureFlagService = featureFlagService,
networkMonitor = networkMonitor,
) )
} }
} }

Loading…
Cancel
Save