diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 8d81632f83..fe63bb677d 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt
index 8fbf342697..5a693a4a83 100644
--- a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt
+++ b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt
@@ -23,7 +23,6 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
interface RoomDirectoryEntryPoint : FeatureEntryPoint {
-
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
interface NodeBuilder {
@@ -35,4 +34,3 @@ interface RoomDirectoryEntryPoint : FeatureEntryPoint {
fun onOpenRoom(roomId: RoomId)
}
}
-
diff --git a/features/roomdirectory/impl/build.gradle.kts b/features/roomdirectory/impl/build.gradle.kts
index 9d56802700..d26a75ca8c 100644
--- a/features/roomdirectory/impl/build.gradle.kts
+++ b/features/roomdirectory/impl/build.gradle.kts
@@ -25,6 +25,11 @@ plugins {
android {
namespace = "io.element.android.features.roomdirectory.impl"
+ testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ }
+ }
}
anvil {
@@ -41,13 +46,17 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
+ implementation(projects.libraries.testtags)
testImplementation(libs.test.junit)
+ testImplementation(libs.androidx.compose.ui.test.junit)
+ testImplementation(libs.test.robolectric)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
+ testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)
}
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt
index 11b5b1b27d..c15a748a9e 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt
@@ -28,12 +28,10 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultRoomDirectoryEntryPoint @Inject constructor() : RoomDirectoryEntryPoint {
-
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDirectoryEntryPoint.NodeBuilder {
val plugins = ArrayList()
return object : RoomDirectoryEntryPoint.NodeBuilder {
-
override fun callback(callback: RoomDirectoryEntryPoint.Callback): RoomDirectoryEntryPoint.NodeBuilder {
plugins += callback
return this
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt
index c99d30640a..dc3581589e 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt
@@ -35,7 +35,6 @@ class RoomDirectoryNode @AssistedInject constructor(
@Assisted plugins: List,
private val presenter: RoomDirectoryPresenter,
) : Node(buildContext, plugins = plugins) {
-
private fun onRoomJoined(roomId: RoomId) {
plugins().forEach {
it.onOpenRoom(roomId)
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt
index 2247a82b55..95cfa8166b 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt
@@ -26,13 +26,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import io.element.android.features.roomdirectory.impl.root.di.JoinRoom
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryListState
import io.element.android.features.roomdirectory.impl.root.model.toFeatureModel
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
-import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
@@ -46,10 +46,9 @@ import javax.inject.Inject
class RoomDirectoryPresenter @Inject constructor(
private val dispatchers: CoroutineDispatchers,
- private val matrixClient: MatrixClient,
+ private val joinRoom: JoinRoom,
private val roomDirectoryService: RoomDirectoryService,
) : Presenter {
-
@Composable
override fun present(): RoomDirectoryState {
var loadingMore by remember {
@@ -68,9 +67,9 @@ class RoomDirectoryPresenter @Inject constructor(
}
LaunchedEffect(searchQuery) {
if (searchQuery == null) return@LaunchedEffect
- //debounce search query
+ // debounce search query
delay(300)
- //cancel load more right away
+ // cancel load more right away
loadingMore = false
roomDirectoryList.filter(searchQuery, 20)
}
@@ -108,7 +107,7 @@ class RoomDirectoryPresenter @Inject constructor(
private fun CoroutineScope.joinRoom(state: MutableState>, roomId: RoomId) = launch {
state.runUpdatingState {
- matrixClient.joinRoom(roomId)
+ joinRoom(roomId)
}
}
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt
index ff12bc7ee6..3fa7877b6f 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt
@@ -25,39 +25,14 @@ import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
-open class RoomDirectorySearchStateProvider : PreviewParameterProvider {
+open class RoomDirectoryStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
aRoomDirectoryState(),
aRoomDirectoryState(
query = "Element",
- roomDescriptions = persistentListOf(
- RoomDescription(
- roomId = RoomId("@exa:matrix.org"),
- name = "Element X Android",
- description = "Element X is a secure, private and decentralized messenger.",
- avatarData = AvatarData(
- id = "@exa:matrix.org",
- name = "Element X Android",
- url = null,
- size = AvatarSize.RoomDirectoryItem
- ),
- canBeJoined = true,
- ),
- RoomDescription(
- roomId = RoomId("@exi:matrix.org"),
- name = "Element X iOS",
- description = "Element X is a secure, private and decentralized messenger.",
- avatarData = AvatarData(
- id = "@exi:matrix.org",
- name = "Element X iOS",
- url = null,
- size = AvatarSize.RoomDirectoryItem
- ),
- canBeJoined = false,
- )
- )
- ),
+ roomDescriptions = aRoomDescriptionList(),
+ )
)
}
@@ -66,10 +41,40 @@ fun aRoomDirectoryState(
displayLoadMoreIndicator: Boolean = false,
roomDescriptions: ImmutableList = persistentListOf(),
joinRoomAction: AsyncAction = AsyncAction.Uninitialized,
+ eventSink: (RoomDirectoryEvents) -> Unit = {},
) = RoomDirectoryState(
query = query,
roomDescriptions = roomDescriptions,
displayLoadMoreIndicator = displayLoadMoreIndicator,
joinRoomAction = joinRoomAction,
- eventSink = {},
+ eventSink = eventSink,
)
+
+fun aRoomDescriptionList(): ImmutableList {
+ return persistentListOf(
+ RoomDescription(
+ roomId = RoomId("!exa:matrix.org"),
+ name = "Element X Android",
+ description = "Element X is a secure, private and decentralized messenger.",
+ avatarData = AvatarData(
+ id = "!exa:matrix.org",
+ name = "Element X Android",
+ url = null,
+ size = AvatarSize.RoomDirectoryItem
+ ),
+ canBeJoined = true,
+ ),
+ RoomDescription(
+ roomId = RoomId("!exi:matrix.org"),
+ name = "Element X iOS",
+ description = "Element X is a secure, private and decentralized messenger.",
+ avatarData = AvatarData(
+ id = "!exi:matrix.org",
+ name = "Element X iOS",
+ url = null,
+ size = AvatarSize.RoomDirectoryItem
+ ),
+ canBeJoined = false,
+ )
+ )
+}
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt
index 6b5d94eb63..a40855fcc3 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt
@@ -40,6 +40,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -61,6 +62,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@@ -71,7 +73,6 @@ fun RoomDirectoryView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
-
fun joinRoom(roomId: RoomId) {
state.eventSink(RoomDirectoryEvents.JoinRoom(roomId))
}
@@ -86,8 +87,8 @@ fun RoomDirectoryView(
state = state,
onResultClicked = ::joinRoom,
modifier = Modifier
- .padding(padding)
- .consumeWindowInsets(padding)
+ .padding(padding)
+ .consumeWindowInsets(padding)
)
}
)
@@ -96,7 +97,8 @@ fun RoomDirectoryView(
onSuccess = onRoomJoined,
onErrorDismiss = {
state.eventSink(RoomDirectoryEvents.JoinRoomDismissError)
- })
+ }
+ )
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -171,7 +173,7 @@ private fun RoomDirectoryRoomList(
if (displayLoadMoreIndicator) {
item {
LoadMoreIndicator(modifier = Modifier.fillMaxWidth())
- LaunchedEffect(Unit) {
+ LaunchedEffect(onReachedLoadMore) {
onReachedLoadMore()
}
}
@@ -182,10 +184,10 @@ private fun RoomDirectoryRoomList(
@Composable
private fun LoadMoreIndicator(modifier: Modifier = Modifier) {
Box(
- modifier
- .fillMaxWidth()
- .wrapContentHeight()
- .padding(24.dp),
+ modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ .padding(24.dp),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator(
@@ -213,7 +215,7 @@ private fun SearchTextField(
) {
val focusManager = LocalFocusManager.current
TextField(
- modifier = modifier,
+ modifier = modifier.testTag(TestTags.searchTextField.value),
textStyle = ElementTheme.typography.fontBodyLgRegular,
singleLine = true,
value = query,
@@ -255,14 +257,14 @@ private fun RoomDirectoryRoomRow(
) {
Row(
modifier = modifier
- .fillMaxWidth()
- .clickable { onClick(roomDescription.roomId) }
- .padding(
- top = 12.dp,
- bottom = 12.dp,
- start = 16.dp,
- )
- .height(IntrinsicSize.Min),
+ .fillMaxWidth()
+ .clickable { onClick(roomDescription.roomId) }
+ .padding(
+ top = 12.dp,
+ bottom = 12.dp,
+ start = 16.dp,
+ )
+ .height(IntrinsicSize.Min),
) {
Avatar(
avatarData = roomDescription.avatarData,
@@ -270,8 +272,8 @@ private fun RoomDirectoryRoomRow(
)
Column(
modifier = Modifier
- .weight(1f)
- .padding(start = 16.dp)
+ .weight(1f)
+ .padding(start = 16.dp)
) {
Text(
text = roomDescription.name,
@@ -293,8 +295,8 @@ private fun RoomDirectoryRoomRow(
text = stringResource(id = CommonStrings.action_join),
color = ElementTheme.colors.textSuccessPrimary,
modifier = Modifier
- .align(Alignment.CenterVertically)
- .padding(start = 4.dp, end = 12.dp)
+ .align(Alignment.CenterVertically)
+ .padding(start = 4.dp, end = 12.dp)
)
} else {
Spacer(modifier = Modifier.width(24.dp))
@@ -304,7 +306,7 @@ private fun RoomDirectoryRoomRow(
@PreviewsDayNight
@Composable
-fun RoomDirectorySearchViewLightPreview(@PreviewParameter(RoomDirectorySearchStateProvider::class) state: RoomDirectoryState) = ElementPreview {
+internal fun RoomDirectoryViewPreview(@PreviewParameter(RoomDirectoryStateProvider::class) state: RoomDirectoryState) = ElementPreview {
RoomDirectoryView(
state = state,
onRoomJoined = {},
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/di/JoinRoom.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/di/JoinRoom.kt
new file mode 100644
index 0000000000..983d2a1dd2
--- /dev/null
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/di/JoinRoom.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.roomdirectory.impl.root.di
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.di.SessionScope
+import io.element.android.libraries.matrix.api.MatrixClient
+import io.element.android.libraries.matrix.api.core.RoomId
+import javax.inject.Inject
+
+interface JoinRoom {
+ suspend operator fun invoke(roomId: RoomId): Result
+}
+
+@ContributesBinding(SessionScope::class)
+class DefaultJoinRoom @Inject constructor(private val client: MatrixClient) : JoinRoom {
+ override suspend fun invoke(roomId: RoomId) = client.joinRoom(roomId)
+}
diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDirectoryListState.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDirectoryListState.kt
index d85295ca7e..60f344f67b 100644
--- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDirectoryListState.kt
+++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDirectoryListState.kt
@@ -24,7 +24,6 @@ internal data class RoomDirectoryListState(
val hasMoreToLoad: Boolean,
val items: ImmutableList,
) {
-
companion object {
val Default = RoomDirectoryListState(
hasMoreToLoad = true,
diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/FakeJoinRoom.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/FakeJoinRoom.kt
new file mode 100644
index 0000000000..3f4d17aefd
--- /dev/null
+++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/FakeJoinRoom.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.roomdirectory.impl.root
+
+import io.element.android.features.roomdirectory.impl.root.di.JoinRoom
+import io.element.android.libraries.matrix.api.core.RoomId
+
+class FakeJoinRoom(
+ var lambda: (RoomId) -> Result = { Result.success(it) }
+) : JoinRoom {
+ override suspend fun invoke(roomId: RoomId) = lambda(roomId)
+}
diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt
new file mode 100644
index 0000000000..eefafc86e1
--- /dev/null
+++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenterTest.kt
@@ -0,0 +1,182 @@
+/*
+ * 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.roomdirectory.impl.root
+
+import com.google.common.truth.Truth.assertThat
+import io.element.android.features.roomdirectory.impl.root.di.JoinRoom
+import io.element.android.libraries.architecture.AsyncAction
+import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
+import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
+import io.element.android.libraries.matrix.test.A_ROOM_ID
+import io.element.android.libraries.matrix.test.roomdirectory.FakeRoomDirectoryList
+import io.element.android.libraries.matrix.test.roomdirectory.FakeRoomDirectoryService
+import io.element.android.libraries.matrix.test.roomdirectory.aRoomDescription
+import io.element.android.tests.testutils.lambda.any
+import io.element.android.tests.testutils.lambda.assert
+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.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class) class RoomDirectoryPresenterTest {
+ @Test
+ fun `present - initial state`() = runTest {
+ val presenter = createRoomDirectoryPresenter()
+ presenter.test {
+ val initialState = awaitItem()
+ assertThat(initialState.query).isEmpty()
+ assertThat(initialState.displayEmptyState).isFalse()
+ assertThat(initialState.joinRoomAction).isEqualTo(AsyncAction.Uninitialized)
+ assertThat(initialState.roomDescriptions).isEmpty()
+ assertThat(initialState.displayLoadMoreIndicator).isTrue()
+ }
+ }
+
+ @Test
+ fun `present - room directory list emits empty state`() = runTest {
+ val directoryListStateFlow = MutableSharedFlow(replay = 1)
+ val roomDirectoryList = FakeRoomDirectoryList(directoryListStateFlow)
+ val roomDirectoryService = FakeRoomDirectoryService { roomDirectoryList }
+ val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService)
+ presenter.test {
+ skipItems(1)
+ directoryListStateFlow.emit(
+ RoomDirectoryList.State(false, emptyList())
+ )
+ awaitItem().also { state ->
+ assertThat(state.displayEmptyState).isTrue()
+ }
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - room directory list emits non-empty state`() = runTest {
+ val directoryListStateFlow = MutableSharedFlow(replay = 1)
+ val roomDirectoryList = FakeRoomDirectoryList(directoryListStateFlow)
+ val roomDirectoryService = FakeRoomDirectoryService { roomDirectoryList }
+ val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService)
+ presenter.test {
+ skipItems(1)
+ directoryListStateFlow.emit(
+ RoomDirectoryList.State(
+ hasMoreToLoad = true,
+ items = listOf(aRoomDescription())
+ )
+ )
+ awaitItem().also { state ->
+ assertThat(state.displayEmptyState).isFalse()
+ assertThat(state.roomDescriptions).hasSize(1)
+ }
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - emit search event`() = runTest {
+ val filterLambda = lambdaRecorder { _: String?, _: Int ->
+ Result.success(Unit)
+ }
+ val roomDirectoryList = FakeRoomDirectoryList(filterLambda = filterLambda)
+ val roomDirectoryService = FakeRoomDirectoryService { roomDirectoryList }
+ val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService)
+ presenter.test {
+ awaitItem().also { state ->
+ state.eventSink(RoomDirectoryEvents.Search("test"))
+ }
+ awaitItem().also { state ->
+ assertThat(state.query).isEqualTo("test")
+ }
+ advanceUntilIdle()
+ cancelAndIgnoreRemainingEvents()
+ }
+ assert(filterLambda)
+ .isCalledOnce()
+ .with(value("test"), any())
+ }
+
+ @Test
+ fun `present - emit load more event`() = runTest {
+ val loadMoreLambda = lambdaRecorder { ->
+ Result.success(Unit)
+ }
+ val roomDirectoryList = FakeRoomDirectoryList(loadMoreLambda = loadMoreLambda)
+ val roomDirectoryService = FakeRoomDirectoryService { roomDirectoryList }
+ val presenter = createRoomDirectoryPresenter(roomDirectoryService = roomDirectoryService)
+ presenter.test {
+ awaitItem().also { state ->
+ state.eventSink(RoomDirectoryEvents.LoadMore)
+ }
+ advanceUntilIdle()
+ cancelAndIgnoreRemainingEvents()
+ }
+ assert(loadMoreLambda)
+ .isCalledOnce()
+ .withNoParameter()
+ }
+
+ @Test
+ fun `present - emit join room event`() = runTest {
+ val joinRoomSuccess = lambdaRecorder { roomId: RoomId ->
+ Result.success(roomId)
+ }
+ val joinRoomFailure = lambdaRecorder { roomId: RoomId ->
+ Result.failure(RuntimeException("Failed to join room $roomId"))
+ }
+ val fakeJoinRoom = FakeJoinRoom(joinRoomSuccess)
+ val presenter = createRoomDirectoryPresenter(joinRoom = fakeJoinRoom)
+ presenter.test {
+ awaitItem().also { state ->
+ state.eventSink(RoomDirectoryEvents.JoinRoom(A_ROOM_ID))
+ }
+ awaitItem().also { state ->
+ assertThat(state.joinRoomAction).isEqualTo(AsyncAction.Success(A_ROOM_ID))
+ fakeJoinRoom.lambda = joinRoomFailure
+ state.eventSink(RoomDirectoryEvents.JoinRoom(A_ROOM_ID))
+ }
+ awaitItem().also { state ->
+ assertThat(state.joinRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
+ }
+ }
+ assert(joinRoomSuccess)
+ .isCalledOnce()
+ .with(value(A_ROOM_ID))
+ assert(joinRoomFailure)
+ .isCalledOnce()
+ .with(value(A_ROOM_ID))
+ }
+
+ private fun TestScope.createRoomDirectoryPresenter(
+ roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(
+ createRoomDirectoryListFactory = { FakeRoomDirectoryList() }
+ ),
+ joinRoom: JoinRoom = FakeJoinRoom { Result.success(it) },
+ ): RoomDirectoryPresenter {
+ return RoomDirectoryPresenter(
+ dispatchers = testCoroutineDispatchers(),
+ joinRoom = joinRoom,
+ roomDirectoryService = roomDirectoryService,
+ )
+ }
+}
diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt
new file mode 100644
index 0000000000..bcac35fc3a
--- /dev/null
+++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.roomdirectory.impl.root
+
+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.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTextInput
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.element.android.libraries.architecture.AsyncAction
+import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.testtags.TestTags
+import io.element.android.tests.testutils.EnsureNeverCalled
+import io.element.android.tests.testutils.EnsureNeverCalledWithParam
+import io.element.android.tests.testutils.EventsRecorder
+import io.element.android.tests.testutils.ensureCalledOnceWithParam
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RoomDirectoryViewTest {
+ @get:Rule val rule = createAndroidComposeRule()
+
+ @Test
+ fun `typing text in search field emits the expected Event`() {
+ val eventsRecorder = EventsRecorder()
+ rule.setRoomDirectoryView(
+ state = aRoomDirectoryState(
+ eventSink = eventsRecorder,
+ )
+ )
+ rule.onNodeWithTag(TestTags.searchTextField.value).performTextInput(
+ text = "Test"
+ )
+ eventsRecorder.assertSingle(RoomDirectoryEvents.Search("Test"))
+ }
+
+ @Test
+ fun `clicking on room item emits the expected Event`() {
+ val eventsRecorder = EventsRecorder()
+ val state = aRoomDirectoryState(
+ roomDescriptions = aRoomDescriptionList(),
+ eventSink = eventsRecorder,
+ )
+ rule.setRoomDirectoryView(state = state)
+ val clickedRoom = state.roomDescriptions.first()
+ rule.onNodeWithText(clickedRoom.name).performClick()
+ eventsRecorder.assertSingle(RoomDirectoryEvents.JoinRoom(clickedRoom.roomId))
+ }
+
+ @Test
+ fun `composing load more indicator emits expected Event`() {
+ val eventsRecorder = EventsRecorder()
+ val state = aRoomDirectoryState(
+ displayLoadMoreIndicator = true,
+ eventSink = eventsRecorder,
+ )
+ rule.setRoomDirectoryView(state = state)
+ eventsRecorder.assertSingle(RoomDirectoryEvents.LoadMore)
+ }
+
+ @Test
+ fun `when joining room with success then onRoomJoined lambda is called once`() {
+ val eventsRecorder = EventsRecorder(expectEvents = false)
+ val roomDescriptions = aRoomDescriptionList()
+ val joinedRoomId = roomDescriptions.first().roomId
+ val state = aRoomDirectoryState(
+ joinRoomAction = AsyncAction.Success(joinedRoomId),
+ roomDescriptions = roomDescriptions,
+ eventSink = eventsRecorder,
+ )
+ ensureCalledOnceWithParam(joinedRoomId) { callback ->
+ rule.setRoomDirectoryView(
+ state = state,
+ onRoomJoined = callback,
+ )
+ }
+ }
+}
+
+private fun AndroidComposeTestRule.setRoomDirectoryView(
+ state: RoomDirectoryState,
+ onBackPressed: () -> Unit = EnsureNeverCalled(),
+ onRoomJoined: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
+) {
+ setContent {
+ RoomDirectoryView(
+ state = state,
+ onRoomJoined = onRoomJoined,
+ onBackPressed = onBackPressed,
+ )
+ }
+}
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt
index f545b27860..c860b6fc42 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt
@@ -191,6 +191,7 @@ private fun AndroidComposeTestRule.setRoomL
onInvitesClicked: () -> Unit = EnsureNeverCalled(),
onRoomSettingsClicked: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
onMenuActionClicked: (RoomListMenuAction) -> Unit = EnsureNeverCalledWithParam(),
+ onRoomDirectorySearchClicked: () -> Unit = EnsureNeverCalled(),
) {
setContent {
RoomListView(
@@ -203,6 +204,7 @@ private fun AndroidComposeTestRule.setRoomL
onInvitesClicked = onInvitesClicked,
onRoomSettingsClicked = onRoomSettingsClicked,
onMenuActionClicked = onMenuActionClicked,
+ onRoomDirectorySearchClicked = onRoomDirectorySearchClicked,
)
}
}
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt
index d3fc434f25..b3463c549f 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt
@@ -23,6 +23,9 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
+import io.element.android.libraries.featureflag.api.FeatureFlagService
+import io.element.android.libraries.featureflag.api.FeatureFlags
+import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
@@ -128,10 +131,26 @@ class RoomListSearchPresenterTests {
}
}
}
+
+ @Test
+ fun `present - room directory search`() = runTest {
+ val featureFlagService = FakeFeatureFlagService()
+ featureFlagService.setFeatureEnabled(FeatureFlags.RoomDirectorySearch, true)
+ val presenter = createRoomListSearchPresenter(featureFlagService = featureFlagService)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ awaitItem().let { state ->
+ assertThat(state.isRoomDirectorySearchEnabled).isTrue()
+ }
+ }
+ }
}
fun TestScope.createRoomListSearchPresenter(
roomListService: RoomListService = FakeRoomListService(),
+ featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
): RoomListSearchPresenter {
return RoomListSearchPresenter(
dataSource = RoomListSearchDataSource(
@@ -141,6 +160,7 @@ fun TestScope.createRoomListSearchPresenter(
roomLastMessageFormatter = FakeRoomLastMessageFormatter(),
),
coroutineDispatchers = testCoroutineDispatchers(),
- )
+ ),
+ featureFlagService = featureFlagService,
)
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
index fffdfa8c7c..481c094fd4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
@@ -76,7 +76,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -441,7 +440,7 @@ class RustMatrixClient(
runCatching { client.removeAvatar() }
}
- override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) {
+ override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) {
runCatching {
client.joinRoomById(roomId.value).destroy()
try {
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt
index 52b8e1ba98..91b84e48c0 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt
@@ -22,7 +22,6 @@ import org.matrix.rustcomponents.sdk.PublicRoomJoinRule
import org.matrix.rustcomponents.sdk.RoomDescription as RustRoomDescription
class RoomDescriptionMapper {
-
fun map(roomDescription: RustRoomDescription): RoomDescription {
return RoomDescription(
roomId = RoomId(roomDescription.roomId),
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDirectorySearchProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDirectorySearchProcessor.kt
index f060635cdf..aff631d6b4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDirectorySearchProcessor.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDirectorySearchProcessor.kt
@@ -30,7 +30,6 @@ class RoomDirectorySearchProcessor(
private val coroutineContext: CoroutineContext,
private val roomDescriptionMapper: RoomDescriptionMapper,
) {
-
private val mutex = Mutex()
suspend fun postUpdates(updates: List) {
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt
index f2e26f51a7..9b444d5ce5 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt
@@ -35,7 +35,6 @@ class RustRoomDirectoryList(
coroutineScope: CoroutineScope,
private val coroutineContext: CoroutineContext,
) : RoomDirectoryList {
-
private val hasMoreToLoad = MutableStateFlow(true)
private val items = MutableSharedFlow>(replay = 1)
private val processor = RoomDirectorySearchProcessor(items, coroutineContext, RoomDescriptionMapper())
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt
index 7627f90261..2939001b21 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt
@@ -26,7 +26,6 @@ class RustRoomDirectoryService(
private val client: Client,
private val sessionDispatcher: CoroutineDispatcher,
) : RoomDirectoryService {
-
override fun createRoomDirectoryList(scope: CoroutineScope): RoomDirectoryList {
return RustRoomDirectoryList(client.roomDirectorySearch(), scope, sessionDispatcher)
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
index 1ac68a5fa5..247bc4bbe9 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
@@ -94,6 +94,9 @@ class FakeMatrixClient(
private var setDisplayNameResult: Result = Result.success(Unit)
private var uploadAvatarResult: Result = Result.success(Unit)
private var removeAvatarResult: Result = Result.success(Unit)
+ var joinRoomLambda: suspend (RoomId) -> Result = {
+ Result.success(it)
+ }
override suspend fun getRoom(roomId: RoomId): MatrixRoom? {
return getRoomResults[roomId]
@@ -181,6 +184,8 @@ class FakeMatrixClient(
return removeAvatarResult
}
+ override suspend fun joinRoom(roomId: RoomId): Result = joinRoomLambda(roomId)
+
override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService
override fun pushersService(): PushersService = pushersService
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt
new file mode 100644
index 0000000000..b01501d328
--- /dev/null
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryList.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.libraries.matrix.test.roomdirectory
+
+import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+class FakeRoomDirectoryList(
+ override val state: Flow = emptyFlow(),
+ val filterLambda: (String?, Int) -> Result = { _, _ -> Result.success(Unit) },
+ val loadMoreLambda: () -> Result = { Result.success(Unit) }
+) : RoomDirectoryList {
+ override suspend fun filter(filter: String?, batchSize: Int) = filterLambda(filter, batchSize)
+
+ override suspend fun loadMore(): Result = loadMoreLambda()
+}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt
index 25f714cfde..68926b9deb 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt
@@ -20,8 +20,8 @@ import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
import kotlinx.coroutines.CoroutineScope
-class FakeRoomDirectoryService : RoomDirectoryService {
- override fun createRoomDirectoryList(scope: CoroutineScope): RoomDirectoryList {
- TODO("Not yet implemented")
- }
+class FakeRoomDirectoryService(
+ private val createRoomDirectoryListFactory: (CoroutineScope) -> RoomDirectoryList = { throw AssertionError("Configure a proper factory.") }
+) : RoomDirectoryService {
+ override fun createRoomDirectoryList(scope: CoroutineScope) = createRoomDirectoryListFactory(scope)
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt
new file mode 100644
index 0000000000..3e53a0c2e3
--- /dev/null
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.libraries.matrix.test.roomdirectory
+
+import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
+import io.element.android.libraries.matrix.test.A_ROOM_ID
+
+fun aRoomDescription(
+ roomId: RoomId = A_ROOM_ID,
+ name: String? = null,
+ topic: String? = null,
+ alias: String? = null,
+ avatarUrl: String? = null,
+ joinRule: RoomDescription.JoinRule = RoomDescription.JoinRule.UNKNOWN,
+ isWorldReadable: Boolean = true,
+ joinedMembers: Long = 2L
+) = RoomDescription(
+ roomId = roomId,
+ name = name,
+ topic = topic,
+ alias = alias,
+ avatarUrl = avatarUrl,
+ joinRule = joinRule,
+ isWorldReadable = isWorldReadable,
+ joinedMembers = joinedMembers
+)
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 1d237979e6..4374d77e52 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
@@ -100,4 +100,9 @@ object TestTags {
* Timeline item.
*/
val timelineItemSenderInfo = TestTag("timeline_item-sender_info")
+
+ /**
+ * Search field.
+ */
+ val searchTextField = TestTag("search_text_field")
}
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
index e651e8f968..827038785c 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
@@ -118,7 +118,8 @@ class RoomListScreen(
roomListService = matrixClient.roomListService,
roomSummaryFactory = roomListRoomSummaryFactory,
coroutineDispatchers = coroutineDispatchers,
- )
+ ),
+ featureFlagService = featureFlagService,
),
sessionPreferencesStore = DefaultSessionPreferencesStore(
context = context,
@@ -156,6 +157,7 @@ class RoomListScreen(
onInvitesClicked = {},
onRoomSettingsClicked = {},
onMenuActionClicked = {},
+ onRoomDirectorySearchClicked = {},
modifier = modifier,
)
diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt
new file mode 100644
index 0000000000..2735827134
--- /dev/null
+++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.tests.testutils
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.TurbineTestContext
+import app.cash.turbine.test
+import io.element.android.libraries.architecture.Presenter
+import kotlin.time.Duration
+
+suspend fun Presenter.test(
+ timeout: Duration? = null,
+ name: String? = null,
+ validate: suspend TurbineTestContext.() -> Unit,
+) {
+ moleculeFlow(RecompositionMode.Immediate) {
+ present()
+ }.test(timeout, name, validate)
+}