From 3684761bcdd52e1408293d12bd886a2c2cc7643b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 15:12:52 +0100 Subject: [PATCH] Add test on RoomDetailsView --- features/roomdetails/impl/build.gradle.kts | 1 + .../impl/RoomDetailsStateProvider.kt | 10 +- .../roomdetails/impl/RoomDetailsView.kt | 3 + .../roomdetails/impl/RoomDetailsViewTest.kt | 214 ++++++++++++++++++ .../android/libraries/testtags/TestTags.kt | 5 + .../tests/testutils/EnsureCalledOnce.kt | 19 ++ 6 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 3427f12577..ce2aed6e70 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.testtags) api(projects.features.roomdetails.api) api(projects.libraries.usersearch.api) api(projects.services.apperror.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 9c985f5c3e..542073dc23 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -90,7 +90,7 @@ fun aRoomDetailsState( roomType: RoomDetailsType = RoomDetailsType.Room, roomMemberDetailsState: RoomMemberDetailsState? = null, leaveRoomState: LeaveRoomState = aLeaveRoomState(), - roomNotificationSettings: RoomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false), + roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(), isFavorite: Boolean = false, eventSink: (RoomDetailsEvent) -> Unit = {}, ) = RoomDetailsState( @@ -112,6 +112,14 @@ fun aRoomDetailsState( eventSink = eventSink ) +fun aRoomNotificationSettings( + mode: RoomNotificationMode = RoomNotificationMode.MUTE, + isDefault: Boolean = false, +) = RoomNotificationSettings( + mode = mode, + isDefault = isDefault +) + fun aDmRoomDetailsState( isDmMemberIgnored: Boolean = false, roomName: String = "Daniel", diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index efa8e6e321..188d7ac753 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -80,6 +80,8 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.getBestName +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -299,6 +301,7 @@ private fun RoomHeaderSection( modifier = Modifier .size(70.dp) .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } + .testTag(TestTags.roomDetailAvatar) ) Spacer(modifier = Modifier.height(24.dp)) Text( diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt new file mode 100644 index 0000000000..34d582cf18 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -0,0 +1,214 @@ +/* + * 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.impl + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureCalledOnceWithTwoParams +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.pressBack +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 RoomDetailsViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `click on back invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + goBack = callback, + ) + rule.pressBack() + } + } + + @Test + fun `click on share invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + onShareRoom = callback, + ) + rule.clickOn(R.string.screen_room_details_share_room_title) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on room members invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openRoomMemberList = callback, + ) + rule.clickOn(CommonStrings.common_people) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on polls invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openPollHistory = callback, + ) + rule.clickOn(R.string.screen_polls_history_title) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on notification invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openRoomNotificationSettings = callback, + ) + rule.clickOn(R.string.screen_room_details_notification_title) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on invite people invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + canInvite = true, + ), + invitePeople = callback, + ) + rule.clickOn(R.string.screen_room_details_invite_people_title) + } + } + + @Test + fun `click on avatar test`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomAvatarUrl = "an_avatar_url", + ) + val callback = EnsureCalledOnceWithTwoParams(state.roomName, "an_avatar_url") + rule.setRoomDetailView( + state = state, + openAvatarPreview = callback, + ) + rule.onNodeWithTag(TestTags.roomDetailAvatar.value).performClick() + callback.assertSuccess() + } + + @Test + fun `click on mute emit expected event`() { + val eventsRecorder = EventsRecorder() + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES), + ) + rule.setRoomDetailView( + state = state, + ) + rule.clickOn(CommonStrings.common_mute) + eventsRecorder.assertSingle(RoomDetailsEvent.MuteNotification) + } + + @Test + fun `click on unmute emit expected event`() { + val eventsRecorder = EventsRecorder() + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.MUTE), + ) + rule.setRoomDetailView( + state = state, + ) + rule.clickOn(CommonStrings.common_unmute) + eventsRecorder.assertSingle(RoomDetailsEvent.UnmuteNotification) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on favorite emit expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.common_favourite) + eventsRecorder.assertSingle(RoomDetailsEvent.SetFavorite(true)) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on leave emit expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_room_details_leave_room_title) + eventsRecorder.assertSingle(RoomDetailsEvent.LeaveRoom) + } +} + +private fun AndroidComposeTestRule.setRoomDetailView( + state: RoomDetailsState = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + ), + goBack: () -> Unit = EnsureNeverCalled(), + onActionClicked: (RoomDetailsAction) -> Unit = EnsureNeverCalledWithParam(), + onShareRoom: () -> Unit = EnsureNeverCalled(), + onShareMember: (RoomMember) -> Unit = EnsureNeverCalledWithParam(), + openRoomMemberList: () -> Unit = EnsureNeverCalled(), + openRoomNotificationSettings: () -> Unit = EnsureNeverCalled(), + invitePeople: () -> Unit = EnsureNeverCalled(), + openAvatarPreview: (name: String, url: String) -> Unit = EnsureNeverCalledWithTwoParams(), + openPollHistory: () -> Unit = EnsureNeverCalled(), +) { + setContent { + RoomDetailsView( + state = state, + goBack = goBack, + onActionClicked = onActionClicked, + onShareRoom = onShareRoom, + onShareMember = onShareMember, + openRoomMemberList = openRoomMemberList, + openRoomNotificationSettings = openRoomNotificationSettings, + invitePeople = invitePeople, + openAvatarPreview = openAvatarPreview, + openPollHistory = openPollHistory, + ) + } +} diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 7992ddee17..8c57033238 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -48,6 +48,11 @@ object TestTags { */ val homeScreenSettings = TestTag("home_screen-settings") + /** + * Room detail screen. + */ + val roomDetailAvatar = TestTag("room_detail-avatar") + /** * Welcome screen. */ diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt index 2ea4e44c75..9789aff870 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt @@ -55,6 +55,25 @@ class EnsureCalledOnceWithParam( } } +class EnsureCalledOnceWithTwoParams( + private val expectedParam1: T, + private val expectedParam2: U, +) : (T, U) -> Unit { + private var counter = 0 + override fun invoke(p1: T, p2: U) { + if (p1 != expectedParam1 || p2 != expectedParam2) { + throw AssertionError("Expected to be called with $expectedParam1 and $expectedParam2, but was called with $p1 and $p2") + } + counter++ + } + + fun assertSuccess() { + if (counter != 1) { + throw AssertionError("Expected to be called once, but was called $counter times") + } + } +} + /** * Shortcut for [ ensureCalledOnceWithParam] with Unit result. */