|
|
@ -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, |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|