diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt index 12e605e04c..6281a8e8e3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressStateProvider.kt @@ -29,8 +29,9 @@ open class JoinRoomByAddressStateProvider : PreviewParameterProvider Unit = {}, ) = JoinRoomByAddressState( address = address, addressState = addressState, - eventSink = {} + eventSink = eventSink ) diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/FakeCreateRoomNavigator.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/FakeCreateRoomNavigator.kt new file mode 100644 index 0000000000..9196605800 --- /dev/null +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/FakeCreateRoomNavigator.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 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.createroom.impl + +import io.element.android.features.createroom.CreateRoomNavigator +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias + +class FakeCreateRoomNavigator( + private val openRoomLambda: (roomIdOrAlias: RoomIdOrAlias, serverNames: List) -> Unit = { _, _ -> }, + private val createNewRoomLambda: () -> Unit = {}, + private val showJoinRoomByAddressLambda: () -> Unit = {}, + private val dismissJoinRoomByAddressLambda: () -> Unit = {}, + + ) : CreateRoomNavigator { + override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) { + openRoomLambda(roomIdOrAlias, serverNames) + } + + override fun onCreateNewRoom() { + createNewRoomLambda() + } + + override fun onShowJoinRoomByAddress() { + showJoinRoomByAddressLambda() + } + + override fun onDismissJoinRoomByAddress() { + dismissJoinRoomByAddressLambda() + } +} diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressPresenterTest.kt new file mode 100644 index 0000000000..02608e3c71 --- /dev/null +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressPresenterTest.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2025 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.createroom.impl.joinbyaddress + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.createroom.CreateRoomNavigator +import io.element.android.features.createroom.impl.FakeCreateRoomNavigator +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper +import io.element.android.tests.testutils.lambda.assert +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class JoinRoomByAddressPresenterTest { + + @Test + fun `present - initial state`() = runTest { + val presenter = createJoinRoomByAddressPresenter() + presenter.test { + with(awaitItem()) { + assertThat(address).isEmpty() + assertThat(addressState).isEqualTo(RoomAddressState.Unknown) + } + } + } + + @Test + fun `present - invalid address`() = runTest { + val presenter = createJoinRoomByAddressPresenter( + roomAliasHelper = FakeRoomAliasHelper( + isRoomAliasValidLambda = { false } + ) + ) + presenter.test { + with(awaitItem()) { + eventSink(JoinRoomByAddressEvents.UpdateAddress("invalid_address")) + } + with(awaitItem()) { + assertThat(address).isEqualTo("invalid_address") + assertThat(addressState).isEqualTo(RoomAddressState.Unknown) + eventSink(JoinRoomByAddressEvents.Continue) + } + // The address should be marked as invalid only after the user tries to continue + with(awaitItem()) { + assertThat(address).isEqualTo("invalid_address") + assertThat(addressState).isEqualTo(RoomAddressState.Invalid) + } + } + } + + @Test + fun `present - room found`() = runTest { + val openRoomLambda = lambdaRecorder, Unit> { _, _ -> } + val dismissJoinRoomByAddressLambda = lambdaRecorder { } + val navigator = FakeCreateRoomNavigator( + openRoomLambda = openRoomLambda, + dismissJoinRoomByAddressLambda = dismissJoinRoomByAddressLambda + ) + val presenter = createJoinRoomByAddressPresenter(navigator = navigator) + presenter.test { + with(awaitItem()) { + eventSink(JoinRoomByAddressEvents.UpdateAddress("#room_found:matrix.org")) + } + with(awaitItem()) { + assertThat(address).isEqualTo("#room_found:matrix.org") + assertThat(addressState).isEqualTo(RoomAddressState.Unknown) + } + with(awaitItem()) { + assertThat(address).isEqualTo("#room_found:matrix.org") + assertThat(addressState).isInstanceOf(RoomAddressState.RoomFound::class.java) + eventSink(JoinRoomByAddressEvents.Continue) + } + assert(openRoomLambda).isCalledOnce() + assert(dismissJoinRoomByAddressLambda).isCalledOnce() + } + } + + @Test + fun `present - room not found`() = runTest { + val presenter = createJoinRoomByAddressPresenter( + matrixClient = FakeMatrixClient( + resolveRoomAliasResult = { Result.failure(RuntimeException()) } + ) + ) + presenter.test { + with(awaitItem()) { + eventSink(JoinRoomByAddressEvents.UpdateAddress("#room_not_found:matrix.org")) + } + with(awaitItem()) { + assertThat(address).isEqualTo("#room_not_found:matrix.org") + assertThat(addressState).isEqualTo(RoomAddressState.Unknown) + eventSink(JoinRoomByAddressEvents.Continue) + } + with(awaitItem()) { + assertThat(address).isEqualTo("#room_not_found:matrix.org") + assertThat(addressState).isEqualTo(RoomAddressState.Resolving) + } + with(awaitItem()) { + assertThat(address).isEqualTo("#room_not_found:matrix.org") + assertThat(addressState).isEqualTo(RoomAddressState.RoomNotFound) + } + } + } + + @Test + fun `present - dismiss`() = runTest { + val dismissJoinRoomByAddressLambda = lambdaRecorder { } + val navigator = FakeCreateRoomNavigator( + dismissJoinRoomByAddressLambda = dismissJoinRoomByAddressLambda + ) + val presenter = createJoinRoomByAddressPresenter(navigator = navigator) + presenter.test { + with(awaitItem()) { + eventSink(JoinRoomByAddressEvents.Dismiss) + } + assert(dismissJoinRoomByAddressLambda).isCalledOnce() + } + } + + private fun createJoinRoomByAddressPresenter( + navigator: CreateRoomNavigator = FakeCreateRoomNavigator(), + matrixClient: MatrixClient = FakeMatrixClient(), + roomAliasHelper: RoomAliasHelper = FakeRoomAliasHelper(), + ): JoinRoomByAddressPresenter { + return JoinRoomByAddressPresenter( + navigator = navigator, + client = matrixClient, + roomAliasHelper = roomAliasHelper, + ) + } +} diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressViewTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressViewTest.kt new file mode 100644 index 0000000000..79a4efdc02 --- /dev/null +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/joinbyaddress/JoinRoomByAddressViewTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2025 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.createroom.impl.joinbyaddress + +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.createroom.impl.R +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class JoinRoomByAddressViewTest { + + @get:Rule + val rule = createAndroidComposeRule() + + @Test + fun `entering text emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setJoinRoomByAddressView( + aJoinRoomByAddressState( + eventSink = eventsRecorder, + ) + ) + val text = rule.activity.getString(R.string.screen_start_chat_join_room_by_address_action) + rule.onNodeWithText(text).performTextInput("#address:matrix.org") + eventsRecorder.assertSingle(JoinRoomByAddressEvents.UpdateAddress("#address:matrix.org")) + } + + @Test + fun `clicking on continue emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setJoinRoomByAddressView( + aJoinRoomByAddressState( + eventSink = eventsRecorder, + ) + ) + rule.clickOn(CommonStrings.action_continue) + eventsRecorder.assertSingle(JoinRoomByAddressEvents.Continue) + } + +} + +private fun AndroidComposeTestRule.setJoinRoomByAddressView( + state: JoinRoomByAddressState, +) { + setContent { + JoinRoomByAddressView(state = state) + } +} diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootViewTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootViewTest.kt index 88390f3b8c..e446a13417 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootViewTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootViewTest.kt @@ -101,6 +101,21 @@ class CreateRoomRootViewTest { rule.onNodeWithText(firstRoom.matrixUser.getBestName()).performClick() } } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on Join room by address invokes the expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { + rule.setCreateRoomRootView( + aCreateRoomRootState( + eventSink = eventsRecorder, + ), + onJoinRoomByAddressClick = it + ) + rule.clickOn(R.string.screen_start_chat_join_room_by_address_action) + } + } } private fun AndroidComposeTestRule.setCreateRoomRootView( @@ -109,6 +124,7 @@ private fun AndroidComposeTestRule.setCreat onNewRoomClick: () -> Unit = EnsureNeverCalled(), onOpenDM: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onInviteFriendsClick: () -> Unit = EnsureNeverCalled(), + onJoinRoomByAddressClick: () -> Unit = EnsureNeverCalled(), ) { setContent { CreateRoomRootView( @@ -117,6 +133,7 @@ private fun AndroidComposeTestRule.setCreat onNewRoomClick = onNewRoomClick, onOpenDM = onOpenDM, onInviteFriendsClick = onInviteFriendsClick, + onJoinByAddressClick = onJoinRoomByAddressClick ) } }