diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index fc0402eafc..de67bb010f 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -51,6 +51,8 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrixtest) + testImplementation(testFixtures(projects.libraries.matrix)) + androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) diff --git a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt index 70fc4f22e4..8bd6efa167 100644 --- a/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt +++ b/features/roomlist/src/test/kotlin/io/element/android/features/roomlist/RoomListPresenterTests.kt @@ -23,11 +23,20 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.roomlist.model.RoomListEvents +import io.element.android.features.roomlist.model.RoomListRoomSummary +import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.core.SessionId import io.element.android.libraries.matrixtest.FakeMatrixClient +import io.element.android.libraries.matrixtest.core.A_ROOM_ID +import io.element.android.libraries.matrixtest.core.A_ROOM_ID_VALUE +import io.element.android.libraries.matrixtest.room.A_LAST_MESSAGE +import io.element.android.libraries.matrixtest.room.A_ROOM_NAME +import io.element.android.libraries.matrixtest.room.InMemoryRoomSummaryDataSource +import io.element.android.libraries.matrixtest.room.aRoomSummaryFilled import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test +import timber.log.Timber class RoomListPresenterTests { @@ -60,7 +69,7 @@ class RoomListPresenterTests { moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { - var initialState = awaitItem() + skipItems(1) val withUserState = awaitItem() assertThat(withUserState.filter).isEqualTo("") withUserState.eventSink.invoke(RoomListEvents.UpdateFilter("t")) @@ -68,4 +77,44 @@ class RoomListPresenterTests { assertThat(withFilterState.filter).isEqualTo("t") } } + + @Test + fun `present - load 1 room with success`() = runTest { + val roomSummaryDataSource = InMemoryRoomSummaryDataSource() + val presenter = RoomListPresenter( + FakeMatrixClient( + sessionId = SessionId("sessionId"), + roomSummaryDataSource = roomSummaryDataSource + ), + LastMessageFormatter() + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + skipItems(1) + val withUserState = awaitItem() + // Room list is loaded with 16 placeholders + assertThat(withUserState.roomList.size).isEqualTo(16) + assertThat(withUserState.roomList.all { it.isPlaceholder }).isTrue() + val roomSummary = aRoomSummaryFilled() + roomSummaryDataSource.postRoomSummary( + listOf(roomSummary) + ) + skipItems(1) + val withRoomState = awaitItem() + assertThat(withRoomState.roomList.size).isEqualTo(1) + assertThat(withRoomState.roomList.first()).isEqualTo( + RoomListRoomSummary( + id = A_ROOM_ID_VALUE, + roomId = A_ROOM_ID, + name = A_ROOM_NAME, + hasUnread = true, + timestamp = "", + lastMessage = A_LAST_MESSAGE, + avatarData = AvatarData(name = A_ROOM_NAME), + isPlaceholder = false, + ) + ) + } + } } diff --git a/features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt b/features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt index 39b7e32ea8..34cc73ba53 100644 --- a/features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt +++ b/features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt @@ -14,12 +14,15 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.features.template import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/gradle.properties b/gradle.properties index 6902acf2bd..df832c13ba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,3 +47,6 @@ signing.element.nightly.keyPassword=Secret # Customise the Lint version to use a more recent version than the one bundled with AGP # https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html android.experimental.lint.version=8.0.0-alpha10 + +# Enable test fixture for all modules by default +android.experimental.enableTestFixtures=true diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt index 4593fe252a..dd0181f503 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/FakeMatrixClient.kt @@ -28,7 +28,10 @@ import io.element.android.libraries.matrixtest.room.FakeMatrixRoom import io.element.android.libraries.matrixtest.room.InMemoryRoomSummaryDataSource import org.matrix.rustcomponents.sdk.MediaSource -class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient { +class FakeMatrixClient( + override val sessionId: SessionId, + val roomSummaryDataSource: RoomSummaryDataSource = InMemoryRoomSummaryDataSource() +) : MatrixClient { override fun getRoom(roomId: RoomId): MatrixRoom? { return FakeMatrixRoom(roomId) @@ -39,7 +42,7 @@ class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient { override fun stopSync() = Unit override fun roomSummaryDataSource(): RoomSummaryDataSource { - return InMemoryRoomSummaryDataSource() + return roomSummaryDataSource } override fun mediaResolver(): MediaResolver { diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/core/RoomIdFixture.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/core/RoomIdFixture.kt new file mode 100644 index 0000000000..5b62ad383a --- /dev/null +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/core/RoomIdFixture.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 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.matrixtest.core + +import io.element.android.libraries.matrix.core.RoomId + +const val A_ROOM_ID_VALUE = "!aRoomId" +val A_ROOM_ID = RoomId(A_ROOM_ID_VALUE) diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt index 5179e911ab..666f64acb4 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/InMemoryRoomSummaryDataSource.kt @@ -23,8 +23,14 @@ import kotlinx.coroutines.flow.StateFlow class InMemoryRoomSummaryDataSource : RoomSummaryDataSource { + private val roomSummariesFlow = MutableStateFlow>(emptyList()) + + suspend fun postRoomSummary(roomSummaries: List) { + roomSummariesFlow.emit(roomSummaries) + } + override fun roomSummaries(): StateFlow> { - return MutableStateFlow(emptyList()) + return roomSummariesFlow } override fun setSlidingSyncRange(range: IntRange) = Unit diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt new file mode 100644 index 0000000000..0bed866300 --- /dev/null +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/RoomSummaryFixture.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 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.matrixtest.room + +import io.element.android.libraries.matrix.core.RoomId +import io.element.android.libraries.matrix.room.RoomSummary +import io.element.android.libraries.matrix.room.RoomSummaryDetails +import io.element.android.libraries.matrixtest.core.A_ROOM_ID + +const val A_ROOM_NAME = "aRoomName" +const val A_LAST_MESSAGE = "Last message" + +fun aRoomSummaryFilled( + roomId: RoomId = A_ROOM_ID, + name: String = A_ROOM_NAME, + isDirect: Boolean = false, + avatarURLString: String? = null, + lastMessage: CharSequence? = A_LAST_MESSAGE, + lastMessageTimestamp: Long? = null, + unreadNotificationCount: Int = 2, +) = RoomSummary.Filled( + aRoomSummaryDetail( + roomId = roomId, + name = name, + isDirect = isDirect, + avatarURLString = avatarURLString, + lastMessage = lastMessage, + lastMessageTimestamp = lastMessageTimestamp, + unreadNotificationCount = unreadNotificationCount, + ) +) + +fun aRoomSummaryDetail( + roomId: RoomId = A_ROOM_ID, + name: String = A_ROOM_NAME, + isDirect: Boolean = false, + avatarURLString: String? = null, + lastMessage: CharSequence? = A_LAST_MESSAGE, + lastMessageTimestamp: Long? = null, + unreadNotificationCount: Int = 2, +) = RoomSummaryDetails( + roomId = roomId, + name = name, + isDirect = isDirect, + avatarURLString = avatarURLString, + lastMessage = lastMessage, + lastMessageTimestamp = lastMessageTimestamp, + unreadNotificationCount = unreadNotificationCount, +) diff --git a/plugins/src/main/kotlin/extension/CommonExtension.kt b/plugins/src/main/kotlin/extension/CommonExtension.kt index f3ba843ab7..5fdb80ba1a 100644 --- a/plugins/src/main/kotlin/extension/CommonExtension.kt +++ b/plugins/src/main/kotlin/extension/CommonExtension.kt @@ -40,6 +40,7 @@ fun CommonExtension<*, *, *, *>.androidConfig(project: Project) { lintConfig = File("${project.rootDir}/tools/lint/lint.xml") checkDependencies = true abortOnError = true + ignoreTestFixturesSources = true } } @@ -64,6 +65,7 @@ fun CommonExtension<*, *, *, *>.composeConfig() { // Disabled until lint stops inspecting generated ksp files... // error.add("ComposableLambdaParameterNaming") error.add("ComposableLambdaParameterPosition") + ignoreTestFixturesSources = true } }