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

* Upgrade lint to 8.7.0-alpha01

* FakeMatrixRoom: lambda everywhere

Fix test compilation issues
This commit is contained in:
Benoit Marty 2024-07-22 10:39:48 +02:00 committed by GitHub
parent 8cb5e9e75b
commit 5bda29ca7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1253 additions and 818 deletions

View file

@ -17,11 +17,15 @@
package io.element.android.features.roomdetails
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_NAME
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.aRoomInfo
import io.element.android.tests.testutils.lambda.lambdaError
fun aMatrixRoom(
roomId: RoomId = A_ROOM_ID,
@ -34,6 +38,16 @@ fun aMatrixRoom(
isDirect: Boolean = false,
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
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(
roomId = roomId,
displayName = displayName,
@ -42,7 +56,17 @@ fun aMatrixRoom(
isEncrypted = isEncrypted,
isPublic = isPublic,
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 {
if (emitRoomInfo) {
givenRoomInfo(

View file

@ -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.WarmUpRule
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.withFakeLifecycleOwner
import kotlinx.collections.immutable.persistentListOf
@ -110,7 +113,11 @@ class RoomDetailsPresenterTest {
@Test
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)
presenter.test {
val initialState = awaitItem()
@ -128,7 +135,11 @@ class RoomDetailsPresenterTest {
@Test
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 room = aMatrixRoom().apply {
val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
).apply {
givenRoomInfo(roomInfo)
}
val presenter = createRoomDetailsPresenter(room)
@ -145,7 +156,12 @@ class RoomDetailsPresenterTest {
@Test
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)
presenter.test {
val initialState = awaitItem()
@ -162,6 +178,16 @@ class RoomDetailsPresenterTest {
val room = aMatrixRoom(
isEncrypted = 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 {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
@ -181,9 +207,11 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when user can invite others to room`() = runTest {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.success(true))
}
val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
presenter.test {
// Initially false
@ -197,9 +225,11 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when user can not invite others to room`() = runTest {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.success(false))
}
val room = aMatrixRoom(
canInviteResult = { Result.success(false) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room)
presenter.test {
assertThat(awaitItem().canInvite).isFalse()
@ -210,9 +240,11 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when canInvite errors`() = runTest {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.failure(Throwable("Whoops")))
}
val room = aMatrixRoom(
canInviteResult = { Result.failure(Throwable("Whoops")) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(room)
presenter.test {
assertThat(awaitItem().canInvite).isFalse()
@ -223,12 +255,18 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when user can edit one attribute`() = runTest {
val room = aMatrixRoom().apply {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp")))
givenCanInviteResult(Result.success(false))
}
val room = aMatrixRoom(
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_TOPIC -> Result.success(true)
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)
presenter.test {
// Initially false
@ -247,14 +285,26 @@ class RoomDetailsPresenterTest {
val room = aMatrixRoom(
isEncrypted = 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 {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
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)
presenter.test {
@ -278,12 +328,28 @@ class RoomDetailsPresenterTest {
isEncrypted = true,
isDirect = true,
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 {
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
}
val presenter = createRoomDetailsPresenter(room)
presenter.test {
skipItems(1)
@ -297,12 +363,20 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when user can edit all attributes`() = runTest {
val room = aMatrixRoom().apply {
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 room = aMatrixRoom(
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) },
)
val presenter = createRoomDetailsPresenter(room)
presenter.test {
// Initially false
@ -316,12 +390,20 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when user can edit no attributes`() = runTest {
val room = aMatrixRoom().apply {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
givenCanInviteResult(Result.success(false))
}
val room = aMatrixRoom(
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_TOPIC -> 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)
presenter.test {
// Initially false, and no further events
@ -333,11 +415,21 @@ class RoomDetailsPresenterTest {
@Test
fun `present - topic state is hidden when no topic and user has no permission`() = runTest {
val room = aMatrixRoom(topic = null).apply {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
givenCanInviteResult(Result.success(false))
}
val room = aMatrixRoom(
topic = null,
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)
presenter.test {
// The initial state is "hidden" and no further state changes happen
@ -349,12 +441,23 @@ class RoomDetailsPresenterTest {
@Test
fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest {
val room = aMatrixRoom(topic = null).apply {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
givenCanInviteResult(Result.success(false))
val room = aMatrixRoom(
topic = null,
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))
}
val presenter = createRoomDetailsPresenter(room)
presenter.test {
// Ignore the initial state
@ -370,7 +473,11 @@ class RoomDetailsPresenterTest {
@Test
fun `present - leave room event is passed on to leave room presenter`() = runTest {
val leaveRoomPresenter = FakeLeaveRoomPresenter()
val room = aMatrixRoom()
val room = aMatrixRoom(
canInviteResult = { Result.success(true) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
val presenter = createRoomDetailsPresenter(
room = room,
leaveRoomPresenter = leaveRoomPresenter,
@ -379,7 +486,11 @@ class RoomDetailsPresenterTest {
presenter.test {
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId))
assertThat(leaveRoomPresenter.events).contains(
LeaveRoomEvent.ShowConfirmation(
room.roomId
)
)
cancelAndIgnoreRemainingEvents()
}
@ -389,33 +500,54 @@ class RoomDetailsPresenterTest {
fun `present - notification mode changes`() = runTest {
val leaveRoomPresenter = FakeLeaveRoomPresenter()
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(
room = room,
leaveRoomPresenter = leaveRoomPresenter,
notificationSettingsService = notificationSettingsService,
)
presenter.test {
notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
notificationSettingsService.setRoomNotificationMode(
room.roomId,
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
)
val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
)
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - mute room notifications`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
val notificationSettingsService =
FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
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 {
awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
}.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MUTE)
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.MUTE
)
cancelAndIgnoreRemainingEvents()
}
}
@ -426,29 +558,50 @@ class RoomDetailsPresenterTest {
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
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 {
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
}.last()
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
RoomNotificationMode.ALL_MESSAGES
)
cancelAndIgnoreRemainingEvents()
}
}
@Test
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 presenter = createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
val presenter =
createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEvent.SetFavorite(true))
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true))
setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
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(
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle),
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle)
@ -459,7 +612,11 @@ class RoomDetailsPresenterTest {
@Test
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)
presenter.test {
room.givenRoomInfo(aRoomInfo(isFavorite = true))

View file

@ -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.FakePermissionsPresenterFactory
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.mockk
import io.mockk.mockkStatic
@ -98,6 +101,7 @@ class RoomDetailsEditPresenterTest {
displayName = A_ROOM_NAME,
rawName = A_ROOM_RAW_NAME,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
@ -120,11 +124,17 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - sets canChangeName if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
}
val room = aMatrixRoom(
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, stateEventType ->
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -144,11 +154,17 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - sets canChangeAvatar if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
}
val room = aMatrixRoom(
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, stateEventType ->
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -168,11 +184,17 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - sets canChangeTopic if user has permission`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops")))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
}
val room = aMatrixRoom(
avatarUrl = AN_AVATAR_URL,
canSendStateResult = { _, stateEventType ->
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -197,6 +219,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
@ -240,6 +263,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(anotherAvatarUri)
val presenter = createRoomDetailsEditPresenter(room)
@ -262,6 +286,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(anotherAvatarUri)
val fakePermissionsPresenter = FakePermissionsPresenter()
@ -298,6 +323,7 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(roomAvatarUri)
val presenter = createRoomDetailsEditPresenter(room)
@ -346,6 +372,7 @@ class RoomDetailsEditPresenterTest {
displayName = "fallback",
avatarUrl = null,
emitRoomInfo = true,
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(roomAvatarUri)
val presenter = createRoomDetailsEditPresenter(room)
@ -389,11 +416,18 @@ class RoomDetailsEditPresenterTest {
@Test
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(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
setNameResult = setNameResult,
setTopicResult = setTopicResult,
removeAvatarResult = removeAvatarResult,
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
@ -405,16 +439,20 @@ class RoomDetailsEditPresenterTest {
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(5)
assertThat(room.newName).isEqualTo("New name")
assertThat(room.newTopic).isEqualTo("New topic")
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isTrue()
setNameResult.assertions().isCalledOnce().with(value("New name"))
setTopicResult.assertions().isCalledOnce().with(value("New topic"))
removeAvatarResult.assertions().isCalledOnce()
}
}
@Test
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -423,17 +461,18 @@ class RoomDetailsEditPresenterTest {
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name "))
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic "))
initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents()
}
}
@Test
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -441,17 +480,18 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(""))
initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents()
}
}
@Test
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)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -459,17 +499,20 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(""))
initialState.eventSink(RoomDetailsEditEvents.Save)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
cancelAndIgnoreRemainingEvents()
}
}
@Test
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()
val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
@ -478,17 +521,19 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(3)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents)
assertThat(room.removedAvatar).isFalse()
skipItems(4)
updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents))
}
}
@Test
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)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
val presenter = createRoomDetailsEditPresenter(room)
@ -498,11 +543,7 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(2)
assertThat(room.newName).isNull()
assertThat(room.newTopic).isNull()
assertThat(room.newAvatarData).isNull()
assertThat(room.removedAvatar).isFalse()
skipItems(3)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
}
}
@ -514,9 +555,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
).apply {
givenSetNameResult(Result.failure(Throwable("!")))
}
setNameResult = { Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
}
@ -527,9 +568,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
).apply {
givenSetTopicResult(Result.failure(Throwable("!")))
}
setTopicResult = { Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
}
@ -540,9 +581,9 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
).apply {
givenRemoveAvatarResult(Result.failure(Throwable("!")))
}
removeAvatarResult = { Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
}
@ -554,18 +595,22 @@ class RoomDetailsEditPresenterTest {
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
emitRoomInfo = true,
).apply {
givenUpdateAvatarResult(Result.failure(Throwable("!")))
}
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
}
@Test
fun `present - CancelSaveChanges resets save action state`() = runTest {
givenPickerReturnsFile()
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply {
givenSetTopicResult(Result.failure(Throwable("!")))
}
val room = aMatrixRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -573,7 +618,7 @@ class RoomDetailsEditPresenterTest {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo"))
initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(2)
skipItems(3)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)

View file

@ -52,7 +52,10 @@ class RoomMemberListPresenterTest {
@Test
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
givenRoomInfo(aRoomInfo(joinedMembersCount = 2))
}
@ -78,7 +81,12 @@ class RoomMemberListPresenterTest {
@Test
fun `open search`() = runTest {
val presenter = createPresenter()
val presenter = createPresenter(
matrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) },
canInviteResult = { Result.success(true) }
)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -93,7 +101,12 @@ class RoomMemberListPresenterTest {
@Test
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) {
presenter.present()
}.test {
@ -112,7 +125,12 @@ class RoomMemberListPresenterTest {
@Test
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) {
presenter.present()
}.test {
@ -134,9 +152,10 @@ class RoomMemberListPresenterTest {
@Test
fun `present - asynchronously sets canInvite when user has correct power level`() = runTest {
val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply {
givenCanInviteResult(Result.success(true))
}
matrixRoom = FakeMatrixRoom(
canInviteResult = { Result.success(true) },
updateMembersResult = { Result.success(Unit) }
)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -150,9 +169,10 @@ class RoomMemberListPresenterTest {
@Test
fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest {
val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply {
givenCanInviteResult(Result.success(false))
}
matrixRoom = FakeMatrixRoom(
canInviteResult = { Result.success(false) },
updateMembersResult = { Result.success(Unit) }
)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -166,9 +186,10 @@ class RoomMemberListPresenterTest {
@Test
fun `present - asynchronously sets canInvite when power level check fails`() = runTest {
val presenter = createPresenter(
matrixRoom = FakeMatrixRoom().apply {
givenCanInviteResult(Result.failure(Throwable("Eek")))
}
matrixRoom = FakeMatrixRoom(
canInviteResult = { Result.failure(Throwable("Eek")) },
updateMembersResult = { Result.success(Unit) }
)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -183,7 +204,14 @@ class RoomMemberListPresenterTest {
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
val navigator = FakeRoomMemberListNavigator()
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) {
presenter.present()
}.test {
@ -205,7 +233,14 @@ class RoomMemberListPresenterTest {
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
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) {
presenter.present()
}.test {
@ -236,10 +271,12 @@ private fun TestScope.createDataSource(
@ExperimentalCoroutinesApi
private fun TestScope.createPresenter(
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
matrixRoom: MatrixRoom = FakeMatrixRoom(),
matrixRoom: MatrixRoom = FakeMatrixRoom(
updateMembersResult = { Result.success(Unit) }
),
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator { }
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
) = RoomMemberListPresenter(
room = matrixRoom,
roomMemberListDataSource = roomMemberListDataSource,

View file

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

View file

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

View file

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

View file

@ -275,7 +275,10 @@ class ChangeRolesPresenterTest {
@Test
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()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100)))
}
@ -325,7 +328,10 @@ class ChangeRolesPresenterTest {
@Test
fun `present - Save will just save the data for moderators`() = runTest {
val analyticsService = FakeAnalyticsService()
val room = FakeMatrixRoom().apply {
val room = FakeMatrixRoom(
updateUserRoleResult = { Result.success(Unit) },
updateMembersResult = { Result.success(Unit) },
).apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
}
@ -351,10 +357,11 @@ class ChangeRolesPresenterTest {
@Test
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()))
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
givenUpdateUserRoleResult(Result.failure(IllegalStateException("Failed")))
}
val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room)
moleculeFlow(RecompositionMode.Immediate) {

View file

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