Browse Source

Rework FakeMatrixRoom so that it contains only lambdas. (#3229)

* Upgrade lint to 8.7.0-alpha01

* FakeMatrixRoom: lambda everywhere

Fix test compilation issues
pull/3237/head
Benoit Marty 2 months ago committed by GitHub
parent
commit
5bda29ca7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt
  2. 16
      appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt
  3. 38
      features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProviderTest.kt
  4. 8
      features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt
  5. 20
      features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenterTest.kt
  6. 74
      features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
  7. 186
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
  8. 26
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt
  9. 30
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenterTest.kt
  10. 142
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt
  11. 31
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt
  12. 28
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
  13. 26
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt
  14. 54
      features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
  15. 26
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt
  16. 283
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt
  17. 169
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
  18. 71
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTest.kt
  19. 88
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt
  20. 104
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/DefaultRoomMembersModerationPresenterTest.kt
  21. 21
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/RolesAndPermissionPresenterTest.kt
  22. 15
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt
  23. 25
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt
  24. 13
      features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt
  25. 9
      features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt
  26. 2
      gradle.properties
  27. 523
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  28. 1
      libraries/mediaupload/api/build.gradle.kts
  29. 23
      libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt
  30. 11
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt

8
appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt

@ -123,7 +123,9 @@ class JoinRoomLoadedFlowNodeTest {
@Test @Test
fun `given a room flow node when initialized then it loads messages entry point`() = runTest { fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
// GIVEN // GIVEN
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
updateMembersResult = { }
)
val fakeMessagesEntryPoint = FakeMessagesEntryPoint() val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages()) val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val roomFlowNode = createJoinedRoomLoadedFlowNode( val roomFlowNode = createJoinedRoomLoadedFlowNode(
@ -144,7 +146,9 @@ class JoinRoomLoadedFlowNodeTest {
@Test @Test
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest { fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
// GIVEN // GIVEN
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
updateMembersResult = { }
)
val fakeMessagesEntryPoint = FakeMessagesEntryPoint() val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint() val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages()) val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())

16
appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt

@ -33,7 +33,6 @@ import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest { @OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest {
private val matrixClient = FakeMatrixClient() private val matrixClient = FakeMatrixClient()
private val room = FakeMatrixRoom()
private val networkMonitor = FakeNetworkMonitor() private val networkMonitor = FakeNetworkMonitor()
private val sut = SendQueues(matrixClient, networkMonitor) private val sut = SendQueues(matrixClient, networkMonitor)
@ -43,11 +42,11 @@ import org.junit.Test
val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> } val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> }
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
matrixClient.givenGetRoomResult(room.roomId, room)
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> } val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda val room = FakeMatrixRoom(
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
)
matrixClient.givenGetRoomResult(room.roomId, room)
sut.launchIn(backgroundScope) sut.launchIn(backgroundScope)
sendQueueDisabledFlow.emit(room.roomId) sendQueueDisabledFlow.emit(room.roomId)
@ -72,10 +71,11 @@ import org.junit.Test
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
networkMonitor.connectivity.value = NetworkStatus.Offline networkMonitor.connectivity.value = NetworkStatus.Offline
matrixClient.givenGetRoomResult(room.roomId, room)
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> } val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda val room = FakeMatrixRoom(
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
)
matrixClient.givenGetRoomResult(room.roomId, room)
sut.launchIn(backgroundScope) sut.launchIn(backgroundScope)

38
features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProviderTest.kt

@ -54,9 +54,9 @@ class DefaultCallWidgetProviderTest {
@Test @Test
fun `getWidget - fails if it can't generate the URL for the widget`() = runTest { fun `getWidget - fails if it can't generate the URL for the widget`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenGenerateWidgetWebViewUrlResult(Result.failure(Exception("Can't generate URL for widget"))) generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.failure(Exception("Can't generate URL for widget")) }
} )
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }
@ -66,10 +66,10 @@ class DefaultCallWidgetProviderTest {
@Test @Test
fun `getWidget - fails if it can't get the widget driver`() = runTest { fun `getWidget - fails if it can't get the widget driver`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenGenerateWidgetWebViewUrlResult(Result.success("url")) generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
givenGetWidgetDriverResult(Result.failure(Exception("Can't get a widget driver"))) getWidgetDriverResult = { Result.failure(Exception("Can't get a widget driver")) }
} )
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }
@ -79,10 +79,10 @@ class DefaultCallWidgetProviderTest {
@Test @Test
fun `getWidget - returns a widget driver when all steps are successful`() = runTest { fun `getWidget - returns a widget driver when all steps are successful`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenGenerateWidgetWebViewUrlResult(Result.success("url")) generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver())) getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
} )
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }
@ -92,10 +92,10 @@ class DefaultCallWidgetProviderTest {
@Test @Test
fun `getWidget - will use a custom base url if it exists`() = runTest { fun `getWidget - will use a custom base url if it exists`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenGenerateWidgetWebViewUrlResult(Result.success("url")) generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver())) getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
} )
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }
@ -120,10 +120,10 @@ class DefaultCallWidgetProviderTest {
val elementCallBaseUrlProvider = FakeElementCallBaseUrlProvider { matrixClient -> val elementCallBaseUrlProvider = FakeElementCallBaseUrlProvider { matrixClient ->
providesLambda(matrixClient) providesLambda(matrixClient)
} }
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenGenerateWidgetWebViewUrlResult(Result.success("url")) generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver())) getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
} )
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }

8
features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt

@ -92,9 +92,9 @@ class AcceptDeclineInvitePresenterTest {
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom().apply { result = FakeMatrixRoom(
leaveRoomLambda = declineInviteFailure leaveRoomLambda = declineInviteFailure
} )
) )
} }
val presenter = createAcceptDeclineInvitePresenter(client = client) val presenter = createAcceptDeclineInvitePresenter(client = client)
@ -142,9 +142,9 @@ class AcceptDeclineInvitePresenterTest {
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom().apply { result = FakeMatrixRoom(
leaveRoomLambda = declineInviteSuccess leaveRoomLambda = declineInviteSuccess
} )
) )
} }
val presenter = createAcceptDeclineInvitePresenter( val presenter = createAcceptDeclineInvitePresenter(

20
features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenterTest.kt

@ -140,7 +140,9 @@ class DefaultLeaveRoomPresenterTest {
client = FakeMatrixClient().apply { client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom(), result = FakeMatrixRoom(
leaveRoomLambda = { Result.success(Unit) }
),
) )
}, },
roomMembershipObserver = roomMembershipObserver roomMembershipObserver = roomMembershipObserver
@ -162,9 +164,9 @@ class DefaultLeaveRoomPresenterTest {
client = FakeMatrixClient().apply { client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom().apply { result = FakeMatrixRoom(
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) } leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
}, ),
) )
} }
) )
@ -186,7 +188,9 @@ class DefaultLeaveRoomPresenterTest {
client = FakeMatrixClient().apply { client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom(), result = FakeMatrixRoom(
leaveRoomLambda = { Result.success(Unit) }
),
) )
} }
) )
@ -208,9 +212,9 @@ class DefaultLeaveRoomPresenterTest {
client = FakeMatrixClient().apply { client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom().apply { result = FakeMatrixRoom(
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) } leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
}, ),
) )
} }
) )

74
features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt

