change (member moderation) : fix all existing tests
This commit is contained in:
parent
e405bf80a6
commit
0b6c5964a1
12 changed files with 64 additions and 726 deletions
|
|
@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr
|
|||
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
|
||||
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
||||
import io.element.android.features.roomcall.api.aStandByCallState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
@ -1182,6 +1183,9 @@ class MessagesPresenterTest {
|
|||
textEditorState = aTextEditorStateMarkdown(initialText = "", initialFocus = false)
|
||||
)
|
||||
},
|
||||
roomMemberModerationPresenter: Presenter<RoomMemberModerationState> = Presenter {
|
||||
aRoomMemberModerationState()
|
||||
},
|
||||
encryptionService: FakeEncryptionService = FakeEncryptionService(),
|
||||
actionListEventSink: (ActionListEvents) -> Unit = {},
|
||||
): MessagesPresenter {
|
||||
|
|
@ -1199,6 +1203,7 @@ class MessagesPresenterTest {
|
|||
linkPresenter = { aLinkState() },
|
||||
pinnedMessagesBannerPresenter = { aLoadedPinnedMessagesBannerState() },
|
||||
roomCallStatePresenter = { aStandByCallState() },
|
||||
roomMemberModerationPresenter = roomMemberModerationPresenter,
|
||||
syncService = FakeSyncService(),
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
navigator = navigator,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot
|
|||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.getDisplayName
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -310,40 +313,42 @@ class MessagesViewTest {
|
|||
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on the avatar of the sender of an Event invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
fun `clicking on the avatar of the sender of an Event emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
val timelineItem = state.timelineState.timelineItems.first()
|
||||
ensureCalledOnceWithParam(
|
||||
param = (timelineItem as TimelineItem.Event).senderId
|
||||
) { callback ->
|
||||
rule.setMessagesView(
|
||||
state = state,
|
||||
onUserDataClick = callback,
|
||||
val timelineEvent = state.timelineState.timelineItems.filterIsInstance<TimelineItem.Event>().first()
|
||||
rule.setMessagesView(state = state)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick()
|
||||
eventsRecorder.assertSingle(
|
||||
MessagesEvents.OnUserClicked(
|
||||
MatrixUser(
|
||||
userId = timelineEvent.senderId,
|
||||
displayName = timelineEvent.senderProfile.getDisplayName(),
|
||||
avatarUrl = timelineEvent.senderProfile.getAvatarUrl()
|
||||
)
|
||||
)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `clicking on the display name of the sender of an Event invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
val timelineItem = state.timelineState.timelineItems.first()
|
||||
ensureCalledOnceWithParam(
|
||||
param = (timelineItem as TimelineItem.Event).senderId
|
||||
) { callback ->
|
||||
rule.setMessagesView(
|
||||
state = state,
|
||||
onUserDataClick = callback,
|
||||
fun `clicking on the display name of the sender of an Event emits expected event`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvents>()
|
||||
val state = aMessagesState(eventSink = eventsRecorder)
|
||||
val timelineEvent = state.timelineState.timelineItems.filterIsInstance<TimelineItem.Event>().first()
|
||||
rule.setMessagesView(state = state)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick()
|
||||
eventsRecorder.assertSingle(
|
||||
MessagesEvents.OnUserClicked(
|
||||
MatrixUser(
|
||||
userId = timelineEvent.senderId,
|
||||
displayName = timelineEvent.senderProfile.getDisplayName(),
|
||||
avatarUrl = timelineEvent.senderProfile.getAvatarUrl()
|
||||
)
|
||||
)
|
||||
rule.onNodeWithTag(TestTags.timelineItemSenderName.value, useUnmergedTree = true).performClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
|||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
|
|
@ -99,7 +100,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setPinne
|
|||
state: PinnedMessagesListState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
onEventClick: (event: TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onUserDataClick: (MatrixUser) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onLinkClick: (Link) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onLinkLongClick: (Link) -> Unit = EnsureNeverCalledWithParam(),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.core.UniqueId
|
|||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
|
|
@ -175,7 +176,7 @@ class TimelineViewTest {
|
|||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimelineView(
|
||||
state: TimelineState,
|
||||
timelineProtectionState: TimelineProtectionState = aTimelineProtectionState(),
|
||||
onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onUserDataClick: (MatrixUser) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onLinkClick: (Link) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onMessageClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onMessageLongClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import io.element.android.services.analytics.api.AnalyticsService
|
|||
class RoomMemberListNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: RoomMemberListPresenter.Factory,
|
||||
private val presenter: RoomMemberListPresenter,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val roomMemberModerationRenderer: RoomMemberModerationRenderer,
|
||||
) : Node(buildContext, plugins = plugins), RoomMemberListNavigator {
|
||||
|
|
@ -39,7 +39,6 @@ class RoomMemberListNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private val callbacks = plugins<Callback>()
|
||||
private val presenter = presenterFactory.create(this)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ import androidx.compose.runtime.produceState
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.roommembermoderation.api.ModerationAction
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
|
|
@ -53,12 +51,7 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
|||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val roomMembersModerationPresenter: Presenter<RoomMemberModerationState>,
|
||||
private val encryptionService: EncryptionService,
|
||||
@Assisted private val navigator: RoomMemberListNavigator,
|
||||
) : Presenter<RoomMemberListState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(navigator: RoomMemberListNavigator): RoomMemberListPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomMemberListState {
|
||||
|
|
@ -168,10 +161,8 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
|||
is RoomMemberListEvents.RoomMemberSelected ->
|
||||
if (event.roomMember.membership == RoomMembershipState.BAN) {
|
||||
roomModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(ModerationAction.UnbanUser, event.roomMember.toMatrixUser()))
|
||||
} else if (!isDm.value && (roomModerationState.canBan || roomModerationState.canKick)) {
|
||||
roomModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.roomMember.toMatrixUser()))
|
||||
} else {
|
||||
navigator.openRoomMemberDetails(event.roomMember.userId)
|
||||
roomModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.roomMember.toMatrixUser()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import app.cash.molecule.RecompositionMode
|
|||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState
|
||||
import io.element.android.features.roomdetails.impl.members.moderation.aRoomMembersModerationState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
|
@ -24,7 +23,6 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
|||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -42,12 +40,12 @@ class RoomMemberListPresenterTest {
|
|||
fun `member loading is done automatically on start, but is async`() = runTest {
|
||||
val room = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
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))
|
||||
}
|
||||
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))
|
||||
}
|
||||
)
|
||||
val presenter = createPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -97,9 +95,9 @@ class RoomMemberListPresenterTest {
|
|||
val presenter = createPresenter(
|
||||
joinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
canInviteResult = { Result.success(true) }
|
||||
)
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
canInviteResult = { Result.success(true) }
|
||||
)
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -204,12 +202,12 @@ class RoomMemberListPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
||||
val navigator = FakeRoomMemberListNavigator()
|
||||
val roomMembersModerationStateLambda = { aRoomMembersModerationState(canDisplayModerationActions = false) }
|
||||
fun `present - RoomMemberSelected will open the moderation options when target user is not banned`() = runTest {
|
||||
val roomMemberModerationPresenter= Presenter {
|
||||
aRoomMemberModerationState(canBan = true, canKick = true)
|
||||
}
|
||||
val presenter = createPresenter(
|
||||
roomMembersModerationStateLambda = roomMembersModerationStateLambda,
|
||||
navigator = navigator,
|
||||
roomMemberModerationPresenter = roomMemberModerationPresenter,
|
||||
joinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
|
|
@ -222,36 +220,6 @@ class RoomMemberListPresenterTest {
|
|||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMemberListEvents.RoomMemberSelected(aVictor()))
|
||||
assertThat(navigator.openRoomMemberDetailsCallCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - RoomMemberSelected will open the moderation options if the current user can use them`() = runTest {
|
||||
val navigator = FakeRoomMemberListNavigator()
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMembersModerationStateLambda = {
|
||||
aRoomMembersModerationState(
|
||||
canDisplayModerationActions = true,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
}
|
||||
val presenter = createPresenter(
|
||||
roomMembersModerationStateLambda = roomMembersModerationStateLambda,
|
||||
navigator = navigator,
|
||||
joinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
canInviteResult = { Result.success(true) }
|
||||
)
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMemberListEvents.RoomMemberSelected(aVictor()))
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -277,19 +245,19 @@ private fun TestScope.createDataSource(
|
|||
private fun TestScope.createPresenter(
|
||||
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
joinedRoom: JoinedRoom = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
updateMembersResult = { Result.success(Unit) }
|
||||
)
|
||||
),
|
||||
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
||||
roomMembersModerationStateLambda: () -> RoomMembersModerationState = { aRoomMembersModerationState() },
|
||||
encryptedService: FakeEncryptionService = FakeEncryptionService(),
|
||||
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
|
||||
roomMemberModerationPresenter: Presenter<RoomMemberModerationState> = Presenter {
|
||||
aRoomMemberModerationState()
|
||||
},
|
||||
) = RoomMemberListPresenter(
|
||||
room = joinedRoom,
|
||||
roomMemberListDataSource = roomMemberListDataSource,
|
||||
coroutineDispatchers = coroutineDispatchers,
|
||||
roomMembersModerationPresenter = { roomMembersModerationStateLambda() },
|
||||
roomMembersModerationPresenter = roomMemberModerationPresenter,
|
||||
encryptionService = encryptedService,
|
||||
navigator = navigator,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,351 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomdetails.impl.members.moderation
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.RoomModeration
|
||||
import io.element.android.features.roomdetails.impl.aJoinedRoom
|
||||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
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.RoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.test.A_REASON
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class RoomMembersModerationPresenterTest {
|
||||
@Test
|
||||
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
isPublic = true,
|
||||
activeMemberCount = 2,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
).apply {
|
||||
givenRoomInfo(aRoomInfo(isDirect = true, activeMembersCount = 2))
|
||||
}
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
presenter.test {
|
||||
assertThat(awaitItem().canDisplayModerationActions).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
activeMemberCount = 10,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().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 = aJoinedRoom(
|
||||
activeMemberCount = 10,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().canDisplayModerationActions).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
with(awaitItem()) {
|
||||
assertThat(this.selectedRoomMember).isNotNull()
|
||||
assertThat(this.selectedRoomMember?.userId).isEqualTo(selectedMember.userId)
|
||||
assertThat(actions).containsExactly(
|
||||
ModerationAction.DisplayProfile(selectedMember.userId),
|
||||
ModerationAction.KickUser(selectedMember.userId),
|
||||
ModerationAction.BanUser(selectedMember.userId)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
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 = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
with(awaitItem()) {
|
||||
assertThat(this.selectedRoomMember).isNotNull()
|
||||
assertThat(this.selectedRoomMember?.userId).isEqualTo(selectedMember.userId)
|
||||
assertThat(actions).containsExactly(
|
||||
ModerationAction.DisplayProfile(selectedMember.userId),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
with(awaitItem()) {
|
||||
assertThat(selectedRoomMember).isNull()
|
||||
assertThat(unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Kick requires confirmation and then kicks the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val kickUserResult = lambdaRecorder<UserId, String?, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||
val room = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
kickUserResult = kickUserResult,
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.KickUser)
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.kickUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
// Confirm
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.DoKickUser(reason = A_REASON))
|
||||
skipItems(1)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.actions).isEmpty()
|
||||
assertThat(loadingState.kickUserAsyncAction).isEqualTo(AsyncAction.Loading)
|
||||
with(awaitItem()) {
|
||||
assertThat(kickUserAsyncAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
assertThat(selectedRoomMember).isNull()
|
||||
}
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.KickMember))
|
||||
kickUserResult.assertions().isCalledOnce().with(
|
||||
value(selectedMember.userId),
|
||||
value(A_REASON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val banUserResult = lambdaRecorder<UserId, String?, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||
val room = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
banUserResult = banUserResult,
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser)
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.banUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
// Confirm
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.DoBanUser(reason = A_REASON))
|
||||
skipItems(1)
|
||||
val loadingItem = awaitItem()
|
||||
assertThat(loadingItem.actions).isEmpty()
|
||||
assertThat(loadingItem.selectedRoomMember).isNull()
|
||||
assertThat(loadingItem.banUserAsyncAction).isEqualTo(AsyncAction.Loading)
|
||||
with(awaitItem()) {
|
||||
assertThat(banUserAsyncAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
assertThat(selectedRoomMember).isNull()
|
||||
}
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.BanMember))
|
||||
banUserResult.assertions().isCalledOnce().with(
|
||||
value(selectedMember.userId),
|
||||
value(A_REASON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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 = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
unBanUserResult = { _, _ -> Result.success(Unit) },
|
||||
).apply {
|
||||
givenRoomMembersState(RoomMembersState.Ready(persistentListOf(selectedMember)))
|
||||
}
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
// Displays unban confirmation dialog
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.selectedRoomMember).isNull()
|
||||
assertThat(confirmingState.actions).isEmpty()
|
||||
assertThat(confirmingState.unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember))
|
||||
// Confirms unban
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(selectedMember.userId))
|
||||
assertThat(awaitItem().unbanUserAsyncAction).isEqualTo(AsyncAction.Loading)
|
||||
with(awaitItem()) {
|
||||
assertThat(unbanUserAsyncAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
assertThat(selectedRoomMember).isNull()
|
||||
}
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.UnbanMember))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Reset removes the selected user and actions`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||
)
|
||||
val presenter = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
// Select a user
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
// Reset state
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.Reset)
|
||||
val finalItem = awaitItem()
|
||||
assertThat(finalItem.selectedRoomMember).isNull()
|
||||
assertThat(finalItem.actions).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Reset resets any async actions`() = runTest {
|
||||
val room = aJoinedRoom(
|
||||
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 = createRoomMembersModerationPresenter(joinedRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialItem = awaitItem()
|
||||
// Kick user and fail
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.DoKickUser(reason = ""))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
// Reset it
|
||||
initialItem.eventSink(RoomMembersModerationEvents.Reset)
|
||||
assertThat(awaitItem().kickUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
|
||||
// Ban user and fail
|
||||
initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.DoBanUser(reason = ""))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
// Reset it
|
||||
initialItem.eventSink(RoomMembersModerationEvents.Reset)
|
||||
assertThat(awaitItem().banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
|
||||
// Unban user and fail
|
||||
initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor().copy(membership = RoomMembershipState.BAN)))
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Confirming::class.java)
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(aVictor().userId))
|
||||
assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
// Reset it
|
||||
initialItem.eventSink(RoomMembersModerationEvents.Reset)
|
||||
assertThat(awaitItem().unbanUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRoomMembersModerationPresenter(
|
||||
joinedRoom: FakeJoinedRoom = aJoinedRoom(),
|
||||
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
): RoomMembersModerationPresenter {
|
||||
return RoomMembersModerationPresenter(
|
||||
room = joinedRoom,
|
||||
dispatchers = dispatchers,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomdetails.impl.members.moderation
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.roomdetails.impl.R
|
||||
import io.element.android.features.roomdetails.impl.members.anAlice
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.test.A_REASON
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnceWithParam
|
||||
import io.element.android.tests.testutils.pressBackKey
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class RoomMembersModerationViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Ignore("This test is not passing yet, need to investigate")
|
||||
@Test
|
||||
fun `clicking on back emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
actions = listOf(
|
||||
ModerationAction.DisplayProfile(roomMember.userId),
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
rule.pressBackKey()
|
||||
// Give time for the bottom sheet to animate
|
||||
rule.mainClock.advanceTimeBy(1_000)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on 'See user info' invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>(expectEvents = false)
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
actions = listOf(
|
||||
ModerationAction.DisplayProfile(roomMember.userId),
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
ensureCalledOnceWithParam(roomMember.userId) { callback ->
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
onDisplayMemberProfile = callback
|
||||
)
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_member_user_info)
|
||||
}
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on 'Remove member' emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
actions = listOf(
|
||||
ModerationAction.DisplayProfile(roomMember.userId),
|
||||
ModerationAction.KickUser(roomMember.userId),
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_remove)
|
||||
// Give time for the bottom sheet to animate
|
||||
rule.mainClock.advanceTimeBy(1_000)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.KickUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cancelling 'Remove member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove member' reason edition then validation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
val reason = rule.activity.getString(CommonStrings.common_reason)
|
||||
rule.onNodeWithText(reason).performTextInput(A_REASON)
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_kick_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.DoKickUser(reason = A_REASON))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_kick_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.DoKickUser(reason = ""))
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on 'Remove and ban member' emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
actions = listOf(
|
||||
ModerationAction.DisplayProfile(roomMember.userId),
|
||||
ModerationAction.KickUser(roomMember.userId),
|
||||
ModerationAction.BanUser(roomMember.userId),
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(R.string.screen_room_member_list_manage_member_remove_confirmation_ban)
|
||||
// Give time for the bottom sheet to animate
|
||||
rule.mainClock.advanceTimeBy(1_000)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cancelling 'Remove and ban member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove and ban member' reason edition emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
val reason = rule.activity.getString(CommonStrings.common_reason)
|
||||
rule.onNodeWithText(reason).performTextInput(A_REASON)
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_ban_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.DoBanUser(reason = A_REASON))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove and ban member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.screen_bottom_sheet_manage_room_member_ban_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.DoBanUser(reason = ""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cancelling 'Unban member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Unban member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(R.string.screen_room_member_list_manage_member_unban_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.UnbanUser(roomMember.userId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomMembersModerationView(
|
||||
state: RoomMembersModerationState,
|
||||
onDisplayMemberProfile: (UserId) -> Unit = EnsureNeverCalledWithParam()
|
||||
) {
|
||||
setContent {
|
||||
RoomMembersModerationView(
|
||||
state = state,
|
||||
onDisplayMemberProfile = onDisplayMemberProfile,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,10 @@
|
|||
|
||||
package io.element.android.features.roommembermoderation.impl
|
||||
|
||||
import io.element.android.features.roommembermoderation.api.ModerationAction
|
||||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -12,15 +12,11 @@ import io.element.android.features.roommembermoderation.api.ModerationAction
|
|||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
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.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.room.toMatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
class RoomMemberModerationStateProvider : PreviewParameterProvider<InternalRoomMemberModerationState> {
|
||||
class InternalRoomMemberModerationStateProvider : PreviewParameterProvider<InternalRoomMemberModerationState> {
|
||||
override val values: Sequence<InternalRoomMemberModerationState>
|
||||
get() = sequenceOf(
|
||||
aRoomMembersModerationState(
|
||||
|
|
@ -317,7 +317,7 @@ private fun RoomMemberActionsBottomSheet(
|
|||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RoomMembersModerationViewPreview(@PreviewParameter(RoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) {
|
||||
internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) {
|
||||
ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue