diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt index 69e8d72be6..8cd5db01d6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt @@ -62,6 +62,7 @@ internal fun aChangeRolesState( exitState: AsyncAction = AsyncAction.Uninitialized, savingState: AsyncAction = AsyncAction.Uninitialized, canRemoveMember: (UserId) -> Boolean = { true }, + eventSink: (ChangeRolesEvent) -> Unit = {}, ) = ChangeRolesState( role = role, query = query, @@ -72,7 +73,7 @@ internal fun aChangeRolesState( exitState = exitState, savingState = savingState, canChangeMemberRole = canRemoveMember, - eventSink = {}, + eventSink = eventSink, ) internal fun aChangeRolesStateWithSelectedUsers() = aChangeRolesState( diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt new file mode 100644 index 0000000000..c5aa04a0db --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesViewTest.kt @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.rolesandpermissions.changeroles + +import androidx.activity.ComponentActivity +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onAllNodesWithText +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 com.google.common.truth.Truth.assertThat +import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.ChangeRolesEvent +import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.ChangeRolesState +import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.ChangeRolesView +import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.aChangeRolesState +import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.aChangeRolesStateWithSelectedUsers +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.toMatrixUser +import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.pressBack +import kotlinx.collections.immutable.toImmutableList +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ChangeRolesViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `click on back icon search not active emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + eventSink = eventsRecorder, + ), + ) + rule.pressBack() + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.Exit, + ) + ) + } + + @Test + fun `click on back icon search active emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + isSearchActive = true, + eventSink = eventsRecorder, + ), + ) + rule.pressBack() + // This event should be there, maybe a problem with the SearchBar + // It's working fine in the app, so let's ignore it for now + // eventsRecorder.assertSingle(ChangeRolesEvent.ToggleSearchActive) + } + + @Test + fun `click on search bar emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.common_search_for_someone) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + // This event should be there, maybe a problem with the SearchBar + // It's working fine in the app, so let's ignore it for now + // ChangeRolesEvent.ToggleSearchActive, + ) + ) + } + + @Test + fun `click on save button emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + hasPendingChanges = true, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_save) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.Save, + ) + ) + } + + @Test + fun `testing exit confirmation dialog ok emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + exitState = AsyncAction.Confirming, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_ok) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.Exit, + ) + ) + } + + @Test + fun `testing exit confirmation dialog cancel emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + exitState = AsyncAction.Confirming, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.CancelExit + ) + ) + } + + @Test + fun `testing saving dialog failure OK emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + savingState = AsyncAction.Failure(Exception("boom")), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_ok) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.ClearError, + ) + ) + } + + @Test + fun `testing saving confirmation dialog for admin OK emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + role = RoomMember.Role.ADMIN, + savingState = AsyncAction.Confirming, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_ok) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.Save, + ) + ) + } + + @Test + fun `testing saving confirmation dialog for admin cancel emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setChangeRolesView( + state = aChangeRolesState( + role = RoomMember.Role.ADMIN, + savingState = AsyncAction.Confirming, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.ClearError, + ) + ) + } + + @Test + fun `testing removing user from selected list emits the expected event`() { + val eventsRecorder = EventsRecorder() + val selectedUsers = aMatrixUserList().take(2) + val userToDeselect = selectedUsers[1] + assertThat(userToDeselect.displayName).isEqualTo("Bob") + rule.setChangeRolesView( + state = aChangeRolesStateWithSelectedUsers().copy( + selectedUsers = selectedUsers.toImmutableList(), + eventSink = eventsRecorder, + ), + ) + // Unselect the user from the row list + val contentDescription = rule.activity.getString(CommonStrings.action_remove) + rule.onNodeWithContentDescription(contentDescription).performClick() + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.UserSelectionToggled(userToDeselect), + ) + ) + } + + @Test + fun `testing adding user to the selected list emits the expected event`() { + val eventsRecorder = EventsRecorder() + val selectedUsers = aMatrixUserList().take(2) + val state = aChangeRolesStateWithSelectedUsers().copy( + selectedUsers = selectedUsers.toImmutableList(), + eventSink = eventsRecorder, + ) + val userToSelect = (state.searchResults as SearchBarResultState.Results).results[2].toMatrixUser() + assertThat(userToSelect.displayName).isEqualTo("Carol") + rule.setChangeRolesView( + state = state, + ) + // Select the user from the rom list + rule.onNodeWithText("Carol").performClick() + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.UserSelectionToggled(userToSelect), + ) + ) + } + + @Test + fun `testing removing user to the selected list emits the expected event`() { + val eventsRecorder = EventsRecorder() + val selectedUsers = aMatrixUserList().take(2) + val state = aChangeRolesStateWithSelectedUsers().copy( + selectedUsers = selectedUsers.toImmutableList(), + eventSink = eventsRecorder, + ) + val userToSelect = (state.searchResults as SearchBarResultState.Results).results[1].toMatrixUser() + assertThat(userToSelect.displayName).isEqualTo("Bob") + rule.setChangeRolesView( + state = state, + ) + // Select the user from the rom list + rule.onAllNodesWithText("Bob")[1].performClick() + eventsRecorder.assertList( + listOf( + ChangeRolesEvent.QueryChanged(""), + ChangeRolesEvent.UserSelectionToggled(userToSelect), + ) + ) + } +} + +private fun AndroidComposeTestRule.setChangeRolesView( + state: ChangeRolesState, + onBackPressed: () -> Unit = EnsureNeverCalled(), +) { + setContent { + CompositionLocalProvider(LocalInspectionMode provides true) { + ChangeRolesView( + state = state, + onBackPressed = onBackPressed, + ) + } + } +}