@ -29,13 +29,15 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.permissions.PermissionsState
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
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.SendLocationInvocation
import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
@ -46,16 +48,18 @@ class SendLocationPresenterTest {
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()
private val fakePermissionsPresenter = FakePermissionsPresenter() private val fakePermissionsPresenter = FakePermissionsPresenter()
private val fakeMatrixRoom = FakeMatrixRoom()
private val fakeAnalyticsService = FakeAnalyticsService() private val fakeAnalyticsService = FakeAnalyticsService()
private val fakeMessageComposerContext = FakeMessageComposerContext() private val fakeMessageComposerContext = FakeMessageComposerContext()
private val fakeLocationActions = FakeLocationActions() private val fakeLocationActions = FakeLocationActions()
private val fakeBuildMeta = aBuildMeta(applicationName = "app name") private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter(
private fun createSendLocationPresenter(
matrixRoom: MatrixRoom = FakeMatrixRoom(),
): SendLocationPresenter = SendLocationPresenter(
permissionsPresenterFactory = object : PermissionsPresenter.Factory { permissionsPresenterFactory = object : PermissionsPresenter.Factory {
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
}, },
room = fakeMatrixRoom, room = matrixRoom,
analyticsService = fakeAnalyticsService, analyticsService = fakeAnalyticsService,
messageComposerContext = fakeMessageComposerContext, messageComposerContext = fakeMessageComposerContext,
locationActions = fakeLocationActions, locationActions = fakeLocationActions,
@ -64,6 +68,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions granted`() = runTest { fun `initial state with permissions granted`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted, permissions = PermissionsState.Permissions.AllGranted,
@ -90,6 +95,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions partially granted`() = runTest { fun `initial state with permissions partially granted`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.SomeGranted, permissions = PermissionsState.Permissions.SomeGranted,
@ -116,6 +122,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions denied`() = runTest { fun `initial state with permissions denied`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -142,6 +149,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions denied once`() = runTest { fun `initial state with permissions denied once`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -168,6 +176,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `rationale dialog dismiss`() = runTest { fun `rationale dialog dismiss`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -199,6 +208,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `rationale dialog continue`() = runTest { fun `rationale dialog continue`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -227,6 +237,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `permission denied dialog dismiss`() = runTest { fun `permission denied dialog dismiss`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -258,6 +269,13 @@ class SendLocationPresenterTest {
@Test @Test
fun `share sender location`() = runTest { fun `share sender location`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
Result.success(Unit)
}
val matrixRoom = FakeMatrixRoom(
sendLocationResult = sendLocationResult,
)
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted, permissions = PermissionsState.Permissions.AllGranted,
@ -289,16 +307,14 @@ class SendLocationPresenterTest {
delay(1) // Wait for the coroutine to finish delay(1) // Wait for the coroutine to finish
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) sendLocationResult.assertions().isCalledOnce()
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( .with(
SendLocationInvocation( value("Location was shared at geo:3.0,4.0;u=5.0"),
body = "Location was shared at geo:3.0,4.0;u=5.0", value("geo:3.0,4.0;u=5.0"),
geoUri = "geo:3.0,4.0;u=5.0", value(null),
description = null, value(15),
zoomLevel = 15, value(AssetType.SENDER),
assetType = AssetType.SENDER
) )
)
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
@ -314,6 +330,13 @@ class SendLocationPresenterTest {
@Test @Test
fun `share pin location`() = runTest { fun `share pin location`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
Result.success(Unit)
}
val matrixRoom = FakeMatrixRoom(
sendLocationResult = sendLocationResult,
)
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -345,16 +368,14 @@ class SendLocationPresenterTest {
delay(1) // Wait for the coroutine to finish delay(1) // Wait for the coroutine to finish
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) sendLocationResult.assertions().isCalledOnce()
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( .with(
SendLocationInvocation( value("Location was shared at geo:0.0,1.0"),
body = "Location was shared at geo:0.0,1.0", value("geo:0.0,1.0"),
geoUri = "geo:0.0,1.0", value(null),
description = null, value(15),
zoomLevel = 15, value(AssetType.PIN),
assetType = AssetType.PIN
) )
)
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1) assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo( assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
@ -370,6 +391,13 @@ class SendLocationPresenterTest {
@Test @Test
fun `composer context passes through analytics`() = runTest { fun `composer context passes through analytics`() = runTest {
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
Result.success(Unit)
}
val matrixRoom = FakeMatrixRoom(
sendLocationResult = sendLocationResult,
)
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -418,6 +446,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `open settings activity`() = runTest { fun `open settings activity`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
fakePermissionsPresenter.givenState( fakePermissionsPresenter.givenState(
aPermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
@ -452,6 +481,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `application name is in state`() = runTest { fun `application name is in state`() = runTest {
val sendLocationPresenter = createSendLocationPresenter()
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present() sendLocationPresenter.present()
}.test { }.test {

186
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt

@ -65,6 +65,7 @@ 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.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
@ -103,6 +104,7 @@ import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilPredicate
import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.consumeItemsUntilTimeout
import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.assert
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
@ -136,7 +138,7 @@ class MessagesPresenterTest {
assertThat(initialState.roomAvatar) assertThat(initialState.roomAvatar)
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom))) .isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
assertThat(initialState.userHasPermissionToSendMessage).isTrue() assertThat(initialState.userHasPermissionToSendMessage).isTrue()
assertThat(initialState.userHasPermissionToRedactOwn).isFalse() assertThat(initialState.userHasPermissionToRedactOwn).isTrue()
assertThat(initialState.hasNetworkConnection).isTrue() assertThat(initialState.hasNetworkConnection).isTrue()
assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.snackbarMessage).isNull()
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
@ -147,7 +149,13 @@ class MessagesPresenterTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `present - check that the room's unread flag is removed`() = runTest { fun `present - check that the room's unread flag is removed`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
assertThat(room.markAsReadCalls).isEmpty() assertThat(room.markAsReadCalls).isEmpty()
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -161,8 +169,13 @@ class MessagesPresenterTest {
@Test @Test
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanUserJoinCall(Result.success(false)) canUserJoinCallResult = { Result.success(false) },
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
).apply {
givenRoomInfo(aRoomInfo(hasRoomCall = true)) givenRoomInfo(aRoomInfo(hasRoomCall = true))
} }
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
@ -183,7 +196,14 @@ class MessagesPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.toggleReactionLambda = toggleReactionSuccess this.toggleReactionLambda = toggleReactionSuccess
} }
val room = FakeMatrixRoom(liveTimeline = timeline) val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -213,7 +233,14 @@ class MessagesPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.toggleReactionLambda = toggleReactionSuccess this.toggleReactionLambda = toggleReactionSuccess
} }
val room = FakeMatrixRoom(liveTimeline = timeline) val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -266,6 +293,11 @@ class MessagesPresenterTest {
val event = aMessageEvent() val event = aMessageEvent()
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
eventPermalinkResult = { Result.success("a link") }, eventPermalinkResult = { Result.success("a link") },
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
) )
val presenter = createMessagesPresenter( val presenter = createMessagesPresenter(
clipboardHelper = clipboardHelper, clipboardHelper = clipboardHelper,
@ -448,7 +480,14 @@ class MessagesPresenterTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val liveTimeline = FakeTimeline() val liveTimeline = FakeTimeline()
val matrixRoom = FakeMatrixRoom(liveTimeline = liveTimeline) val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) } val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
liveTimeline.redactEventLambda = redactEventLambda liveTimeline.redactEventLambda = redactEventLambda
@ -513,7 +552,16 @@ class MessagesPresenterTest {
@Test @Test
fun `present - shows prompt to reinvite users in DM`() = runTest { fun `present - shows prompt to reinvite users in DM`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 1L) val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
isDirect = true,
activeMemberCount = 1L,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -539,7 +587,16 @@ class MessagesPresenterTest {
@Test @Test
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest { fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = false, activeMemberCount = 1L) val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
isDirect = false,
activeMemberCount = 1L,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -554,7 +611,16 @@ class MessagesPresenterTest {
@Test @Test
fun `present - doesn't show reinvite prompt if other party is present`() = runTest { fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 2L) val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
isDirect = true,
activeMemberCount = 2L,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -569,7 +635,16 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle reinviting other user when memberlist is ready`() = runTest { fun `present - handle reinviting other user when memberlist is ready`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID) val inviteUserResult = lambdaRecorder { _: UserId -> Result.success(Unit) }
val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
inviteUserResult = inviteUserResult,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -589,13 +664,22 @@ class MessagesPresenterTest {
assertThat(loadingState.inviteProgress.isLoading()).isTrue() assertThat(loadingState.inviteProgress.isLoading()).isTrue()
val newState = awaitItem() val newState = awaitItem()
assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(newState.inviteProgress.isSuccess()).isTrue()
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
} }
} }
@Test @Test
fun `present - handle reinviting other user when memberlist is error`() = runTest { fun `present - handle reinviting other user when memberlist is error`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID) val inviteUserResult = lambdaRecorder { _: UserId -> Result.success(Unit) }
val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
inviteUserResult = inviteUserResult,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Error( MatrixRoomMembersState.Error(
failure = Throwable(), failure = Throwable(),
@ -618,13 +702,20 @@ class MessagesPresenterTest {
assertThat(loadingState.inviteProgress.isLoading()).isTrue() assertThat(loadingState.inviteProgress.isLoading()).isTrue()
val newState = awaitItem() val newState = awaitItem()
assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(newState.inviteProgress.isSuccess()).isTrue()
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
} }
} }
@Test @Test
fun `present - handle reinviting other user when memberlist is not ready`() = runTest { fun `present - handle reinviting other user when memberlist is not ready`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID) val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
room.givenRoomMembersState(MatrixRoomMembersState.Unknown) room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -642,7 +733,15 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle reinviting other user when inviting fails`() = runTest { fun `present - handle reinviting other user when inviting fails`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID) val room = FakeMatrixRoom(
sessionId = A_SESSION_ID,
inviteUserResult = { Result.failure(Throwable("Oops!")) },
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
room.givenRoomMembersState( room.givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf( persistentListOf(
@ -651,7 +750,6 @@ class MessagesPresenterTest {
) )
) )
) )
room.givenInviteUserResult(Result.failure(Throwable("Oops!")))
val presenter = createMessagesPresenter(matrixRoom = room) val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -671,8 +769,19 @@ class MessagesPresenterTest {
@Test @Test
fun `present - permission to post`() = runTest { fun `present - permission to post`() = runTest {
val matrixRoom = FakeMatrixRoom() val matrixRoom = FakeMatrixRoom(
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true)) canUserSendMessageResult = { _, messageEventType ->
when (messageEventType) {
MessageEventType.ROOM_MESSAGE -> Result.success(true)
MessageEventType.REACTION -> Result.success(true)
else -> lambdaError()
}
},
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -684,8 +793,19 @@ class MessagesPresenterTest {
@Test @Test
fun `present - no permission to post`() = runTest { fun `present - no permission to post`() = runTest {
val matrixRoom = FakeMatrixRoom() val matrixRoom = FakeMatrixRoom(
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false)) canUserSendMessageResult = { _, messageEventType ->
when (messageEventType) {
MessageEventType.ROOM_MESSAGE -> Result.success(false)
MessageEventType.REACTION -> Result.success(false)
else -> lambdaError()
}
},
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -700,7 +820,13 @@ class MessagesPresenterTest {
@Test @Test
fun `present - permission to redact own`() = runTest { fun `present - permission to redact own`() = runTest {
val matrixRoom = FakeMatrixRoom(canRedactOwn = true) val matrixRoom = FakeMatrixRoom(
canRedactOwnResult = { Result.success(true) },
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOtherResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -714,7 +840,13 @@ class MessagesPresenterTest {
@Test @Test
fun `present - permission to redact other`() = runTest { fun `present - permission to redact other`() = runTest {
val matrixRoom = FakeMatrixRoom(canRedactOther = true) val matrixRoom = FakeMatrixRoom(
canRedactOtherResult = { Result.success(true) },
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom) val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -754,7 +886,13 @@ class MessagesPresenterTest {
private fun TestScope.createMessagesPresenter( private fun TestScope.createMessagesPresenter(
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
matrixRoom: MatrixRoom = FakeMatrixRoom().apply { matrixRoom: MatrixRoom = FakeMatrixRoom(
canUserSendMessageResult = { _, _ -> Result.success(true) },
canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) },
).apply {
givenRoomInfo(aRoomInfo(id = roomId, name = "")) givenRoomInfo(aRoomInfo(id = roomId, name = ""))
}, },
navigator: FakeMessagesNavigator = FakeMessagesNavigator(), navigator: FakeMessagesNavigator = FakeMessagesNavigator(),

26
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt

@ -26,7 +26,9 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
import io.element.android.features.messages.impl.attachments.preview.SendActionState import io.element.android.features.messages.impl.attachments.preview.SendActionState
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaSender
@ -34,6 +36,7 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.LocalMedia
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -49,13 +52,16 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send media success scenario`() = runTest { fun `present - send media success scenario`() = runTest {
val room = FakeMatrixRoom() val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
room.givenProgressCallbackValues( Result.success(FakeMediaUploadHandler())
listOf( }
val room = FakeMatrixRoom(
progressCallbackValues = listOf(
Pair(0, 10), Pair(0, 10),
Pair(5, 10), Pair(5, 10),
Pair(10, 10) Pair(10, 10)
) ),
sendMediaResult = sendMediaResult,
) )
val presenter = createAttachmentsPreviewPresenter(room = room) val presenter = createAttachmentsPreviewPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -70,15 +76,19 @@ class AttachmentsPreviewPresenterTest {
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f))
val successState = awaitItem() val successState = awaitItem()
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done) assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
assertThat(room.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
} }
} }
@Test @Test
fun `present - send media failure scenario`() = runTest { fun `present - send media failure scenario`() = runTest {
val room = FakeMatrixRoom()
val failure = MediaPreProcessor.Failure(null) val failure = MediaPreProcessor.Failure(null)
room.givenSendMediaResult(Result.failure(failure)) val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
Result.failure(failure)
}
val room = FakeMatrixRoom(
sendMediaResult = sendMediaResult,
)
val presenter = createAttachmentsPreviewPresenter(room = room) val presenter = createAttachmentsPreviewPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -90,7 +100,7 @@ class AttachmentsPreviewPresenterTest {
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing) assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
val failureState = awaitItem() val failureState = awaitItem()
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure)) assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
assertThat(room.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isCalledOnce()
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState) failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
val clearedState = awaitItem() val clearedState = awaitItem()
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle) assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle)

30
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenterTest.kt

@ -22,11 +22,14 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -81,7 +84,12 @@ class ReportMessagePresenterTest {
@Test @Test
fun `presenter - handle successful report and block user`() = runTest { fun `presenter - handle successful report and block user`() = runTest {
val room = FakeMatrixRoom() val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
Result.success(Unit)
}
val room = FakeMatrixRoom(
reportContentResult = reportContentResult
)
val presenter = createReportMessagePresenter(matrixRoom = room) val presenter = createReportMessagePresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -92,13 +100,18 @@ class ReportMessagePresenterTest {
initialState.eventSink(ReportMessageEvents.Report) initialState.eventSink(ReportMessageEvents.Report)
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
assertThat(room.reportedContentCount).isEqualTo(1) reportContentResult.assertions().isCalledOnce()
} }
} }
@Test @Test
fun `presenter - handle successful report`() = runTest { fun `presenter - handle successful report`() = runTest {
val room = FakeMatrixRoom() val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
Result.success(Unit)
}
val room = FakeMatrixRoom(
reportContentResult = reportContentResult
)
val presenter = createReportMessagePresenter(matrixRoom = room) val presenter = createReportMessagePresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -107,15 +120,18 @@ class ReportMessagePresenterTest {
initialState.eventSink(ReportMessageEvents.Report) initialState.eventSink(ReportMessageEvents.Report)
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
assertThat(room.reportedContentCount).isEqualTo(1) reportContentResult.assertions().isCalledOnce()
} }
} }
@Test @Test
fun `presenter - handle failed report`() = runTest { fun `presenter - handle failed report`() = runTest {
val room = FakeMatrixRoom().apply { val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
givenReportContentResult(Result.failure(Exception("Failed to report content"))) Result.failure(Exception("Failed to report content"))
} }
val room = FakeMatrixRoom(
reportContentResult = reportContentResult
)
val presenter = createReportMessagePresenter(matrixRoom = room) val presenter = createReportMessagePresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -125,7 +141,7 @@ class ReportMessagePresenterTest {
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
val resultState = awaitItem() val resultState = awaitItem()
assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java) assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java)
assertThat(room.reportedContentCount).isEqualTo(1) reportContentResult.assertions().isCalledOnce()
resultState.eventSink(ReportMessageEvents.ClearError) resultState.eventSink(ReportMessageEvents.ClearError)
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().result).isInstanceOf(AsyncAction.Uninitialized::class.java)

142
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt

@ -43,6 +43,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
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.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.ImageInfo
@ -67,6 +68,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.A_USER_ID_3 import io.element.android.libraries.matrix.test.A_USER_ID_3
import io.element.android.libraries.matrix.test.A_USER_ID_4 import io.element.android.libraries.matrix.test.A_USER_ID_4
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@ -297,7 +299,13 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - send message with rich text enabled`() = runTest { fun `present - send message with rich text enabled`() = runTest {
val presenter = createPresenter(this) val presenter = createPresenter(
coroutineScope = this,
room = FakeMatrixRoom(
sendMessageResult = { _, _, _ -> Result.success(Unit) },
typingNoticeResult = { Result.success(Unit) }
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
val state = presenter.present() val state = presenter.present()
remember(state, state.textEditorState.messageHtml()) { state } remember(state, state.textEditorState.messageHtml()) { state }
@ -324,7 +332,14 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - send message with plain text enabled`() = runTest { fun `present - send message with plain text enabled`() = runTest {
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("") }) val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("") })
val presenter = createPresenter(this, isRichTextEditorEnabled = false) val presenter = createPresenter(
coroutineScope = this,
isRichTextEditorEnabled = false,
room = FakeMatrixRoom(
sendMessageResult = { _, _, _ -> Result.success(Unit) },
typingNoticeResult = { Result.success(Unit) }
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
val state = presenter.present() val state = presenter.present()
val messageMarkdown = state.textEditorState.messageMarkdown(permalinkBuilder) val messageMarkdown = state.textEditorState.messageMarkdown(permalinkBuilder)
@ -358,7 +373,10 @@ class MessageComposerPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.editMessageLambda = editMessageLambda this.editMessageLambda = editMessageLambda
} }
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline) val fakeMatrixRoom = FakeMatrixRoom(
liveTimeline = timeline,
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter( val presenter = createPresenter(
this, this,
fakeMatrixRoom, fakeMatrixRoom,
@ -407,7 +425,10 @@ class MessageComposerPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.editMessageLambda = editMessageLambda this.editMessageLambda = editMessageLambda
} }
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline) val fakeMatrixRoom = FakeMatrixRoom(
liveTimeline = timeline,
typingNoticeResult = { Result.success(Unit) },
)
val presenter = createPresenter( val presenter = createPresenter(
this, this,
fakeMatrixRoom, fakeMatrixRoom,
@ -456,7 +477,10 @@ class MessageComposerPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.replyMessageLambda = replyMessageLambda this.replyMessageLambda = replyMessageLambda
} }
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline) val fakeMatrixRoom = FakeMatrixRoom(
liveTimeline = timeline,
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter( val presenter = createPresenter(
this, this,
fakeMatrixRoom, fakeMatrixRoom,
@ -524,7 +548,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Pick image from gallery`() = runTest { fun `present - Pick image from gallery`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
pickerProvider.givenMimeType(MimeTypes.Images) pickerProvider.givenMimeType(MimeTypes.Images)
mediaPreProcessor.givenResult( mediaPreProcessor.givenResult(
@ -557,7 +583,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Pick video from gallery`() = runTest { fun `present - Pick video from gallery`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
pickerProvider.givenMimeType(MimeTypes.Videos) pickerProvider.givenMimeType(MimeTypes.Videos)
mediaPreProcessor.givenResult( mediaPreProcessor.givenResult(
@ -607,13 +635,17 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Pick file from storage`() = runTest { fun `present - Pick file from storage`() = runTest {
val room = FakeMatrixRoom() val sendMediaResult = lambdaRecorder { _: ProgressCallback? ->
room.givenProgressCallbackValues( Result.success(FakeMediaUploadHandler())
listOf( }
val room = FakeMatrixRoom(
progressCallbackValues = listOf(
Pair(0, 10), Pair(0, 10),
Pair(5, 10), Pair(5, 10),
Pair(10, 10) Pair(10, 10)
) ),
sendMediaResult = sendMediaResult,
typingNoticeResult = { Result.success(Unit) }
) )
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -629,13 +661,15 @@ class MessageComposerPresenterTest {
assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f)) assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f))
val sentState = awaitItem() val sentState = awaitItem()
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None) assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
assertThat(room.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
} }
} }
@Test @Test
fun `present - create poll`() = runTest { fun `present - create poll`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -652,7 +686,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - share location`() = runTest { fun `present - share location`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -669,7 +705,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Take photo`() = runTest { fun `present - Take photo`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
val presenter = createPresenter( val presenter = createPresenter(
this, this,
@ -689,7 +727,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Take photo with permission request`() = runTest { fun `present - Take photo with permission request`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val permissionPresenter = FakePermissionsPresenter() val permissionPresenter = FakePermissionsPresenter()
val presenter = createPresenter( val presenter = createPresenter(
this, this,
@ -714,7 +754,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Record video`() = runTest { fun `present - Record video`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
val presenter = createPresenter( val presenter = createPresenter(
this, this,
@ -734,7 +776,9 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Record video with permission request`() = runTest { fun `present - Record video with permission request`() = runTest {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
)
val permissionPresenter = FakePermissionsPresenter() val permissionPresenter = FakePermissionsPresenter()
val presenter = createPresenter( val presenter = createPresenter(
this, this,
@ -759,9 +803,10 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - Uploading media failure can be recovered from`() = runTest { fun `present - Uploading media failure can be recovered from`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenSendMediaResult(Result.failure(Exception())) sendMediaResult = { Result.failure(Exception()) },
} typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(this, room = room) val presenter = createPresenter(this, room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -842,15 +887,17 @@ class MessageComposerPresenterTest {
val invitedUser = aRoomMember(userId = A_USER_ID_3, membership = RoomMembershipState.INVITE) val invitedUser = aRoomMember(userId = A_USER_ID_3, membership = RoomMembershipState.INVITE)
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN) val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN) val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
var canUserTriggerRoomNotificationResult = true
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
isDirect = false, isDirect = false,
canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) },
typingNoticeResult = { Result.success(Unit) }
).apply { ).apply {
givenRoomMembersState( givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf(currentUser, invitedUser, bob, david), persistentListOf(currentUser, invitedUser, bob, david),
) )
) )
givenCanTriggerRoomNotification(Result.success(true))
} }
val flagsService = FakeFeatureFlagService( val flagsService = FakeFeatureFlagService(
mapOf( mapOf(
@ -890,13 +937,10 @@ class MessageComposerPresenterTest {
assertThat(awaitItem().memberSuggestions).isEmpty() assertThat(awaitItem().memberSuggestions).isEmpty()
// If user has no permission to send `@room` mentions, `RoomMemberSuggestion.Room` is not returned // If user has no permission to send `@room` mentions, `RoomMemberSuggestion.Room` is not returned
room.givenCanTriggerRoomNotification(Result.success(false)) canUserTriggerRoomNotificationResult = false
initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, ""))) initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "")))
assertThat(awaitItem().memberSuggestions) assertThat(awaitItem().memberSuggestions)
.containsExactly(ResolvedMentionSuggestion.Member(bob), ResolvedMentionSuggestion.Member(david)) .containsExactly(ResolvedMentionSuggestion.Member(bob), ResolvedMentionSuggestion.Member(david))
// If room is a DM, `RoomMemberSuggestion.Room` is not returned
room.givenCanTriggerRoomNotification(Result.success(true))
} }
} }
@ -910,13 +954,14 @@ class MessageComposerPresenterTest {
isDirect = true, isDirect = true,
activeMemberCount = 2, activeMemberCount = 2,
isEncrypted = true, isEncrypted = true,
canUserTriggerRoomNotificationResult = { Result.success(true) },
typingNoticeResult = { Result.success(Unit) }
).apply { ).apply {
givenRoomMembersState( givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
persistentListOf(currentUser, invitedUser, bob, david), persistentListOf(currentUser, invitedUser, bob, david),
) )
) )
givenCanTriggerRoomNotification(Result.success(true))
} }
val flagsService = FakeFeatureFlagService( val flagsService = FakeFeatureFlagService(
mapOf( mapOf(
@ -973,7 +1018,14 @@ class MessageComposerPresenterTest {
this.replyMessageLambda = replyMessageLambda this.replyMessageLambda = replyMessageLambda
this.editMessageLambda = editMessageLambda this.editMessageLambda = editMessageLambda
} }
val room = FakeMatrixRoom(liveTimeline = timeline) val sendMessageResult = lambdaRecorder { _: String, _: String?, _: List<Mention> ->
Result.success(Unit)
}
val room = FakeMatrixRoom(
liveTimeline = timeline,
sendMessageResult = sendMessageResult,
typingNoticeResult = { Result.success(Unit) }
)
val presenter = createPresenter(room = room, coroutineScope = this) val presenter = createPresenter(room = room, coroutineScope = this)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -993,7 +1045,8 @@ class MessageComposerPresenterTest {
advanceUntilIdle() advanceUntilIdle()
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID))) sendMessageResult.assertions().isCalledOnce()
.with(value(A_MESSAGE), any(), value(listOf(Mention.User(A_USER_ID))))
// Check intentional mentions on reply sent // Check intentional mentions on reply sent
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode())) initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
@ -1049,22 +1102,32 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - handle typing notice event`() = runTest { fun `present - handle typing notice event`() = runTest {
val room = FakeMatrixRoom() val typingNoticeResult = lambdaRecorder<Boolean, Result<Unit>> { Result.success(Unit) }
val room = FakeMatrixRoom(
typingNoticeResult = typingNoticeResult
)
val presenter = createPresenter(room = room, coroutineScope = this) val presenter = createPresenter(room = room, coroutineScope = this)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(room.typingRecord).isEmpty() typingNoticeResult.assertions().isNeverCalled()
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true)) initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false)) initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
assertThat(room.typingRecord).isEqualTo(listOf(true, false)) typingNoticeResult.assertions().isCalledExactly(2)
.withSequence(
listOf(value(true)),
listOf(value(false)),
)
} }
} }
@Test @Test
fun `present - handle typing notice event when sending typing notice is disabled`() = runTest { fun `present - handle typing notice event when sending typing notice is disabled`() = runTest {
val room = FakeMatrixRoom() val typingNoticeResult = lambdaRecorder<Boolean, Result<Unit>> { Result.success(Unit) }
val room = FakeMatrixRoom(
typingNoticeResult = typingNoticeResult
)
val store = InMemorySessionPreferencesStore( val store = InMemorySessionPreferencesStore(
isSendTypingNotificationsEnabled = false isSendTypingNotificationsEnabled = false
) )
@ -1073,10 +1136,10 @@ class MessageComposerPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(room.typingRecord).isEmpty() typingNoticeResult.assertions().isNeverCalled()
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true)) initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false)) initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
assertThat(room.typingRecord).isEmpty() typingNoticeResult.assertions().isNeverCalled()
} }
} }
@ -1215,7 +1278,10 @@ class MessageComposerPresenterTest {
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
this.loadReplyDetailsLambda = loadReplyDetailsLambda this.loadReplyDetailsLambda = loadReplyDetailsLambda
} }
val room = FakeMatrixRoom(liveTimeline = timeline) val room = FakeMatrixRoom(
liveTimeline = timeline,
typingNoticeResult = { Result.success(Unit) },
)
val permalinkBuilder = FakePermalinkBuilder() val permalinkBuilder = FakePermalinkBuilder()
val presenter = createPresenter( val presenter = createPresenter(
room = room, room = room,
@ -1352,7 +1418,9 @@ class MessageComposerPresenterTest {
private fun createPresenter( private fun createPresenter(
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
room: MatrixRoom = FakeMatrixRoom(), room: MatrixRoom = FakeMatrixRoom(
typingNoticeResult = { Result.success(Unit) }
),
pickerProvider: PickerProvider = this.pickerProvider, pickerProvider: PickerProvider = this.pickerProvider,
featureFlagService: FeatureFlagService = this.featureFlagService, featureFlagService: FeatureFlagService = this.featureFlagService,
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),

31
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt

@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.test.A_UNIQUE_ID
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.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -38,9 +39,9 @@ class TimelineControllerTest {
val liveTimeline = FakeTimeline(name = "live") val liveTimeline = FakeTimeline(name = "live")
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
) )
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {
@ -68,8 +69,17 @@ class TimelineControllerTest {
val liveTimeline = FakeTimeline(name = "live") val liveTimeline = FakeTimeline(name = "live")
val detachedTimeline1 = FakeTimeline(name = "detached 1") val detachedTimeline1 = FakeTimeline(name = "detached 1")
val detachedTimeline2 = FakeTimeline(name = "detached 2") val detachedTimeline2 = FakeTimeline(name = "detached 2")
var callNumber = 0
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
timelineFocusedOnEventResult = {
callNumber++
when (callNumber) {
1 -> Result.success(detachedTimeline1)
2 -> Result.success(detachedTimeline2)
else -> lambdaError()
}
}
) )
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
@ -77,7 +87,6 @@ class TimelineControllerTest {
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state).isEqualTo(liveTimeline) assertThat(state).isEqualTo(liveTimeline)
} }
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline1))
sut.focusOnEvent(AN_EVENT_ID) sut.focusOnEvent(AN_EVENT_ID)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state).isEqualTo(detachedTimeline1) assertThat(state).isEqualTo(detachedTimeline1)
@ -85,7 +94,6 @@ class TimelineControllerTest {
assertThat(detachedTimeline1.closeCounter).isEqualTo(0) assertThat(detachedTimeline1.closeCounter).isEqualTo(0)
assertThat(detachedTimeline2.closeCounter).isEqualTo(0) assertThat(detachedTimeline2.closeCounter).isEqualTo(0)
// Focus on another event should close the previous detached timeline // Focus on another event should close the previous detached timeline
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline2))
sut.focusOnEvent(AN_EVENT_ID) sut.focusOnEvent(AN_EVENT_ID)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state).isEqualTo(detachedTimeline2) assertThat(state).isEqualTo(detachedTimeline2)
@ -117,11 +125,10 @@ class TimelineControllerTest {
val liveTimeline = FakeTimeline(name = "live") val liveTimeline = FakeTimeline(name = "live")
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
) )
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state).isEqualTo(liveTimeline) assertThat(state).isEqualTo(liveTimeline)
@ -168,9 +175,9 @@ class TimelineControllerTest {
sendMessageLambda = lambdaForDetached sendMessageLambda = lambdaForDetached
} }
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
) )
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {
sut.focusOnEvent(AN_EVENT_ID) sut.focusOnEvent(AN_EVENT_ID)
@ -193,9 +200,9 @@ class TimelineControllerTest {
val liveTimeline = FakeTimeline(name = "live") val liveTimeline = FakeTimeline(name = "live")
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
) )
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {

28
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt

@ -133,7 +133,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
) )
) )
) )
val room = FakeMatrixRoom(liveTimeline = timeline) val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
)
val sessionPreferencesStore = InMemorySessionPreferencesStore(isSendPublicReadReceiptsEnabled = false) val sessionPreferencesStore = InMemorySessionPreferencesStore(isSendPublicReadReceiptsEnabled = false)
val presenter = createTimelinePresenter( val presenter = createTimelinePresenter(
timeline = timeline, timeline = timeline,
@ -482,9 +485,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
) )
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
).apply { timelineFocusedOnEventResult = { Result.success(detachedTimeline) },
givenTimelineFocusedOnEventResult(Result.success(detachedTimeline)) canUserSendMessageResult = { _, _ -> Result.success(true) },
} )
val presenter = createTimelinePresenter( val presenter = createTimelinePresenter(
room = room, room = room,
) )
@ -529,6 +532,7 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
) )
) )
), ),
canUserSendMessageResult = { _, _ -> Result.success(true) },
), ),
timelineItemIndexer = timelineItemIndexer, timelineItemIndexer = timelineItemIndexer,
) )
@ -551,9 +555,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
liveTimeline = FakeTimeline( liveTimeline = FakeTimeline(
timelineItems = flowOf(emptyList()), timelineItems = flowOf(emptyList()),
), ),
).apply { timelineFocusedOnEventResult = { Result.failure(Throwable("An error")) },
givenTimelineFocusedOnEventResult(Result.failure(Throwable("An error"))) canUserSendMessageResult = { _, _ -> Result.success(true) },
}, )
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -594,7 +598,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
) )
) )
) )
val room = FakeMatrixRoom(liveTimeline = timeline).apply { val room = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Unknown) givenRoomMembersState(MatrixRoomMembersState.Unknown)
} }
@ -626,7 +633,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
private fun TestScope.createTimelinePresenter( private fun TestScope.createTimelinePresenter(
timeline: Timeline = FakeTimeline(), timeline: Timeline = FakeTimeline(),
room: FakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline), room: FakeMatrixRoom = FakeMatrixRoom(
liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) }
),
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(), timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),

26
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt

@ -28,7 +28,9 @@ import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
@ -45,6 +47,7 @@ import io.element.android.libraries.textcomposer.model.VoiceMessageState
import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder
import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -63,7 +66,10 @@ class VoiceMessageComposerPresenterTest {
recordingDuration = RECORDING_DURATION recordingDuration = RECORDING_DURATION
) )
private val analyticsService = FakeAnalyticsService() private val analyticsService = FakeAnalyticsService()
private val matrixRoom = FakeMatrixRoom() private val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> { Result.success(FakeMediaUploadHandler()) }
private val matrixRoom = FakeMatrixRoom(
sendMediaResult = sendMediaResult
)
private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() } private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() }
private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom) private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom)
private val messageComposerContext = FakeMessageComposerContext() private val messageComposerContext = FakeMessageComposerContext()
@ -295,7 +301,7 @@ class VoiceMessageComposerPresenterTest {
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
assertThat(matrixRoom.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
testPauseAndDestroy(finalState) testPauseAndDestroy(finalState)
@ -346,7 +352,7 @@ class VoiceMessageComposerPresenterTest {
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
assertThat(matrixRoom.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
testPauseAndDestroy(finalState) testPauseAndDestroy(finalState)
@ -369,7 +375,7 @@ class VoiceMessageComposerPresenterTest {
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
assertThat(matrixRoom.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
testPauseAndDestroy(finalState) testPauseAndDestroy(finalState)
@ -393,7 +399,7 @@ class VoiceMessageComposerPresenterTest {
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true)) assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true))
assertThat(matrixRoom.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isNeverCalled()
assertThat(analyticsService.trackedErrors).hasSize(0) assertThat(analyticsService.trackedErrors).hasSize(0)
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0)
@ -418,13 +424,13 @@ class VoiceMessageComposerPresenterTest {
ensureAllEventsConsumed() ensureAllEventsConsumed()
assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState()) assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState())
assertThat(matrixRoom.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isNeverCalled()
mediaPreProcessor.givenAudioResult() mediaPreProcessor.givenAudioResult()
previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
assertThat(matrixRoom.sendMediaCount).isEqualTo(1) sendMediaResult.assertions().isCalledOnce()
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
testPauseAndDestroy(finalState) testPauseAndDestroy(finalState)
@ -461,7 +467,7 @@ class VoiceMessageComposerPresenterTest {
assertThat(showSendFailureDialog).isFalse() assertThat(showSendFailureDialog).isFalse()
} }
assertThat(matrixRoom.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isNeverCalled()
testPauseAndDestroy(finalState) testPauseAndDestroy(finalState)
} }
} }
@ -477,7 +483,7 @@ class VoiceMessageComposerPresenterTest {
initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
assertThat(matrixRoom.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isNeverCalled()
assertThat(analyticsService.trackedErrors).hasSize(1) assertThat(analyticsService.trackedErrors).hasSize(1)
voiceRecorder.assertCalls(started = 0) voiceRecorder.assertCalls(started = 0)
@ -496,7 +502,7 @@ class VoiceMessageComposerPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start))
assertThat(matrixRoom.sendMediaCount).isEqualTo(0) sendMediaResult.assertions().isNeverCalled()
assertThat(analyticsService.trackedErrors).containsExactly( assertThat(analyticsService.trackedErrors).containsExactly(
VoiceMessageException.PermissionMissing(message = "Expected permission to record but none", cause = exception) VoiceMessageException.PermissionMissing(message = "Expected permission to record but none", cause = exception)
) )

54
features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt

@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
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.SavePollInvocation
import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService
@ -51,9 +50,9 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class) class CreatePollPresenterTest { @OptIn(ExperimentalCoroutinesApi::class)
@get:Rule class CreatePollPresenterTest {
val warmUpRule = WarmUpRule() @get:Rule val warmUpRule = WarmUpRule()
private val pollEventId = AN_EVENT_ID private val pollEventId = AN_EVENT_ID
private var navUpInvocationsCount = 0 private var navUpInvocationsCount = 0
@ -128,7 +127,13 @@ import org.junit.Test
@Test @Test
fun `create poll sends a poll start event`() = runTest { fun `create poll sends a poll start event`() = runTest {
val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) val createPollResult = lambdaRecorder<String, List<String>, Int, PollKind, Result<Unit>> { _, _, _, _ -> Result.success(Unit) }
val presenter = createCreatePollPresenter(
room = FakeMatrixRoom(
createPollResult = createPollResult
),
mode = CreatePollMode.NewPoll,
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -139,15 +144,13 @@ import org.junit.Test
skipItems(3) skipItems(3)
initial.eventSink(CreatePollEvents.Save) initial.eventSink(CreatePollEvents.Save)
delay(1) // Wait for the coroutine to finish delay(1) // Wait for the coroutine to finish
assertThat(fakeMatrixRoom.createPollInvocations.size).isEqualTo(1) createPollResult.assertions().isCalledOnce()
assertThat(fakeMatrixRoom.createPollInvocations.last()).isEqualTo( .with(
SavePollInvocation( value("A question?"),
question = "A question?", value(listOf("Answer 1", "Answer 2")),
answers = listOf("Answer 1", "Answer 2"), value(1),
maxSelections = 1, value(PollKind.Disclosed),
pollKind = PollKind.Disclosed
) )
)
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2) assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2)
assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo( assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo(
Composer( Composer(
@ -170,8 +173,15 @@ import org.junit.Test
@Test @Test
fun `when poll creation fails, error is tracked`() = runTest { fun `when poll creation fails, error is tracked`() = runTest {
val error = Exception("cause") val error = Exception("cause")
fakeMatrixRoom.givenCreatePollResult(Result.failure(error)) val createPollResult = lambdaRecorder<String, List<String>, Int, PollKind, Result<Unit>> { _, _, _, _ ->
val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll) Result.failure(error)
}
val presenter = createCreatePollPresenter(
room = FakeMatrixRoom(
createPollResult = createPollResult
),
mode = CreatePollMode.NewPoll,
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -180,7 +190,7 @@ import org.junit.Test
awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2")) awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2"))
awaitItem().eventSink(CreatePollEvents.Save) awaitItem().eventSink(CreatePollEvents.Save)
delay(1) // Wait for the coroutine to finish delay(1) // Wait for the coroutine to finish
assertThat(fakeMatrixRoom.createPollInvocations).hasSize(1) createPollResult.assertions().isCalledOnce()
assertThat(fakeAnalyticsService.capturedEvents).isEmpty() assertThat(fakeAnalyticsService.capturedEvents).isEmpty()
assertThat(fakeAnalyticsService.trackedErrors).hasSize(1) assertThat(fakeAnalyticsService.trackedErrors).hasSize(1)
assertThat(fakeAnalyticsService.trackedErrors).containsExactly( assertThat(fakeAnalyticsService.trackedErrors).containsExactly(
@ -252,14 +262,22 @@ import org.junit.Test
@Test @Test
fun `when edit poll fails, error is tracked`() = runTest { fun `when edit poll fails, error is tracked`() = runTest {
val error = Exception("cause") val error = Exception("cause")
val editPollResult = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind ->
Result.failure<Unit>(error)
}
val presenter = createCreatePollPresenter(
room = FakeMatrixRoom(
editPollResult = editPollResult,
liveTimeline = timeline,
),
mode = CreatePollMode.EditPoll(pollEventId),
)
val editPollLambda = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind -> val editPollLambda = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind ->
Result.failure<Unit>(error) Result.failure<Unit>(error)
} }
timeline.apply { timeline.apply {
this.editPollLambda = editPollLambda this.editPollLambda = editPollLambda
} }
fakeMatrixRoom.givenEditPollResult(Result.failure(error))
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {

26
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt

@ -17,11 +17,15 @@
package io.element.android.features.roomdetails package io.element.android.features.roomdetails
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
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.room.aRoomInfo
import io.element.android.tests.testutils.lambda.lambdaError
fun aMatrixRoom( fun aMatrixRoom(
roomId: RoomId = A_ROOM_ID, roomId: RoomId = A_ROOM_ID,
@ -34,6 +38,16 @@ fun aMatrixRoom(
isDirect: Boolean = false, isDirect: Boolean = false,
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
emitRoomInfo: Boolean = false, emitRoomInfo: Boolean = false,
canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
userDisplayNameResult: () -> Result<String?> = { lambdaError() },
userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
setNameResult: (String) -> Result<Unit> = { lambdaError() },
setTopicResult: (String) -> Result<Unit> = { lambdaError() },
updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> lambdaError() },
removeAvatarResult: () -> Result<Unit> = { lambdaError() },
canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
) = FakeMatrixRoom( ) = FakeMatrixRoom(
roomId = roomId, roomId = roomId,
displayName = displayName, displayName = displayName,
@ -42,7 +56,17 @@ fun aMatrixRoom(
isEncrypted = isEncrypted, isEncrypted = isEncrypted,
isPublic = isPublic, isPublic = isPublic,
isDirect = isDirect, isDirect = isDirect,
notificationSettingsService = notificationSettingsService notificationSettingsService = notificationSettingsService,
canInviteResult = canInviteResult,
canSendStateResult = canSendStateResult,
userDisplayNameResult = userDisplayNameResult,
userAvatarUrlResult = userAvatarUrlResult,
setNameResult = setNameResult,
setTopicResult = setTopicResult,
updateAvatarResult = updateAvatarResult,
removeAvatarResult = removeAvatarResult,
canUserJoinCallResult = canUserJoinCallResult,
getUpdatedMemberResult = getUpdatedMemberResult,
).apply { ).apply {
if (emitRoomInfo) { if (emitRoomInfo) {
givenRoomInfo( givenRoomInfo(

283
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt

@ -53,6 +53,9 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.FakeLifecycleOwner import io.element.android.tests.testutils.FakeLifecycleOwner
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilPredicate
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
import io.element.android.tests.testutils.withFakeLifecycleOwner import io.element.android.tests.testutils.withFakeLifecycleOwner
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -110,7 +113,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state is created from room if roomInfo is null`() = runTest { fun `present - initial state is created from room if roomInfo is null`() = runTest {
val room = aMatrixRoom() val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
@ -128,7 +135,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state is updated with roomInfo if it exists`() = runTest { fun `present - initial state is updated with roomInfo if it exists`() = runTest {
val roomInfo = aRoomInfo(name = "A room name", topic = "A topic", avatarUrl = "https://matrix.org/avatar.jpg") val roomInfo = aRoomInfo(name = "A room name", topic = "A topic", avatarUrl = "https://matrix.org/avatar.jpg")
val room = aMatrixRoom().apply { val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
).apply {
givenRoomInfo(roomInfo) givenRoomInfo(roomInfo)
} }
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
@ -145,7 +156,12 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state with no room name`() = runTest { fun `present - initial state with no room name`() = runTest {
val room = aMatrixRoom(displayName = "") val room = aMatrixRoom(
displayName = "",
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
@ -162,6 +178,16 @@ class RoomDetailsPresenterTest {
val room = aMatrixRoom( val room = aMatrixRoom(
isEncrypted = true, isEncrypted = true,
isDirect = true, isDirect = true,
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
getUpdatedMemberResult = { userId ->
when (userId) {
A_SESSION_ID -> Result.success(myRoomMember)
A_USER_ID_2 -> Result.success(otherRoomMember)
else -> lambdaError()
}
},
).apply { ).apply {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember) val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
@ -181,9 +207,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when user can invite others to room`() = runTest { fun `present - initial state when user can invite others to room`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanInviteResult(Result.success(true)) canInviteResult = { Result.success(true) },
} canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
presenter.test { presenter.test {
// Initially false // Initially false
@ -197,9 +225,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when user can not invite others to room`() = runTest { fun `present - initial state when user can not invite others to room`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanInviteResult(Result.success(false)) canInviteResult = { Result.success(false) },
} canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
assertThat(awaitItem().canInvite).isFalse() assertThat(awaitItem().canInvite).isFalse()
@ -210,9 +240,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when canInvite errors`() = runTest { fun `present - initial state when canInvite errors`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanInviteResult(Result.failure(Throwable("Whoops"))) canInviteResult = { Result.failure(Throwable("Whoops")) },
} canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
assertThat(awaitItem().canInvite).isFalse() assertThat(awaitItem().canInvite).isFalse()
@ -223,12 +255,18 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when user can edit one attribute`() = runTest { fun `present - initial state when user can edit one attribute`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) when (stateEventType) {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp"))) StateEventType.ROOM_TOPIC -> Result.success(true)
givenCanInviteResult(Result.success(false)) StateEventType.ROOM_NAME -> Result.success(false)
} StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Whelp"))
else -> lambdaError()
}
},
canInviteResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
// Initially false // Initially false
@ -247,14 +285,26 @@ class RoomDetailsPresenterTest {
val room = aMatrixRoom( val room = aMatrixRoom(
isEncrypted = true, isEncrypted = true,
isDirect = true, isDirect = true,
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_TOPIC -> Result.success(true)
StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_AVATAR -> Result.success(true)
else -> lambdaError()
}
},
canInviteResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) },
getUpdatedMemberResult = { userId ->
when (userId) {
A_SESSION_ID -> Result.success(myRoomMember)
A_USER_ID_2 -> Result.success(otherRoomMember)
else -> lambdaError()
}
},
).apply { ).apply {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember) val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
givenCanInviteResult(Result.success(false))
} }
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
@ -278,12 +328,28 @@ class RoomDetailsPresenterTest {
isEncrypted = true, isEncrypted = true,
isDirect = true, isDirect = true,
topic = null, topic = null,
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true)
else -> lambdaError()
}
},
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
getUpdatedMemberResult = { userId ->
when (userId) {
A_SESSION_ID -> Result.success(myRoomMember)
A_USER_ID_2 -> Result.success(otherRoomMember)
else -> lambdaError()
}
},
).apply { ).apply {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember) val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
} }
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
skipItems(1) skipItems(1)
@ -297,12 +363,20 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when user can edit all attributes`() = runTest { fun `present - initial state when user can edit all attributes`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true)) when (stateEventType) {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) StateEventType.ROOM_TOPIC -> Result.success(true)
givenCanInviteResult(Result.success(false)) StateEventType.ROOM_NAME -> Result.success(true)
} StateEventType.ROOM_AVATAR -> Result.success(true)
else -> lambdaError()
}
},
canInviteResult = {
Result.success(false)
},
canUserJoinCallResult = { Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
// Initially false // Initially false
@ -316,12 +390,20 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when user can edit no attributes`() = runTest { fun `present - initial state when user can edit no attributes`() = runTest {
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false)) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) when (stateEventType) {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false)) StateEventType.ROOM_TOPIC -> Result.success(false)
givenCanInviteResult(Result.success(false)) StateEventType.ROOM_NAME -> Result.success(false)
} StateEventType.ROOM_AVATAR -> Result.success(false)
else -> lambdaError()
}
},
canInviteResult = {
Result.success(false)
},
canUserJoinCallResult = { Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
// Initially false, and no further events // Initially false, and no further events
@ -333,11 +415,21 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - topic state is hidden when no topic and user has no permission`() = runTest { fun `present - topic state is hidden when no topic and user has no permission`() = runTest {
val room = aMatrixRoom(topic = null).apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false)) topic = null,
givenCanInviteResult(Result.success(false)) canSendStateResult = { _, stateEventType ->
} when (stateEventType) {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.success(false)
else -> lambdaError()
}
},
canInviteResult = {
Result.success(false)
},
canUserJoinCallResult = { Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
// The initial state is "hidden" and no further state changes happen // The initial state is "hidden" and no further state changes happen
@ -349,12 +441,23 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest { fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest {
val room = aMatrixRoom(topic = null).apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) topic = null,
givenCanInviteResult(Result.success(false)) canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true)
else -> lambdaError()
}
},
canInviteResult = {
Result.success(false)
},
canUserJoinCallResult = { Result.success(true) },
).apply {
givenRoomInfo(aRoomInfo(topic = null)) givenRoomInfo(aRoomInfo(topic = null))
} }
val presenter = createRoomDetailsPresenter(room) val presenter = createRoomDetailsPresenter(room)
presenter.test { presenter.test {
// Ignore the initial state // Ignore the initial state
@ -370,7 +473,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - leave room event is passed on to leave room presenter`() = runTest { fun `present - leave room event is passed on to leave room presenter`() = runTest {
val leaveRoomPresenter = FakeLeaveRoomPresenter() val leaveRoomPresenter = FakeLeaveRoomPresenter()
val room = aMatrixRoom() val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter( val presenter = createRoomDetailsPresenter(
room = room, room = room,
leaveRoomPresenter = leaveRoomPresenter, leaveRoomPresenter = leaveRoomPresenter,
@ -379,7 +486,11 @@ class RoomDetailsPresenterTest {
presenter.test { presenter.test {
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom) awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId)) assertThat(leaveRoomPresenter.events).contains(
LeaveRoomEvent.ShowConfirmation(
room.roomId
)
)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
@ -389,33 +500,54 @@ class RoomDetailsPresenterTest {
fun `present - notification mode changes`() = runTest { fun `present - notification mode changes`() = runTest {
val leaveRoomPresenter = FakeLeaveRoomPresenter() val leaveRoomPresenter = FakeLeaveRoomPresenter()
val notificationSettingsService = FakeNotificationSettingsService() val notificationSettingsService = FakeNotificationSettingsService()
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val room = aMatrixRoom(
notificationSettingsService = notificationSettingsService,
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter( val presenter = createRoomDetailsPresenter(
room = room, room = room,
leaveRoomPresenter = leaveRoomPresenter, leaveRoomPresenter = leaveRoomPresenter,
notificationSettingsService = notificationSettingsService, notificationSettingsService = notificationSettingsService,
) )
presenter.test { presenter.test {
notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) notificationSettingsService.setRoomNotificationMode(
room.roomId,
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
)
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last() }.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@Test @Test
fun `present - mute room notifications`() = runTest { fun `present - mute room notifications`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val notificationSettingsService =
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) val room = aMatrixRoom(
notificationSettingsService = notificationSettingsService,
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(
room = room,
notificationSettingsService = notificationSettingsService
)
presenter.test { presenter.test {
awaitItem().eventSink(RoomDetailsEvent.MuteNotification) awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) { val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
}.last() }.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MUTE) assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.MUTE
)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@ -426,29 +558,50 @@ class RoomDetailsPresenterTest {
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
) )
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val room = aMatrixRoom(
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) notificationSettingsService = notificationSettingsService,
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(
room = room,
notificationSettingsService = notificationSettingsService
)
presenter.test { presenter.test {
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification) awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
}.last() }.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.ALL_MESSAGES
)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@Test @Test
fun `present - when set is favorite event is emitted, then the action is called`() = runTest { fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
val room = FakeMatrixRoom() val setIsFavoriteResult = lambdaRecorder<Boolean, Result<Unit>> { _ -> Result.success(Unit) }
val room = FakeMatrixRoom(
setIsFavoriteResult = setIsFavoriteResult,
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val presenter = createRoomDetailsPresenter(room = room, analyticsService = analyticsService) val presenter =
createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEvent.SetFavorite(true)) initialState.eventSink(RoomDetailsEvent.SetFavorite(true))
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true)) setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
initialState.eventSink(RoomDetailsEvent.SetFavorite(false)) initialState.eventSink(RoomDetailsEvent.SetFavorite(false))
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false)) setIsFavoriteResult.assertions().isCalledExactly(2)
.withSequence(
listOf(value(true)),
listOf(value(false)),
)
assertThat(analyticsService.capturedEvents).containsExactly( assertThat(analyticsService.capturedEvents).containsExactly(
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle), Interaction(name = Interaction.Name.MobileRoomFavouriteToggle),
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle) Interaction(name = Interaction.Name.MobileRoomFavouriteToggle)
@ -459,7 +612,11 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - changes in room info updates the is favorite flag`() = runTest { fun `present - changes in room info updates the is favorite flag`() = runTest {
val room = aMatrixRoom() val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room = room) val presenter = createRoomDetailsPresenter(room = room)
presenter.test { presenter.test {
room.givenRoomInfo(aRoomInfo(isFavorite = true)) room.givenRoomInfo(aRoomInfo(isFavorite = true))

169
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt

@ -39,6 +39,9 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenter
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
@ -98,6 +101,7 @@ class RoomDetailsEditPresenterTest {
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
rawName = A_ROOM_RAW_NAME, rawName = A_ROOM_RAW_NAME,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -120,11 +124,17 @@ class RoomDetailsEditPresenterTest {
@Test @Test
fun `present - sets canChangeName if user has permission`() = runTest { fun `present - sets canChangeName if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true)) avatarUrl = AN_AVATAR_URL,
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false)) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) when (stateEventType) {
} StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_AVATAR -> Result.success(false)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
else -> lambdaError()
}
},
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -144,11 +154,17 @@ class RoomDetailsEditPresenterTest {
@Test @Test
fun `present - sets canChangeAvatar if user has permission`() = runTest { fun `present - sets canChangeAvatar if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) avatarUrl = AN_AVATAR_URL,
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) when (stateEventType) {
} StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
else -> lambdaError()
}
}
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -168,11 +184,17 @@ class RoomDetailsEditPresenterTest {
@Test @Test
fun `present - sets canChangeTopic if user has permission`() = runTest { fun `present - sets canChangeTopic if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { val room = aMatrixRoom(
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) avatarUrl = AN_AVATAR_URL,
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops"))) canSendStateResult = { _, stateEventType ->
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) when (stateEventType) {
} StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Oops"))
StateEventType.ROOM_TOPIC -> Result.success(true)
else -> lambdaError()
}
}
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -197,6 +219,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -240,6 +263,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
fakePickerProvider.givenResult(anotherAvatarUri) fakePickerProvider.givenResult(anotherAvatarUri)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
@ -262,6 +286,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
fakePickerProvider.givenResult(anotherAvatarUri) fakePickerProvider.givenResult(anotherAvatarUri)
val fakePermissionsPresenter = FakePermissionsPresenter() val fakePermissionsPresenter = FakePermissionsPresenter()
@ -298,6 +323,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
fakePickerProvider.givenResult(roomAvatarUri) fakePickerProvider.givenResult(roomAvatarUri)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
@ -346,6 +372,7 @@ class RoomDetailsEditPresenterTest {
displayName = "fallback", displayName = "fallback",
avatarUrl = null, avatarUrl = null,
emitRoomInfo = true, emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
) )
fakePickerProvider.givenResult(roomAvatarUri) fakePickerProvider.givenResult(roomAvatarUri)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
@ -389,11 +416,18 @@ class RoomDetailsEditPresenterTest {
@Test @Test
fun `present - save changes room details if different`() = runTest { fun `present - save changes room details if different`() = runTest {
val setNameResult = lambdaRecorder { _: String -> Result.success(Unit) }
val setTopicResult = lambdaRecorder { _: String -> Result.success(Unit) }
val removeAvatarResult = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val room = aMatrixRoom( val room = aMatrixRoom(
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
setNameResult = setNameResult,
setTopicResult = setTopicResult,
removeAvatarResult = removeAvatarResult,
canSendStateResult = { _, _ -> Result.success(true) }
) )
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -405,16 +439,20 @@ class RoomDetailsEditPresenterTest {
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(5) skipItems(5)
assertThat(room.newName).isEqualTo("New name") setNameResult.assertions().isCalledOnce().with(value("New name"))
assertThat(room.newTopic).isEqualTo("New topic") setTopicResult.assertions().isCalledOnce().with(value("New topic"))
assertThat(room.newAvatarData).isNull() removeAvatarResult.assertions().isCalledOnce()
assertThat(room.removedAvatar).isTrue()
} }
} }
@Test @Test
fun `present - save doesn't change room details if they're the same trimmed`() = runTest { fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) val room = aMatrixRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -423,17 +461,18 @@ class RoomDetailsEditPresenterTest {
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name ")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name "))
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic ")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic "))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@Test @Test
fun `present - save doesn't change topic if it was unset and is now blank`() = runTest { fun `present - save doesn't change topic if it was unset and is now blank`() = runTest {
val room = aMatrixRoom(topic = null, displayName = "Name", avatarUrl = AN_AVATAR_URL) val room = aMatrixRoom(
topic = null,
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -441,17 +480,18 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(""))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@Test @Test
fun `present - save doesn't change name if it's now empty`() = runTest { fun `present - save doesn't change name if it's now empty`() = runTest {
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) val room = aMatrixRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -459,17 +499,20 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(""))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@Test @Test
fun `present - save processes and sets avatar when processor returns successfully`() = runTest { fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) val updateAvatarResult = lambdaRecorder { _: String, _: ByteArray -> Result.success(Unit) }
val room = aMatrixRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
updateAvatarResult = updateAvatarResult,
canSendStateResult = { _, _ -> Result.success(true) }
)
givenPickerReturnsFile() givenPickerReturnsFile()
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -478,17 +521,19 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(3) skipItems(4)
assertThat(room.newName).isNull() updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents))
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents)
assertThat(room.removedAvatar).isFalse()
} }
} }
@Test @Test
fun `present - save does not set avatar data if processor fails`() = runTest { fun `present - save does not set avatar data if processor fails`() = runTest {
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) val room = aMatrixRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(anotherAvatarUri) fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
@ -498,11 +543,7 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(2) skipItems(3)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
} }
} }
@ -514,9 +555,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
).apply { setNameResult = { Result.failure(Throwable("!")) },
givenSetNameResult(Result.failure(Throwable("!"))) canSendStateResult = { _, _ -> Result.success(true) }
} )
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name")) saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
} }
@ -527,9 +568,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
).apply { setTopicResult = { Result.failure(Throwable("!")) },
givenSetTopicResult(Result.failure(Throwable("!"))) canSendStateResult = { _, _ -> Result.success(true) }
} )
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic")) saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
} }
@ -540,9 +581,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
).apply { removeAvatarResult = { Result.failure(Throwable("!")) },
givenRemoveAvatarResult(Result.failure(Throwable("!"))) canSendStateResult = { _, _ -> Result.success(true) }
} )
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
} }
@ -554,18 +595,22 @@ class RoomDetailsEditPresenterTest {
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true, emitRoomInfo = true,
).apply { updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
givenUpdateAvatarResult(Result.failure(Throwable("!"))) canSendStateResult = { _, _ -> Result.success(true) }
} )
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
} }
@Test @Test
fun `present - CancelSaveChanges resets save action state`() = runTest { fun `present - CancelSaveChanges resets save action state`() = runTest {
givenPickerReturnsFile() givenPickerReturnsFile()
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { val room = aMatrixRoom(
givenSetTopicResult(Result.failure(Throwable("!"))) topic = "My topic",
} displayName = "Name",
avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room) val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -573,7 +618,7 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo"))
initialState.eventSink(RoomDetailsEditEvents.Save) initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(2) skipItems(3)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges) initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)

71
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTest.kt

@ -52,7 +52,10 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `member loading is done automatically on start, but is async`() = runTest { fun `member loading is done automatically on start, but is async`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
).apply {
// Needed to avoid discarding the loaded members as a partial and invalid result // Needed to avoid discarding the loaded members as a partial and invalid result
givenRoomInfo(aRoomInfo(joinedMembersCount = 2)) givenRoomInfo(aRoomInfo(joinedMembersCount = 2))
} }
@ -78,7 +81,12 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `open search`() = runTest { fun `open search`() = runTest {
val presenter = createPresenter() val presenter = createPresenter(
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -93,7 +101,12 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `search for something which is not found`() = runTest { fun `search for something which is not found`() = runTest {
val presenter = createPresenter() val presenter = createPresenter(
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -112,7 +125,12 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `search for something which is found`() = runTest { fun `search for something which is found`() = runTest {
val presenter = createPresenter() val presenter = createPresenter(
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -134,9 +152,10 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `present - asynchronously sets canInvite when user has correct power level`() = runTest { fun `present - asynchronously sets canInvite when user has correct power level`() = runTest {
val presenter = createPresenter( val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply { matrixRoom = FakeMatrixRoom(
givenCanInviteResult(Result.success(true)) canInviteResult = { Result.success(true) },
} updateMembersResult = { Result.success(Unit) }
)
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -150,9 +169,10 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest { fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest {
val presenter = createPresenter( val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply { matrixRoom = FakeMatrixRoom(
givenCanInviteResult(Result.success(false)) canInviteResult = { Result.success(false) },
} updateMembersResult = { Result.success(Unit) }
)
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -166,9 +186,10 @@ class RoomMemberListPresenterTest {
@Test @Test
fun `present - asynchronously sets canInvite when power level check fails`() = runTest { fun `present - asynchronously sets canInvite when power level check fails`() = runTest {
val presenter = createPresenter( val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply { matrixRoom = FakeMatrixRoom(
givenCanInviteResult(Result.failure(Throwable("Eek"))) canInviteResult = { Result.failure(Throwable("Eek")) },
} updateMembersResult = { Result.success(Unit) }
)
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -183,7 +204,14 @@ class RoomMemberListPresenterTest {
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest { fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
val navigator = FakeRoomMemberListNavigator() val navigator = FakeRoomMemberListNavigator()
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = false) val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = false)
val presenter = createPresenter(moderationPresenter = moderationPresenter, navigator = navigator) val presenter = createPresenter(
moderationPresenter = moderationPresenter,
navigator = navigator,
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -205,7 +233,14 @@ class RoomMemberListPresenterTest {
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply { val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
givenState(capturingState) givenState(capturingState)
} }
val presenter = createPresenter(moderationPresenter = moderationPresenter, navigator = navigator) val presenter = createPresenter(
moderationPresenter = moderationPresenter,
navigator = navigator,
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -236,10 +271,12 @@ private fun TestScope.createDataSource(
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
private fun TestScope.createPresenter( private fun TestScope.createPresenter(
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
matrixRoom: MatrixRoom = FakeMatrixRoom(), matrixRoom: MatrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) }
),
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers), roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(), moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator { } navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
) = RoomMemberListPresenter( ) = RoomMemberListPresenter(
room = matrixRoom, room = matrixRoom,
roomMemberListDataSource = roomMemberListDataSource, roomMemberListDataSource = roomMemberListDataSource,

88
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTest.kt

@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
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.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
@ -53,9 +54,11 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - returns the room member's data, then updates it if needed`() = runTest { fun `present - returns the room member's data, then updates it if needed`() = runTest {
val roomMember = aRoomMember(displayName = "Alice") val roomMember = aRoomMember(displayName = "Alice")
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenUserDisplayNameResult(Result.success("A custom name")) userDisplayNameResult = { Result.success("A custom name") },
givenUserAvatarUrlResult(Result.success("A custom avatar")) userAvatarUrlResult = { Result.success("A custom avatar") },
getUpdatedMemberResult = { Result.success(roomMember) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
} }
val presenter = createRoomMemberDetailsPresenter( val presenter = createRoomMemberDetailsPresenter(
@ -82,11 +85,14 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - will recover when retrieving room member details fails`() = runTest { fun `present - will recover when retrieving room member details fails`() = runTest {
val roomMember = aRoomMember(displayName = "Alice") val roomMember = aRoomMember(displayName = "Alice")
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenUserDisplayNameResult(Result.failure(Throwable())) userDisplayNameResult = { Result.failure(Throwable()) },
givenUserAvatarUrlResult(Result.failure(Throwable())) userAvatarUrlResult = { Result.failure(Throwable()) },
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
} }
val presenter = createRoomMemberDetailsPresenter( val presenter = createRoomMemberDetailsPresenter(
room = room, room = room,
roomMemberId = roomMember.userId roomMemberId = roomMember.userId
@ -105,9 +111,11 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - will fallback to original data if the updated data is null`() = runTest { fun `present - will fallback to original data if the updated data is null`() = runTest {
val roomMember = aRoomMember(displayName = "Alice") val roomMember = aRoomMember(displayName = "Alice")
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenUserDisplayNameResult(Result.success(null)) userDisplayNameResult = { Result.success(null) },
givenUserAvatarUrlResult(Result.success(null)) userAvatarUrlResult = { Result.success(null) },
getUpdatedMemberResult = { Result.success(roomMember) }
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember))) givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
} }
val presenter = createRoomMemberDetailsPresenter( val presenter = createRoomMemberDetailsPresenter(
@ -128,10 +136,11 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - will fallback to user profile if user is not a member of the room`() = runTest { fun `present - will fallback to user profile if user is not a member of the room`() = runTest {
val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl") val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl")
val room = aMatrixRoom().apply { val room = aMatrixRoom(
givenUserDisplayNameResult(Result.failure(Exception("Not a member!"))) userDisplayNameResult = { Result.failure(Exception("Not a member!")) },
givenUserAvatarUrlResult(Result.failure(Exception("Not a member!"))) userAvatarUrlResult = { Result.failure(Exception("Not a member!")) },
} getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
)
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetProfileResult(bobProfile.userId, Result.success(bobProfile)) givenGetProfileResult(bobProfile.userId, Result.success(bobProfile))
} }
@ -154,7 +163,13 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest { fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest {
val presenter = createRoomMemberDetailsPresenter() val presenter = createRoomMemberDetailsPresenter(
room = aMatrixRoom(
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -176,6 +191,11 @@ class RoomMemberDetailsPresenterTest {
val client = FakeMatrixClient() val client = FakeMatrixClient()
val roomMember = aRoomMember() val roomMember = aRoomMember()
val presenter = createRoomMemberDetailsPresenter( val presenter = createRoomMemberDetailsPresenter(
room = aMatrixRoom(
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
),
client = client, client = client,
roomMemberId = roomMember.userId roomMemberId = roomMember.userId
) )
@ -199,13 +219,21 @@ class RoomMemberDetailsPresenterTest {
fun `present - BlockUser with error`() = runTest { fun `present - BlockUser with error`() = runTest {
val matrixClient = FakeMatrixClient() val matrixClient = FakeMatrixClient()
matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE))
val presenter = createRoomMemberDetailsPresenter(client = matrixClient) val presenter = createRoomMemberDetailsPresenter(
client = matrixClient,
room = aMatrixRoom(
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
skipItems(2)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
// Clear error // Clear error
@ -218,13 +246,21 @@ class RoomMemberDetailsPresenterTest {
fun `present - UnblockUser with error`() = runTest { fun `present - UnblockUser with error`() = runTest {
val matrixClient = FakeMatrixClient() val matrixClient = FakeMatrixClient()
matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE)) matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE))
val presenter = createRoomMemberDetailsPresenter(client = matrixClient) val presenter = createRoomMemberDetailsPresenter(
room = aMatrixRoom(
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
),
client = matrixClient,
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
skipItems(2)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
// Clear error // Clear error
@ -235,7 +271,13 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest { fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest {
val presenter = createRoomMemberDetailsPresenter() val presenter = createRoomMemberDetailsPresenter(
room = aMatrixRoom(
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -255,7 +297,14 @@ class RoomMemberDetailsPresenterTest {
@Test @Test
fun `present - start DM action complete scenario`() = runTest { fun `present - start DM action complete scenario`() = runTest {
val startDMAction = FakeStartDMAction() val startDMAction = FakeStartDMAction()
val presenter = createRoomMemberDetailsPresenter(startDMAction = startDMAction) val presenter = createRoomMemberDetailsPresenter(
room = aMatrixRoom(
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
userDisplayNameResult = { Result.success("Alice") },
userAvatarUrlResult = { Result.success("anAvatarUrl") },
),
startDMAction = startDMAction,
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -268,6 +317,7 @@ class RoomMemberDetailsPresenterTest {
startDMAction.givenExecuteResult(startDMFailureResult) startDMAction.givenExecuteResult(startDMFailureResult)
initialState.eventSink(UserProfileEvents.StartDM) initialState.eventSink(UserProfileEvents.StartDM)
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
skipItems(2)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult) assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
state.eventSink(UserProfileEvents.ClearStartDMState) state.eventSink(UserProfileEvents.ClearStartDMState)
@ -292,8 +342,8 @@ class RoomMemberDetailsPresenterTest {
} }
private fun createRoomMemberDetailsPresenter( private fun createRoomMemberDetailsPresenter(
room: MatrixRoom,
client: MatrixClient = FakeMatrixClient(), client: MatrixClient = FakeMatrixClient(),
room: MatrixRoom = aMatrixRoom(),
roomMemberId: UserId = UserId("@alice:server.org"), roomMemberId: UserId = UserId("@alice:server.org"),
startDMAction: StartDMAction = FakeStartDMAction() startDMAction: StartDMAction = FakeStartDMAction()
): RoomMemberDetailsPresenter { ): RoomMemberDetailsPresenter {

104
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/DefaultRoomMembersModerationPresenterTest.kt

@ -54,29 +54,34 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest { fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) isDirect = false,
} activeMemberCount = 10,
canKickResult = { Result.success(true) },
canBanResult = { Result.success(true) },
)
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
assertThat(presenter.canDisplayModerationActions()).isTrue() assertThat(presenter.canDisplayModerationActions()).isTrue()
} }
@Test @Test
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest { fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply { val room = FakeMatrixRoom(
givenCanBanResult(Result.success(true)) isDirect = false,
} activeMemberCount = 10,
canBanResult = { Result.success(true) },
)
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
assertThat(presenter.canDisplayModerationActions()).isTrue() assertThat(presenter.canDisplayModerationActions()).isTrue()
} }
@Test @Test
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest { fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN)) userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
} )
val selectedMember = aVictor() val selectedMember = aVictor()
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -98,11 +103,12 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest { fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
val room = FakeMatrixRoom(sessionId = A_USER_ID).apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) sessionId = A_USER_ID,
givenCanBanResult(Result.success(true)) canKickResult = { Result.success(true) },
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN)) canBanResult = { Result.success(true) },
} userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
)
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L) val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -123,11 +129,11 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest { fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest {
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN) val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN)) userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
} )
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -144,11 +150,12 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - Kick removes the user`() = runTest { fun `present - Kick removes the user`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN)) userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
} kickUserResult = { _, _ -> Result.success(Unit) },
)
val selectedMember = aVictor() val selectedMember = aVictor()
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -171,11 +178,12 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - BanUser requires confirmation and then bans the user`() = runTest { fun `present - BanUser requires confirmation and then bans the user`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN)) userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
} banUserResult = { _, _ -> Result.success(Unit) },
)
val selectedMember = aVictor() val selectedMember = aVictor()
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -204,11 +212,13 @@ class DefaultRoomMembersModerationPresenterTest {
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest { fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN) val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
unBanUserResult = { _, _ -> Result.success(Unit) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember))) givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
} }
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@ -231,10 +241,11 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - Reset removes the selected user and actions`() = runTest { fun `present - Reset removes the selected user and actions`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
} userRoleResult = { Result.success(RoomMember.Role.USER) },
)
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -251,13 +262,14 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `present - Reset resets any async actions`() = runTest { fun `present - Reset resets any async actions`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenCanKickResult(Result.success(true)) canKickResult = { Result.success(true) },
givenCanBanResult(Result.success(true)) canBanResult = { Result.success(true) },
givenKickUserResult(Result.failure(Throwable("Eek"))) kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
givenBanUserResult(Result.failure(Throwable("Eek"))) banUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
givenUnbanUserResult(Result.failure(Throwable("Eek"))) unBanUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
} userRoleResult = { Result.success(RoomMember.Role.USER) },
)
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()

21
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/RolesAndPermissionPresenterTest.kt

@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMember
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.defaultRoomPowerLevels
import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -67,7 +68,12 @@ class RolesAndPermissionPresenterTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `present - DemoteSelfTo changes own role to the specified one`() = runTest(StandardTestDispatcher()) { fun `present - DemoteSelfTo changes own role to the specified one`() = runTest(StandardTestDispatcher()) {
val presenter = createRolesAndPermissionsPresenter(dispatchers = testCoroutineDispatchers()) val presenter = createRolesAndPermissionsPresenter(
dispatchers = testCoroutineDispatchers(),
room = FakeMatrixRoom(
updateUserRoleResult = { Result.success(Unit) }
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -85,9 +91,9 @@ class RolesAndPermissionPresenterTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) { fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenUpdateUserRoleResult(Result.failure(Exception("Failed to update role"))) updateUserRoleResult = { Result.failure(Exception("Failed to update role")) }
} )
val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers()) val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -123,7 +129,12 @@ class RolesAndPermissionPresenterTest {
@Test @Test
fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest { fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val presenter = createRolesAndPermissionsPresenter(analyticsService = analyticsService) val presenter = createRolesAndPermissionsPresenter(
analyticsService = analyticsService,
room = FakeMatrixRoom(
resetPowerLevelsResult = { Result.success(defaultRoomPowerLevels()) }
)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {

15
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTest.kt

@ -275,7 +275,10 @@ class ChangeRolesPresenterTest {
@Test @Test
fun `present - Save will display a confirmation when adding admins`() = runTest { fun `present - Save will display a confirmation when adding admins`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
updateUserRoleResult = { Result.success(Unit) },
updateMembersResult = { Result.success(Unit) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList())) givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100))) givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100)))
} }
@ -325,7 +328,10 @@ class ChangeRolesPresenterTest {
@Test @Test
fun `present - Save will just save the data for moderators`() = runTest { fun `present - Save will just save the data for moderators`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
updateUserRoleResult = { Result.success(Unit) },
updateMembersResult = { Result.success(Unit) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList())) givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50))) givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
} }
@ -351,10 +357,11 @@ class ChangeRolesPresenterTest {
@Test @Test
fun `present - Save can handle failures and ClearError clears them`() = runTest { fun `present - Save can handle failures and ClearError clears them`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
updateUserRoleResult = { Result.failure(IllegalStateException("Failed")) }
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList())) givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50))) givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
givenUpdateUserRoleResult(Result.failure(IllegalStateException("Failed")))
} }
val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room) val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {

25
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/permissions/ChangeRoomPermissionsPresenterTest.kt

@ -164,7 +164,13 @@ class ChangeRoomPermissionsPresenterTest {
@Test @Test
fun `present - Save updates the current permissions and resets hasChanges`() = runTest { fun `present - Save updates the current permissions and resets hasChanges`() = runTest {
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val presenter = createChangeRoomPermissionsPresenter(analyticsService = analyticsService) val presenter = createChangeRoomPermissionsPresenter(
analyticsService = analyticsService,
room = FakeMatrixRoom(
updatePowerLevelsResult = { Result.success(Unit) },
powerLevelsResult = { Result.success(defaultPermissions()) }
),
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -208,9 +214,9 @@ class ChangeRoomPermissionsPresenterTest {
@Test @Test
fun `present - Save will fail if there are not current permissions`() = runTest { fun `present - Save will fail if there are not current permissions`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenPowerLevelsResult(Result.failure(IllegalStateException("Failed to load power levels"))) powerLevelsResult = { Result.failure(IllegalStateException("Failed to load power levels")) }
} )
val presenter = createChangeRoomPermissionsPresenter(room = room) val presenter = createChangeRoomPermissionsPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -225,9 +231,10 @@ class ChangeRoomPermissionsPresenterTest {
@Test @Test
fun `present - Save can handle failures and they can be cleared`() = runTest { fun `present - Save can handle failures and they can be cleared`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenUpdatePowerLevelsResult(Result.failure(IllegalStateException("Failed to update power levels"))) powerLevelsResult = { Result.success(defaultPermissions()) },
} updatePowerLevelsResult = { Result.failure(IllegalStateException("Failed to update power levels")) },
)
val presenter = createChangeRoomPermissionsPresenter(room = room) val presenter = createChangeRoomPermissionsPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -292,7 +299,9 @@ class ChangeRoomPermissionsPresenterTest {
private fun createChangeRoomPermissionsPresenter( private fun createChangeRoomPermissionsPresenter(
section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails, section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails,
room: FakeMatrixRoom = FakeMatrixRoom(), room: FakeMatrixRoom = FakeMatrixRoom(
powerLevelsResult = { Result.success(defaultPermissions()) }
),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
) = ChangeRoomPermissionsPresenter( ) = ChangeRoomPermissionsPresenter(
section = section, section = section,

13
features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt

@ -441,7 +441,10 @@ class RoomListPresenterTest {
@Test @Test
fun `present - when set is favorite event is emitted, then the action is called`() = runTest { fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
val scope = CoroutineScope(coroutineContext + SupervisorJob()) val scope = CoroutineScope(coroutineContext + SupervisorJob())
val room = FakeMatrixRoom() val setIsFavoriteResult = lambdaRecorder { _: Boolean -> Result.success(Unit) }
val room = FakeMatrixRoom(
setIsFavoriteResult = setIsFavoriteResult
)
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
@ -452,9 +455,13 @@ class RoomListPresenterTest {
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true)) initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true))
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true)) setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, false)) initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, false))
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false)) setIsFavoriteResult.assertions().isCalledExactly(2)
.withSequence(
listOf(value(true)),
listOf(value(false)),
)
assertThat(analyticsService.capturedEvents).containsExactly( assertThat(analyticsService.capturedEvents).containsExactly(
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle), Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle),
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle) Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle)

9
features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt

@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
@ -92,7 +93,9 @@ class SharePresenterTest {
@Test @Test
fun `present - send text ok`() = runTest { fun `present - send text ok`() = runTest {
val matrixRoom = FakeMatrixRoom() val matrixRoom = FakeMatrixRoom(
sendMessageResult = { _, _, _ -> Result.success(Unit) },
)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, matrixRoom) givenGetRoomResult(A_ROOM_ID, matrixRoom)
} }
@ -117,7 +120,9 @@ class SharePresenterTest {
@Test @Test
fun `present - send media ok`() = runTest { fun `present - send media ok`() = runTest {
val matrixRoom = FakeMatrixRoom() val matrixRoom = FakeMatrixRoom(
sendMediaResult = { Result.success(FakeMediaUploadHandler()) },
)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, matrixRoom) givenGetRoomResult(A_ROOM_ID, matrixRoom)
} }

2
gradle.properties

@ -49,7 +49,7 @@ signing.element.nightly.keyPassword=Secret
# Customise the Lint version to use a more recent version than the one bundled with AGP # Customise the Lint version to use a more recent version than the one bundled with AGP
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html # https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
android.experimental.lint.version=8.5.0-alpha07 android.experimental.lint.version=8.7.0-alpha01
# Enable test fixture for all modules by default # Enable test fixture for all modules by default
android.experimental.enableTestFixtures=true android.experimental.enableTestFixtures=true

523
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -56,7 +56,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask import io.element.android.tests.testutils.simulateLongTask
import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
@ -84,126 +84,88 @@ class FakeMatrixRoom(
override val activeMemberCount: Long = 234L, override val activeMemberCount: Long = 234L,
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
override val liveTimeline: Timeline = FakeTimeline(), override val liveTimeline: Timeline = FakeTimeline(),
private var roomPermalinkResult: () -> Result<String> = { Result.success("room link") }, private var roomPermalinkResult: () -> Result<String> = { lambdaError() },
private var eventPermalinkResult: (EventId) -> Result<String> = { Result.success("event link") }, private var eventPermalinkResult: (EventId) -> Result<String> = { lambdaError() },
var sendCallNotificationIfNeededResult: () -> Result<Unit> = { Result.success(Unit) }, private val sendCallNotificationIfNeededResult: () -> Result<Unit> = { lambdaError() },
canRedactOwn: Boolean = false, private val userDisplayNameResult: () -> Result<String?> = { lambdaError() },
canRedactOther: Boolean = false, private val userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
private val userRoleResult: () -> Result<RoomMember.Role> = { lambdaError() },
private val getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
private val joinRoomResult: () -> Result<Unit> = { lambdaError() },
private val inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() },
private val canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canKickResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canBanResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canRedactOwnResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canRedactOtherResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
private val canUserSendMessageResult: (UserId, MessageEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
private val sendMediaResult: (ProgressCallback?) -> Result<FakeMediaUploadHandler> = { lambdaError() },
private val setNameResult: (String) -> Result<Unit> = { lambdaError() },
private val setTopicResult: (String) -> Result<Unit> = { lambdaError() },
private val updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> lambdaError() },
private val removeAvatarResult: () -> Result<Unit> = { lambdaError() },
private val sendMessageResult: (String, String?, List<Mention>) -> Result<Unit> = { _, _, _ -> lambdaError() },
private val updateUserRoleResult: () -> Result<Unit> = { lambdaError() },
private val toggleReactionResult: (String, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
private val retrySendMessageResult: (TransactionId) -> Result<Unit> = { lambdaError() },
private val cancelSendResult: (TransactionId) -> Result<Boolean> = { lambdaError() },
private val forwardEventResult: (EventId, List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() },
private val reportContentResult: (EventId, String, UserId?) -> Result<Unit> = { _, _, _ -> lambdaError() },
private val kickUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
private val banUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
private val unBanUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
private val sendLocationResult: (String, String, String?, Int?, AssetType?) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
private val createPollResult: (String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
private val editPollResult: (EventId, String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
private val sendPollResponseResult: (EventId, List<String>) -> Result<Unit> = { _, _ -> lambdaError() },
private val endPollResult: (EventId, String) -> Result<Unit> = { _, _ -> lambdaError() },
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
private val generateWidgetWebViewUrlResult: (MatrixWidgetSettings, String, String?, String?) -> Result<String> = { _, _, _, _ -> lambdaError() },
private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() },
private val canUserTriggerRoomNotificationResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
private val setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() },
private val powerLevelsResult: () -> Result<MatrixRoomPowerLevels> = { lambdaError() },
private val updatePowerLevelsResult: () -> Result<Unit> = { lambdaError() },
private val resetPowerLevelsResult: () -> Result<MatrixRoomPowerLevels> = { lambdaError() },
private val typingNoticeResult: (Boolean) -> Result<Unit> = { lambdaError() },
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
private val updateMembersResult: () -> Unit = { lambdaError() },
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
private val timelineFocusedOnEventResult: (EventId) -> Result<Timeline> = { lambdaError() },
private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> },
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
private val clearComposerDraftLambda: () -> Result<Unit> = { Result.success(Unit) },
) : MatrixRoom { ) : MatrixRoom {
private var ignoreResult: Result<Unit> = Result.success(Unit)
private var unignoreResult: Result<Unit> = Result.success(Unit)
private var userDisplayNameResult = Result.success<String?>(null)
private var userAvatarUrlResult = Result.success<String?>(null)
private var userRoleResult = Result.success(RoomMember.Role.USER)
private var getRoomMemberResult = Result.failure<RoomMember>(IllegalStateException("Member not found"))
private var joinRoomResult = Result.success(Unit)
private var inviteUserResult = Result.success(Unit)
private var canInviteResult = Result.success(true)
private var canKickResult = Result.success(false)
private var canBanResult = Result.success(false)
private var canRedactOwnResult = Result.success(canRedactOwn)
private var canRedactOtherResult = Result.success(canRedactOther)
private val canSendStateResults = mutableMapOf<StateEventType, Result<Boolean>>()
private val canSendEventResults = mutableMapOf<MessageEventType, Result<Boolean>>()
private var sendMediaResult = Result.success(FakeMediaUploadHandler())
private var setNameResult = Result.success(Unit)
private var setTopicResult = Result.success(Unit)
private var updateAvatarResult = Result.success(Unit)
private var removeAvatarResult = Result.success(Unit)
private var updateUserRoleResult = Result.success(Unit)
private var toggleReactionResult = Result.success(Unit)
private var retrySendMessageResult = Result.success(Unit)
private var cancelSendResult = Result.success(true)
private var forwardEventResult = Result.success(Unit)
private var reportContentResult = Result.success(Unit)
private var kickUserResult = Result.success(Unit)
private var banUserResult = Result.success(Unit)
private var unBanUserResult = Result.success(Unit)
private var sendLocationResult = Result.success(Unit)
private var createPollResult = Result.success(Unit)
private var editPollResult = Result.success(Unit)
private var sendPollResponseResult = Result.success(Unit)
private var endPollResult = Result.success(Unit)
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io")
private var getWidgetDriverResult: Result<MatrixWidgetDriver> = Result.success(FakeMatrixWidgetDriver())
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
private var canUserJoinCallResult: Result<Boolean> = Result.success(true)
private var setIsFavoriteResult = Result.success(Unit)
private var powerLevelsResult = Result.success(defaultRoomPowerLevels())
private var updatePowerLevelsResult = Result.success(Unit)
private var resetPowerLevelsResult = Result.success(defaultRoomPowerLevels())
var sendMessageMentions = emptyList<Mention>()
private val _typingRecord = mutableListOf<Boolean>()
val typingRecord: List<Boolean>
get() = _typingRecord
var sendMediaCount = 0
private set
private val _myReactions = mutableSetOf<String>()
val myReactions: Set<String> = _myReactions
var retrySendMessageCount: Int = 0
private set
var cancelSendCount: Int = 0
private set
var reportedContentCount: Int = 0
private set
private val _sentLocations = mutableListOf<SendLocationInvocation>()
val sentLocations: List<SendLocationInvocation> = _sentLocations
private val _createPollInvocations = mutableListOf<SavePollInvocation>()
val createPollInvocations: List<SavePollInvocation> = _createPollInvocations
private val _editPollInvocations = mutableListOf<SavePollInvocation>()
val editPollInvocations: List<SavePollInvocation> = _editPollInvocations
private val _sendPollResponseInvocations = mutableListOf<SendPollResponseInvocation>()
val sendPollResponseInvocations: List<SendPollResponseInvocation> = _sendPollResponseInvocations
private val _endPollInvocations = mutableListOf<EndPollInvocation>()
val endPollInvocations: List<EndPollInvocation> = _endPollInvocations
var invitedUserId: UserId? = null
private set
var newTopic: String? = null
private set
var newName: String? = null
private set
var newAvatarData: ByteArray? = null
private set
var removedAvatar: Boolean = false
private set
var leaveRoomLambda: (() -> Result<Unit>) = { Result.success(Unit) }
private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableSharedFlow(replay = 1) private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableSharedFlow(replay = 1)
override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
_roomInfoFlow.tryEmit(roomInfo)
}
private val _roomTypingMembersFlow: MutableSharedFlow<List<UserId>> = MutableSharedFlow(replay = 1) private val _roomTypingMembersFlow: MutableSharedFlow<List<UserId>> = MutableSharedFlow(replay = 1)
override val roomTypingMembersFlow: Flow<List<UserId>> = _roomTypingMembersFlow override val roomTypingMembersFlow: Flow<List<UserId>> = _roomTypingMembersFlow
fun givenRoomTypingMembers(typingMembers: List<UserId>) {
_roomTypingMembersFlow.tryEmit(typingMembers)
}
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown) override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> = override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> =
MutableStateFlow(MatrixRoomNotificationSettingsState.Unknown) MutableStateFlow(MatrixRoomNotificationSettingsState.Unknown)
override suspend fun updateMembers() = Unit override suspend fun updateMembers() = updateMembersResult()
override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> { override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> {
return getRoomMemberResult return getUpdatedMemberResult(userId)
} }
override suspend fun getMembers(limit: Int): Result<List<RoomMember>> { override suspend fun getMembers(limit: Int): Result<List<RoomMember>> {
return Result.success(emptyList()) return getMembersResult(limit)
} }
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask { override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
@ -214,76 +176,56 @@ class FakeMatrixRoom(
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L) override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L)
private var timelineFocusedOnEventResult: Result<Timeline> = Result.success(FakeTimeline())
fun givenTimelineFocusedOnEventResult(result: Result<Timeline>) {
timelineFocusedOnEventResult = result
}
override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> = simulateLongTask { override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> = simulateLongTask {
timelineFocusedOnEventResult timelineFocusedOnEventResult(eventId)
} }
override suspend fun subscribeToSync() = Unit override suspend fun subscribeToSync() = Unit
override suspend fun powerLevels(): Result<MatrixRoomPowerLevels> { override suspend fun powerLevels(): Result<MatrixRoomPowerLevels> {
return powerLevelsResult return powerLevelsResult()
} }
override suspend fun updatePowerLevels(matrixRoomPowerLevels: MatrixRoomPowerLevels): Result<Unit> = simulateLongTask { override suspend fun updatePowerLevels(matrixRoomPowerLevels: MatrixRoomPowerLevels): Result<Unit> = simulateLongTask {
updatePowerLevelsResult updatePowerLevelsResult()
} }
override suspend fun resetPowerLevels(): Result<MatrixRoomPowerLevels> = simulateLongTask { override suspend fun resetPowerLevels(): Result<MatrixRoomPowerLevels> = simulateLongTask {
resetPowerLevelsResult resetPowerLevelsResult()
} }
override fun destroy() = Unit override fun destroy() = Unit
override suspend fun userDisplayName(userId: UserId): Result<String?> = simulateLongTask { override suspend fun userDisplayName(userId: UserId): Result<String?> = simulateLongTask {
userDisplayNameResult userDisplayNameResult()
} }
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = simulateLongTask { override suspend fun userAvatarUrl(userId: UserId): Result<String?> = simulateLongTask {
userAvatarUrlResult userAvatarUrlResult()
} }
override suspend fun userRole(userId: UserId): Result<RoomMember.Role> { override suspend fun userRole(userId: UserId): Result<RoomMember.Role> {
return userRoleResult return userRoleResult()
} }
override suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit> { override suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit> {
return updateUserRoleResult return updateUserRoleResult()
} }
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>) = simulateLongTask { override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>) = simulateLongTask {
sendMessageMentions = mentions sendMessageResult(body, htmlBody, mentions)
Result.success(Unit)
} }
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> { override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> {
if (toggleReactionResult.isFailure) { return toggleReactionResult(emoji, eventId)
// Don't do the toggle if we failed
return toggleReactionResult
}
if (_myReactions.contains(emoji)) {
_myReactions.remove(emoji)
} else {
_myReactions.add(emoji)
}
return toggleReactionResult
} }
override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> { override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> {
retrySendMessageCount++ return retrySendMessageResult(transactionId)
return retrySendMessageResult
} }
override suspend fun cancelSend(transactionId: TransactionId): Result<Boolean> { override suspend fun cancelSend(transactionId: TransactionId): Result<Boolean> {
cancelSendCount++ return cancelSendResult(transactionId)
return cancelSendResult
} }
override suspend fun getPermalink(): Result<String> { override suspend fun getPermalink(): Result<String> {
@ -299,48 +241,47 @@ class FakeMatrixRoom(
} }
override suspend fun join(): Result<Unit> { override suspend fun join(): Result<Unit> {
return joinRoomResult return joinRoomResult()
} }
override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask { override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask {
invitedUserId = id inviteUserResult(id)
inviteUserResult
} }
override suspend fun canUserBan(userId: UserId): Result<Boolean> { override suspend fun canUserBan(userId: UserId): Result<Boolean> {
return canBanResult return canBanResult(userId)
} }
override suspend fun canUserKick(userId: UserId): Result<Boolean> { override suspend fun canUserKick(userId: UserId): Result<Boolean> {
return canKickResult return canKickResult(userId)
} }
override suspend fun canUserInvite(userId: UserId): Result<Boolean> { override suspend fun canUserInvite(userId: UserId): Result<Boolean> {
return canInviteResult return canInviteResult(userId)
} }
override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> { override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> {
return canRedactOwnResult return canRedactOwnResult(userId)
} }
override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> { override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> {
return canRedactOtherResult return canRedactOtherResult(userId)
} }
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> { override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> {
return canSendStateResults[type] ?: Result.failure(IllegalStateException("No fake answer")) return canSendStateResult(userId, type)
} }
override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> { override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> {
return canSendEventResults[type] ?: Result.failure(IllegalStateException("No fake answer")) return canUserSendMessageResult(userId, type)
} }
override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> { override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> {
return canUserTriggerRoomNotificationResult return canUserTriggerRoomNotificationResult(userId)
} }
override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> { override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> {
return canUserJoinCallResult return canUserJoinCallResult(userId)
} }
override suspend fun sendImage( override suspend fun sendImage(
@ -376,37 +317,31 @@ class FakeMatrixRoom(
): Result<MediaUploadHandler> = fakeSendMedia(progressCallback) ): Result<MediaUploadHandler> = fakeSendMedia(progressCallback)
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask { override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
forwardEventResult forwardEventResult(eventId, roomIds)
} }
private suspend fun fakeSendMedia(progressCallback: ProgressCallback?): Result<MediaUploadHandler> = simulateLongTask { private suspend fun fakeSendMedia(progressCallback: ProgressCallback?): Result<MediaUploadHandler> = simulateLongTask {
sendMediaResult.onSuccess { progressCallbackValues.forEach { (current, total) ->
progressCallbackValues.forEach { (current, total) -> progressCallback?.onProgress(current, total)
progressCallback?.onProgress(current, total) delay(1)
delay(1)
}
sendMediaCount++
} }
sendMediaResult(progressCallback)
} }
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = simulateLongTask { override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = simulateLongTask {
newAvatarData = data updateAvatarResult(mimeType, data)
updateAvatarResult
} }
override suspend fun removeAvatar(): Result<Unit> = simulateLongTask { override suspend fun removeAvatar(): Result<Unit> = simulateLongTask {
removedAvatar = true removeAvatarResult()
removeAvatarResult
} }
override suspend fun setName(name: String): Result<Unit> = simulateLongTask { override suspend fun setName(name: String): Result<Unit> = simulateLongTask {
newName = name setNameResult(name)
setNameResult
} }
override suspend fun setTopic(topic: String): Result<Unit> = simulateLongTask { override suspend fun setTopic(topic: String): Result<Unit> = simulateLongTask {
newTopic = topic setTopicResult(topic)
setTopicResult
} }
override suspend fun reportContent( override suspend fun reportContent(
@ -414,28 +349,23 @@ class FakeMatrixRoom(
reason: String, reason: String,
blockUserId: UserId? blockUserId: UserId?
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
reportedContentCount++ return reportContentResult(eventId, reason, blockUserId)
return reportContentResult
} }
override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> { override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> {
return kickUserResult return kickUserResult(userId, reason)
} }
override suspend fun banUser(userId: UserId, reason: String?): Result<Unit> { override suspend fun banUser(userId: UserId, reason: String?): Result<Unit> {
return banUserResult return banUserResult(userId, reason)
} }
override suspend fun unbanUser(userId: UserId, reason: String?): Result<Unit> { override suspend fun unbanUser(userId: UserId, reason: String?): Result<Unit> {
return unBanUserResult return unBanUserResult(userId, reason)
} }
val setIsFavoriteCalls = mutableListOf<Boolean>()
override suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit> { override suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit> {
return setIsFavoriteResult.also { return setIsFavoriteResult(isFavorite)
setIsFavoriteCalls.add(isFavorite)
}
} }
val markAsReadCalls = mutableListOf<ReceiptType>() val markAsReadCalls = mutableListOf<ReceiptType>()
@ -460,8 +390,13 @@ class FakeMatrixRoom(
zoomLevel: Int?, zoomLevel: Int?,
assetType: AssetType?, assetType: AssetType?,
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
_sentLocations.add(SendLocationInvocation(body, geoUri, description, zoomLevel, assetType)) return sendLocationResult(
return sendLocationResult body,
geoUri,
description,
zoomLevel,
assetType,
)
} }
override suspend fun createPoll( override suspend fun createPoll(
@ -470,8 +405,12 @@ class FakeMatrixRoom(
maxSelections: Int, maxSelections: Int,
pollKind: PollKind pollKind: PollKind
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
_createPollInvocations.add(SavePollInvocation(question, answers, maxSelections, pollKind)) return createPollResult(
return createPollResult question,
answers,
maxSelections,
pollKind,
)
} }
override suspend fun editPoll( override suspend fun editPoll(
@ -481,24 +420,27 @@ class FakeMatrixRoom(
maxSelections: Int, maxSelections: Int,
pollKind: PollKind pollKind: PollKind
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
_editPollInvocations.add(SavePollInvocation(question, answers, maxSelections, pollKind)) return editPollResult(
return editPollResult pollStartId,
question,
answers,
maxSelections,
pollKind,
)
} }
override suspend fun sendPollResponse( override suspend fun sendPollResponse(
pollStartId: EventId, pollStartId: EventId,
answers: List<String> answers: List<String>
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
_sendPollResponseInvocations.add(SendPollResponseInvocation(pollStartId, answers)) return sendPollResponseResult(pollStartId, answers)
return sendPollResponseResult
} }
override suspend fun endPoll( override suspend fun endPoll(
pollStartId: EventId, pollStartId: EventId,
text: String text: String
): Result<Unit> = simulateLongTask { ): Result<Unit> = simulateLongTask {
_endPollInvocations.add(EndPollInvocation(pollStartId, text)) return endPollResult(pollStartId, text)
return endPollResult
} }
override suspend fun sendVoiceMessage( override suspend fun sendVoiceMessage(
@ -509,8 +451,7 @@ class FakeMatrixRoom(
): Result<MediaUploadHandler> = fakeSendMedia(progressCallback) ): Result<MediaUploadHandler> = fakeSendMedia(progressCallback)
override suspend fun typingNotice(isTyping: Boolean): Result<Unit> { override suspend fun typingNotice(isTyping: Boolean): Result<Unit> {
_typingRecord += isTyping return typingNoticeResult(isTyping)
return Result.success(Unit)
} }
override suspend fun generateWidgetWebViewUrl( override suspend fun generateWidgetWebViewUrl(
@ -518,228 +459,34 @@ class FakeMatrixRoom(
clientId: String, clientId: String,
languageTag: String?, languageTag: String?,
theme: String?, theme: String?,
): Result<String> = generateWidgetWebViewUrlResult ): Result<String> = generateWidgetWebViewUrlResult(
widgetSettings,
clientId,
languageTag,
theme,
)
override suspend fun sendCallNotificationIfNeeded(): Result<Unit> { override suspend fun sendCallNotificationIfNeeded(): Result<Unit> {
return sendCallNotificationIfNeededResult() return sendCallNotificationIfNeededResult()
} }
var setSendQueueEnabledLambda = { _: Boolean -> }
override suspend fun setSendQueueEnabled(enabled: Boolean) = setSendQueueEnabledLambda(enabled) override suspend fun setSendQueueEnabled(enabled: Boolean) = setSendQueueEnabledLambda(enabled)
var saveComposerDraftLambda = { _: ComposerDraft -> Result.success(Unit) }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft) = saveComposerDraftLambda(composerDraft) override suspend fun saveComposerDraft(composerDraft: ComposerDraft) = saveComposerDraftLambda(composerDraft)
var loadComposerDraftLambda = { Result.success<ComposerDraft?>(null) }
override suspend fun loadComposerDraft() = loadComposerDraftLambda() override suspend fun loadComposerDraft() = loadComposerDraftLambda()
var clearComposerDraftLambda = { Result.success(Unit) }
override suspend fun clearComposerDraft() = clearComposerDraftLambda() override suspend fun clearComposerDraft() = clearComposerDraftLambda()
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = getWidgetDriverResult override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> {
return getWidgetDriverResult(widgetSettings)
}
fun givenRoomMembersState(state: MatrixRoomMembersState) { fun givenRoomMembersState(state: MatrixRoomMembersState) {
membersStateFlow.value = state membersStateFlow.value = state
} }
fun givenGetRoomMemberResult(result: Result<RoomMember>) {
getRoomMemberResult = result
}
fun givenUserDisplayNameResult(displayName: Result<String?>) {
userDisplayNameResult = displayName
}
fun givenUserAvatarUrlResult(avatarUrl: Result<String?>) {
userAvatarUrlResult = avatarUrl
}
fun givenUserRoleResult(role: Result<RoomMember.Role>) {
userRoleResult = role
}
fun givenUpdateUserRoleResult(result: Result<Unit>) {
updateUserRoleResult = result
}
fun givenJoinRoomResult(result: Result<Unit>) {
joinRoomResult = result
}
fun givenCanKickResult(result: Result<Boolean>) {
canKickResult = result
}
fun givenCanBanResult(result: Result<Boolean>) {
canBanResult = result
}
fun givenInviteUserResult(result: Result<Unit>) {
inviteUserResult = result
}
fun givenCanInviteResult(result: Result<Boolean>) {
canInviteResult = result
}
fun givenCanSendStateResult(type: StateEventType, result: Result<Boolean>) {
canSendStateResults[type] = result
}
fun givenCanSendEventResult(type: MessageEventType, result: Result<Boolean>) {
canSendEventResults[type] = result
}
fun givenCanTriggerRoomNotification(result: Result<Boolean>) {
canUserTriggerRoomNotificationResult = result
}
fun givenCanUserJoinCall(result: Result<Boolean>) {
canUserJoinCallResult = result
}
fun givenIgnoreResult(result: Result<Unit>) {
ignoreResult = result
}
fun givenUnIgnoreResult(result: Result<Unit>) {
unignoreResult = result
}
fun givenSendMediaResult(result: Result<FakeMediaUploadHandler>) {
sendMediaResult = result
}
fun givenUpdateAvatarResult(result: Result<Unit>) {
updateAvatarResult = result
}
fun givenRemoveAvatarResult(result: Result<Unit>) {
removeAvatarResult = result
}
fun givenSetNameResult(result: Result<Unit>) {
setNameResult = result
}
fun givenSetTopicResult(result: Result<Unit>) {
setTopicResult = result
}
fun givenToggleReactionResult(result: Result<Unit>) {
toggleReactionResult = result
}
fun givenRetrySendMessageResult(result: Result<Unit>) {
retrySendMessageResult = result
}
fun givenCancelSendResult(result: Result<Boolean>) {
cancelSendResult = result
}
fun givenForwardEventResult(result: Result<Unit>) {
forwardEventResult = result
}
fun givenReportContentResult(result: Result<Unit>) {
reportContentResult = result
}
fun givenKickUserResult(result: Result<Unit>) {
kickUserResult = result
}
fun givenBanUserResult(result: Result<Unit>) {
banUserResult = result
}
fun givenUnbanUserResult(result: Result<Unit>) {
unBanUserResult = result
}
fun givenSendLocationResult(result: Result<Unit>) {
sendLocationResult = result
}
fun givenCreatePollResult(result: Result<Unit>) {
createPollResult = result
}
fun givenEditPollResult(result: Result<Unit>) {
editPollResult = result
}
fun givenSendPollResponseResult(result: Result<Unit>) {
sendPollResponseResult = result
}
fun givenEndPollResult(result: Result<Unit>) {
endPollResult = result
}
fun givenProgressCallbackValues(values: List<Pair<Long, Long>>) {
progressCallbackValues = values
}
fun givenGenerateWidgetWebViewUrlResult(result: Result<String>) {
generateWidgetWebViewUrlResult = result
}
fun givenGetWidgetDriverResult(result: Result<MatrixWidgetDriver>) {
getWidgetDriverResult = result
}
fun givenSetIsFavoriteResult(result: Result<Unit>) {
setIsFavoriteResult = result
}
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
_roomInfoFlow.tryEmit(roomInfo)
}
fun givenRoomTypingMembers(typingMembers: List<UserId>) {
_roomTypingMembersFlow.tryEmit(typingMembers)
}
fun givenPowerLevelsResult(result: Result<MatrixRoomPowerLevels>) {
powerLevelsResult = result
}
fun givenUpdatePowerLevelsResult(result: Result<Unit>) {
updatePowerLevelsResult = result
}
fun givenResetPowerLevelsResult(result: Result<MatrixRoomPowerLevels>) {
resetPowerLevelsResult = result
}
} }
data class SendLocationInvocation(
val body: String,
val geoUri: String,
val description: String?,
val zoomLevel: Int?,
val assetType: AssetType?,
)
data class SavePollInvocation(
val question: String,
val answers: List<String>,
val maxSelections: Int,
val pollKind: PollKind,
)
data class SendPollResponseInvocation(
val pollStartId: EventId,
val answers: List<String>,
)
data class EndPollInvocation(
val pollStartId: EventId,
val text: String,
)
fun aRoomInfo( fun aRoomInfo(
id: RoomId = A_ROOM_ID, id: RoomId = A_ROOM_ID,
name: String? = A_ROOM_NAME, name: String? = A_ROOM_NAME,

1
libraries/mediaupload/api/build.gradle.kts

@ -40,6 +40,7 @@ android {
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.tests.testutils)
testImplementation(libs.test.junit) testImplementation(libs.test.junit)
testImplementation(libs.test.truth) testImplementation(libs.test.truth)
testImplementation(libs.coroutines.test) testImplementation(libs.coroutines.test)

23
libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt

@ -19,9 +19,12 @@ package io.element.android.libraries.mediaupload.api
import android.net.Uri import android.net.Uri
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.StandardTestDispatcher
@ -46,13 +49,17 @@ class MediaSenderTest {
@Test @Test
fun `given an attachment when sending it the MatrixRoom will call sendMedia`() = runTest { fun `given an attachment when sending it the MatrixRoom will call sendMedia`() = runTest {
val room = FakeMatrixRoom() val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
Result.success(FakeMediaUploadHandler())
}
val room = FakeMatrixRoom(
sendMediaResult = sendMediaResult
)
val sender = aMediaSender(room = room) val sender = aMediaSender(room = room)
val uri = Uri.parse("content://image.jpg") val uri = Uri.parse("content://image.jpg")
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true) sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
sendMediaResult.assertions().isCalledOnce()
assertThat(room.sendMediaCount).isEqualTo(1)
} }
@Test @Test
@ -70,9 +77,9 @@ class MediaSenderTest {
@Test @Test
fun `given a failure in the media upload when sending the whole process fails`() = runTest { fun `given a failure in the media upload when sending the whole process fails`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom(
givenSendMediaResult(Result.failure(Exception())) sendMediaResult = { Result.failure(Exception()) }
} )
val sender = aMediaSender(room = room) val sender = aMediaSender(room = room)
val uri = Uri.parse("content://image.jpg") val uri = Uri.parse("content://image.jpg")
@ -84,7 +91,9 @@ class MediaSenderTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) { fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) {
val room = FakeMatrixRoom() val room = FakeMatrixRoom(
sendMediaResult = { Result.success(FakeMediaUploadHandler()) }
)
val sender = aMediaSender(room = room) val sender = aMediaSender(room = room)
val sendJob = launch { val sendJob = launch {
val uri = Uri.parse("content://image.jpg") val uri = Uri.parse("content://image.jpg")

11
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt

@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
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.aRoomMember
import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
@ -297,9 +298,9 @@ class NotificationBroadcastReceiverHandlerTest {
@Test @Test
fun `Test reject room`() = runTest { fun `Test reject room`() = runTest {
val leaveRoom = lambdaRecorder<Result<Unit>> { Result.success(Unit) } val leaveRoom = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val matrixRoom = FakeMatrixRoom().apply { val matrixRoom = FakeMatrixRoom(
leaveRoomLambda = leaveRoom leaveRoomLambda = leaveRoom
} )
val clearMembershipNotificationForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> } val clearMembershipNotificationForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
val fakeNotificationCleaner = FakeNotificationCleaner( val fakeNotificationCleaner = FakeNotificationCleaner(
clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda, clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda,
@ -342,7 +343,8 @@ class NotificationBroadcastReceiverHandlerTest {
replyMessageLambda = replyMessage replyMessageLambda = replyMessage
} }
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
getUpdatedMemberResult = { Result.success(aRoomMember()) },
) )
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> } val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult) val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
@ -400,7 +402,8 @@ class NotificationBroadcastReceiverHandlerTest {
replyMessageLambda = replyMessage replyMessageLambda = replyMessage
} }
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline liveTimeline = liveTimeline,
getUpdatedMemberResult = { Result.success(aRoomMember()) },
) )
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> } val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult) val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)

Loading…
Cancel
Save