Add tests for space manage rooms mode
This commit is contained in:
@@ -22,16 +22,18 @@ import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
@@ -355,6 +357,213 @@ class SpacePresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - enter manage mode`() = runTest {
|
||||
val presenter = createSpacePresenter()
|
||||
presenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(state.isManageMode).isFalse()
|
||||
state.eventSink(SpaceEvents.EnterManageMode)
|
||||
val manageModeState = awaitItem()
|
||||
assertThat(manageModeState.isManageMode).isTrue()
|
||||
assertThat(manageModeState.selectedRoomIds).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - exit manage mode clears selection`() = runTest {
|
||||
val presenter = createSpacePresenter()
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(SpaceEvents.EnterManageMode)
|
||||
initialState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
initialState.eventSink(SpaceEvents.ExitManageMode)
|
||||
val finalState = expectMostRecentItem()
|
||||
assertThat(finalState.isManageMode).isFalse()
|
||||
assertThat(finalState.selectedRoomIds).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - toggle room selection`() = runTest {
|
||||
val presenter = createSpacePresenter()
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(SpaceEvents.EnterManageMode)
|
||||
// Select a room
|
||||
initialState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
var latestState = expectMostRecentItem()
|
||||
assertThat(latestState.selectedRoomIds).containsExactly(A_ROOM_ID)
|
||||
// Deselect the room
|
||||
latestState.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
latestState = expectMostRecentItem()
|
||||
assertThat(latestState.selectedRoomIds).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - remove rooms success`() = runTest {
|
||||
val removeChildFromSpaceResult = lambdaRecorder<RoomId, RoomId, Result<Unit>> { _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val aRoom = aSpaceRoom(
|
||||
roomId = A_ROOM_ID,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
||||
initialSpaceRoomsValue = listOf(aRoom),
|
||||
paginateResult = { Result.success(Unit) },
|
||||
)
|
||||
val presenter = createSpacePresenter(
|
||||
spaceRoomList = fakeSpaceRoomList,
|
||||
spaceService = FakeSpaceService(
|
||||
removeChildFromSpaceResult = removeChildFromSpaceResult,
|
||||
),
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem() // Initial empty state
|
||||
advanceUntilIdle()
|
||||
val stateWithChildren = awaitItem()
|
||||
assertThat(stateWithChildren.children).hasSize(1)
|
||||
stateWithChildren.eventSink(SpaceEvents.EnterManageMode)
|
||||
stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms)
|
||||
stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval)
|
||||
advanceUntilIdle()
|
||||
val successState = expectMostRecentItem()
|
||||
assertThat(successState.removeRoomsAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
assertThat(successState.isManageMode).isFalse()
|
||||
assertThat(successState.children).isEmpty()
|
||||
removeChildFromSpaceResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - remove rooms partial failure`() = runTest {
|
||||
val aRoom1 = aSpaceRoom(
|
||||
roomId = A_ROOM_ID,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val aRoom2 = aSpaceRoom(
|
||||
roomId = A_ROOM_ID_2,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val removeChildFromSpaceResult = lambdaRecorder<RoomId, RoomId, Result<Unit>> { _, childId ->
|
||||
if (childId == A_ROOM_ID_2) Result.failure(AN_EXCEPTION)
|
||||
else Result.success(Unit)
|
||||
}
|
||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
||||
initialSpaceRoomsValue = listOf(aRoom1, aRoom2),
|
||||
paginateResult = { Result.success(Unit) },
|
||||
)
|
||||
val presenter = createSpacePresenter(
|
||||
spaceRoomList = fakeSpaceRoomList,
|
||||
spaceService = FakeSpaceService(
|
||||
removeChildFromSpaceResult = removeChildFromSpaceResult,
|
||||
),
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem() // Initial empty state
|
||||
advanceUntilIdle()
|
||||
val stateWithChildren = awaitItem()
|
||||
assertThat(stateWithChildren.children).hasSize(2)
|
||||
stateWithChildren.eventSink(SpaceEvents.EnterManageMode)
|
||||
stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID_2))
|
||||
stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms)
|
||||
stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval)
|
||||
advanceUntilIdle()
|
||||
val failureState = expectMostRecentItem()
|
||||
assertThat(failureState.removeRoomsAction.isFailure()).isTrue()
|
||||
// Successfully removed room should be filtered out
|
||||
assertThat(failureState.children.map { it.roomId }).doesNotContain(A_ROOM_ID)
|
||||
// Failed room should still be present
|
||||
assertThat(failureState.children.map { it.roomId }).contains(A_ROOM_ID_2)
|
||||
removeChildFromSpaceResult.assertions().isCalledExactly(2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - children filtered in manage mode shows only rooms`() = runTest {
|
||||
val aRoom = aSpaceRoom(
|
||||
roomId = A_ROOM_ID,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val aSubSpace = aSpaceRoom(
|
||||
roomId = A_ROOM_ID_2,
|
||||
roomType = RoomType.Space,
|
||||
)
|
||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
||||
initialSpaceRoomsValue = listOf(aRoom, aSubSpace),
|
||||
paginateResult = { Result.success(Unit) },
|
||||
)
|
||||
val presenter = createSpacePresenter(spaceRoomList = fakeSpaceRoomList)
|
||||
presenter.test {
|
||||
awaitItem() // Initial empty state
|
||||
advanceUntilIdle()
|
||||
val stateWithChildren = awaitItem()
|
||||
// Both room and space visible initially
|
||||
assertThat(stateWithChildren.children).hasSize(2)
|
||||
assertThat(stateWithChildren.isManageMode).isFalse()
|
||||
stateWithChildren.eventSink(SpaceEvents.EnterManageMode)
|
||||
val manageModeState = expectMostRecentItem()
|
||||
// Only rooms visible in manage mode
|
||||
assertThat(manageModeState.children).hasSize(1)
|
||||
assertThat(manageModeState.children.first().roomId).isEqualTo(A_ROOM_ID)
|
||||
assertThat(manageModeState.children.first().isSpace).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - removed rooms persist after flow update`() = runTest {
|
||||
val removeChildFromSpaceResult = lambdaRecorder<RoomId, RoomId, Result<Unit>> { _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val aRoom1 = aSpaceRoom(
|
||||
roomId = A_ROOM_ID,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val aRoom2 = aSpaceRoom(
|
||||
roomId = A_ROOM_ID_2,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val aRoom3 = aSpaceRoom(
|
||||
roomId = A_ROOM_ID_3,
|
||||
roomType = RoomType.Room,
|
||||
)
|
||||
val spaceRoomList = FakeSpaceRoomList(
|
||||
initialSpaceRoomsValue = listOf(aRoom1, aRoom2),
|
||||
paginateResult = { Result.success(Unit) },
|
||||
)
|
||||
val presenter = createSpacePresenter(
|
||||
spaceRoomList = spaceRoomList,
|
||||
spaceService = FakeSpaceService(
|
||||
removeChildFromSpaceResult = removeChildFromSpaceResult,
|
||||
),
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem() // Initial empty state
|
||||
advanceUntilIdle()
|
||||
val stateWithChildren = awaitItem()
|
||||
stateWithChildren.eventSink(SpaceEvents.EnterManageMode)
|
||||
stateWithChildren.eventSink(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
stateWithChildren.eventSink(SpaceEvents.RemoveSelectedRooms)
|
||||
stateWithChildren.eventSink(SpaceEvents.ConfirmRoomRemoval)
|
||||
advanceUntilIdle()
|
||||
val successState = expectMostRecentItem()
|
||||
assertThat(successState.children.map { it.roomId }).doesNotContain(A_ROOM_ID)
|
||||
// Emit new flow update with a new room added (simulating server refresh)
|
||||
spaceRoomList.emitSpaceRooms(listOf(aRoom1, aRoom2, aRoom3))
|
||||
advanceUntilIdle()
|
||||
val afterFlowUpdate = awaitItem()
|
||||
// A_ROOM_ID should still be filtered out even though it's in the new emission
|
||||
assertThat(afterFlowUpdate.children.map { it.roomId }).doesNotContain(A_ROOM_ID)
|
||||
// But the other rooms should be present
|
||||
assertThat(afterFlowUpdate.children.map { it.roomId }).contains(A_ROOM_ID_2)
|
||||
assertThat(afterFlowUpdate.children.map { it.roomId }).contains(A_ROOM_ID_3)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createSpacePresenter(
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
room: BaseRoom = FakeBaseRoom(),
|
||||
|
||||
@@ -14,6 +14,8 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||
import org.junit.Test
|
||||
|
||||
class SpaceStateTest {
|
||||
@@ -45,4 +47,80 @@ class SpaceStateTest {
|
||||
)
|
||||
assertThat(state.isJoining(A_ROOM_ID)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isSelected returns true for selected room`() {
|
||||
val state = aSpaceState(
|
||||
selectedRoomIds = setOf(A_ROOM_ID)
|
||||
)
|
||||
assertThat(state.isSelected(A_ROOM_ID)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isSelected returns false for non-selected room`() {
|
||||
val state = aSpaceState(
|
||||
selectedRoomIds = setOf(A_ROOM_ID)
|
||||
)
|
||||
assertThat(state.isSelected(A_ROOM_ID_2)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test showManageRoomsAction true when canManageRooms and has room children`() {
|
||||
val state = aSpaceState(
|
||||
canManageRooms = true,
|
||||
children = listOf(aSpaceRoom(roomType = RoomType.Room))
|
||||
)
|
||||
assertThat(state.showManageRoomsAction).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test showManageRoomsAction false when canManageRooms but children empty`() {
|
||||
val state = aSpaceState(
|
||||
canManageRooms = true,
|
||||
children = emptyList()
|
||||
)
|
||||
assertThat(state.showManageRoomsAction).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test showManageRoomsAction false when canManageRooms but only space children`() {
|
||||
val state = aSpaceState(
|
||||
canManageRooms = true,
|
||||
children = listOf(aSpaceRoom(roomType = RoomType.Space))
|
||||
)
|
||||
assertThat(state.showManageRoomsAction).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test showManageRoomsAction false when has room children but canManageRooms false`() {
|
||||
val state = aSpaceState(
|
||||
canManageRooms = false,
|
||||
children = listOf(aSpaceRoom(roomType = RoomType.Room))
|
||||
)
|
||||
assertThat(state.showManageRoomsAction).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test selectedCount returns correct count`() {
|
||||
val state = aSpaceState(
|
||||
selectedRoomIds = setOf(A_ROOM_ID, A_ROOM_ID_2, A_ROOM_ID_3)
|
||||
)
|
||||
assertThat(state.selectedCount).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isRemoveButtonEnabled true when selectedRoomIds not empty`() {
|
||||
val state = aSpaceState(
|
||||
selectedRoomIds = setOf(A_ROOM_ID)
|
||||
)
|
||||
assertThat(state.isRemoveButtonEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isRemoveButtonEnabled false when selectedRoomIds empty`() {
|
||||
val state = aSpaceState(
|
||||
selectedRoomIds = emptySet()
|
||||
)
|
||||
assertThat(state.isRemoveButtonEnabled).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
@@ -29,6 +31,7 @@ import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.ensureCalledOnceWithParam
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import io.element.android.tests.testutils.pressBackKey
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
@@ -132,6 +135,71 @@ class SpaceViewTest {
|
||||
rule.onNodeWithText(A_ROOM_TOPIC).performClick()
|
||||
eventsRecorder.assertSingle(SpaceEvents.ShowTopicViewer(A_ROOM_TOPIC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking back in manage mode emits ExitManageMode event`() {
|
||||
val eventsRecorder = EventsRecorder<SpaceEvents>()
|
||||
rule.setSpaceView(
|
||||
aSpaceState(
|
||||
hasMoreToLoad = false,
|
||||
isManageMode = true,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.pressBackKey()
|
||||
eventsRecorder.assertSingle(SpaceEvents.ExitManageMode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on room in manage mode emits ToggleRoomSelection event`() {
|
||||
val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME)
|
||||
val eventsRecorder = EventsRecorder<SpaceEvents>()
|
||||
rule.setSpaceView(
|
||||
aSpaceState(
|
||||
children = listOf(aSpaceRoom),
|
||||
hasMoreToLoad = false,
|
||||
isManageMode = true,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.onNodeWithText(A_ROOM_NAME).performClick()
|
||||
eventsRecorder.assertSingle(SpaceEvents.ToggleRoomSelection(A_ROOM_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking remove button emits RemoveSelectedRooms event`() {
|
||||
val eventsRecorder = EventsRecorder<SpaceEvents>()
|
||||
rule.setSpaceView(
|
||||
aSpaceState(
|
||||
children = listOf(aSpaceRoom(roomId = A_ROOM_ID)),
|
||||
hasMoreToLoad = false,
|
||||
isManageMode = true,
|
||||
selectedRoomIds = setOf(A_ROOM_ID),
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_remove)
|
||||
eventsRecorder.assertSingle(SpaceEvents.RemoveSelectedRooms)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking confirm in removal dialog emits ConfirmRoomRemoval event`() {
|
||||
val eventsRecorder = EventsRecorder<SpaceEvents>()
|
||||
rule.setSpaceView(
|
||||
aSpaceState(
|
||||
children = listOf(aSpaceRoom(roomId = A_ROOM_ID)),
|
||||
hasMoreToLoad = false,
|
||||
isManageMode = true,
|
||||
selectedRoomIds = setOf(A_ROOM_ID),
|
||||
removeRoomsAction = AsyncAction.ConfirmingNoParams,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
)
|
||||
// Click on the Remove button in the confirmation dialog
|
||||
rule.clickOn(CommonStrings.action_remove, inDialog = true)
|
||||
eventsRecorder.assertSingle(SpaceEvents.ConfirmRoomRemoval)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSpaceView(
|
||||
|
||||
Reference in New Issue
Block a user