Pinned events: add tests on PinnedMessagesBannerPresenter
This commit is contained in:
parent
4771b25fe3
commit
291ccd45da
2 changed files with 154 additions and 19 deletions
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue