From 98bb91cd2733e1d829b4778e96825511807ca3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 15 Sep 2023 16:00:36 +0200 Subject: [PATCH] Create tests --- features/preferences/impl/build.gradle.kts | 3 + .../editprofile/EditUserProfilePresenter.kt | 11 +- .../EditUserProfilePresenterTest.kt | 494 ++++++++++++++++++ .../libraries/matrix/test/FakeMatrixClient.kt | 37 ++ .../ui/components/MatrixUserProvider.kt | 7 +- 5 files changed, 546 insertions(+), 6 deletions(-) create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index 4d0403809d..b28a068708 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -66,8 +66,11 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.featureflag.test) + testImplementation(projects.libraries.mediapickers.test) + testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.pushstore.test) testImplementation(projects.features.rageshake.test) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index 6557079de3..edbb2467f2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -33,7 +35,6 @@ import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor @@ -56,8 +57,8 @@ class EditUserProfilePresenter @AssistedInject constructor( @Composable override fun present(): EditUserProfileState { - var userAvatarUri = remember { matrixUser.avatarUrl?.let { Uri.parse(it) } } - var userDisplayName = remember { matrixUser.displayName } + var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) } + var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) userAvatarUri = uri } ) @@ -106,7 +107,7 @@ class EditUserProfilePresenter @AssistedInject constructor( avatarActions = avatarActions, saveButtonEnabled = canSave && saveAction.value !is Async.Loading, saveAction = saveAction.value, - eventSink = ::handleEvents + eventSink = { handleEvents(it) }, ) } @@ -138,6 +139,6 @@ class EditUserProfilePresenter @AssistedInject constructor( } else { matrixClient.removeAvatar().getOrThrow() } - } + }.onFailure { it.printStackTrace() } } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt new file mode 100644 index 0000000000..c377265689 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt @@ -0,0 +1,494 @@ +/* + * 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.features.preferences.impl.user.editprofile + +import android.net.Uri +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.mediapickers.test.FakePickerProvider +import io.element.android.libraries.mediaupload.api.MediaUploadInfo +import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.element.android.tests.testutils.WarmUpRule +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import java.io.File + +@ExperimentalCoroutinesApi +class EditUserProfilePresenterTest { + + @get:Rule + val warmUpRule = WarmUpRule() + + private lateinit var fakePickerProvider: FakePickerProvider + private lateinit var fakeMediaPreProcessor: FakeMediaPreProcessor + + private val userAvatarUri: Uri = mockk() + private val anotherAvatarUri: Uri = mockk() + + private val fakeFileContents = ByteArray(2) + + @Before + fun setup() { + fakePickerProvider = FakePickerProvider() + fakeMediaPreProcessor = FakeMediaPreProcessor() + mockkStatic(Uri::class) + + every { Uri.parse(AN_AVATAR_URL) } returns userAvatarUri + every { Uri.parse(ANOTHER_AVATAR_URL) } returns anotherAvatarUri + } + + @After + fun tearDown() { + unmockkAll() + } + + private fun anEditUserProfilePresenter( + matrixClient: MatrixClient = FakeMatrixClient(), + matrixUser: MatrixUser = aMatrixUser(), + ): EditUserProfilePresenter { + return EditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = matrixUser, + mediaPickerProvider = fakePickerProvider, + mediaPreProcessor = fakeMediaPreProcessor, + ) + } + + @Test + fun `present - initial state is created from room info`() = runTest { + val user = aMatrixUser(avatarUrl = AN_AVATAR_URL) + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.userId).isEqualTo(user.userId.value) + assertThat(initialState.displayName).isEqualTo(user.displayName) + assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(initialState.avatarActions).containsExactly( + AvatarAction.ChoosePhoto, + AvatarAction.TakePhoto, + AvatarAction.Remove + ) + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + assertThat(initialState.saveAction).isInstanceOf(Async.Uninitialized::class.java) + } + } + + @Test + fun `present - updates state in response to changes`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.displayName).isEqualTo("Name") + assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II")) + awaitItem().apply { + assertThat(displayName).isEqualTo("Name II") + assertThat(userAvatarUrl).isEqualTo(userAvatarUri) + } + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name III")) + awaitItem().apply { + assertThat(displayName).isEqualTo("Name III") + assertThat(userAvatarUrl).isEqualTo(userAvatarUri) + } + + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(displayName).isEqualTo("Name III") + assertThat(userAvatarUrl).isNull() + } + } + } + + @Test + fun `present - obtains avatar uris from gallery`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(anotherAvatarUri) + + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri) + } + } + } + + @Test + fun `present - obtains avatar uris from camera`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(anotherAvatarUri) + + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto)) + awaitItem().apply { + assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri) + } + } + } + + @Test + fun `present - updates save button state`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(userAvatarUri) + + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + + // Once a change is made, the save button is enabled + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // If it's reverted then the save disables again + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + } + } + + @Test + fun `present - updates save button state when initial values are null`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = null) + + fakePickerProvider.givenResult(userAvatarUri) + + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + + // Once a change is made, the save button is enabled + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // If it's reverted then the save disables again + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("fallback")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + } + } + + @Test + fun `present - save changes room details if different`() = runTest { + val matrixClient = FakeMatrixClient() + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = anEditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = user + ) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("New name")) + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) + initialState.eventSink(EditUserProfileEvents.Save) + skipItems(5) + + assertThat(matrixClient.setDisplayNameCalled).isTrue() + assertThat(matrixClient.removeAvatarCalled).isTrue() + assertThat(matrixClient.uploadAvatarCalled).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save doesn't change room details if they're the same trimmed`() = runTest { + val matrixClient = FakeMatrixClient() + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = anEditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = user + ) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(" Name ")) + initialState.eventSink(EditUserProfileEvents.Save) + + assertThat(matrixClient.setDisplayNameCalled).isTrue() + assertThat(matrixClient.uploadAvatarCalled).isFalse() + assertThat(matrixClient.removeAvatarCalled).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save doesn't change name if it's now empty`() = runTest { + val matrixClient = FakeMatrixClient() + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = anEditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = user + ) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("")) + initialState.eventSink(EditUserProfileEvents.Save) + + assertThat(matrixClient.setDisplayNameCalled).isFalse() + assertThat(matrixClient.uploadAvatarCalled).isFalse() + assertThat(matrixClient.removeAvatarCalled).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save processes and sets avatar when processor returns successfully`() = runTest { + val matrixClient = FakeMatrixClient() + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + givenPickerReturnsFile() + + val presenter = anEditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = user + ) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(EditUserProfileEvents.Save) + skipItems(2) + + assertThat(matrixClient.uploadAvatarCalled).isTrue() + } + } + + @Test + fun `present - save does not set avatar data if processor fails`() = runTest { + val matrixClient = FakeMatrixClient() + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = anEditUserProfilePresenter( + matrixClient = matrixClient, + matrixUser = user + ) + + fakePickerProvider.givenResult(anotherAvatarUri) + fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(EditUserProfileEvents.Save) + skipItems(2) + + assertThat(matrixClient.uploadAvatarCalled).isFalse() + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + } + } + + @Test + fun `present - sets save action to failure if name update fails`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + val matrixClient = FakeMatrixClient().apply { + givenSetDisplayNameResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name")) + } + + @Test + fun `present - sets save action to failure if removing avatar fails`() = runTest { + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + val matrixClient = FakeMatrixClient().apply { + givenRemoveAvatarResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) + } + + @Test + fun `present - sets save action to failure if setting avatar fails`() = runTest { + givenPickerReturnsFile() + + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + val matrixClient = FakeMatrixClient().apply { + givenUploadAvatarResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + } + + @Test + fun `present - CancelSaveChanges resets save action state`() = runTest { + givenPickerReturnsFile() + + val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) + val matrixClient = FakeMatrixClient().apply { + givenSetDisplayNameResult(Result.failure(Throwable("!"))) + } + + val presenter = anEditUserProfilePresenter(matrixUser = user) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo")) + initialState.eventSink(EditUserProfileEvents.Save) + skipItems(2) + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + + initialState.eventSink(EditUserProfileEvents.CancelSaveChanges) + assertThat(awaitItem().saveAction).isInstanceOf(Async.Uninitialized::class.java) + } + } + + private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvents) { + val presenter = anEditUserProfilePresenter(matrixUser = matrixUser, matrixClient = matrixClient) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(event) + initialState.eventSink(EditUserProfileEvents.Save) + skipItems(1) + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Loading::class.java) + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + } + } + + private fun givenPickerReturnsFile() { + mockkStatic(File::readBytes) + val processedFile: File = mockk { + every { readBytes() } returns fakeFileContents + } + + fakePickerProvider.givenResult(anotherAvatarUri) + fakeMediaPreProcessor.givenResult( + Result.success( + MediaUploadInfo.AnyFile( + file = processedFile, + fileInfo = mockk(), + ) + ) + ) + } + + companion object { + private const val ANOTHER_AVATAR_URL = "example://camera/foo.jpg" + } +} 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 660c1e268a..ce518730c4 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 @@ -58,6 +58,13 @@ class FakeMatrixClient( private val accountManagementUrlString: Result = Result.success(null), ) : MatrixClient { + var setDisplayNameCalled: Boolean = false + private set + var uploadAvatarCalled: Boolean = false + private set + var removeAvatarCalled: Boolean = false + private set + private var ignoreUserResult: Result = Result.success(Unit) private var unignoreUserResult: Result = Result.success(Unit) private var createRoomResult: Result = Result.success(A_ROOM_ID) @@ -69,6 +76,9 @@ class FakeMatrixClient( private val searchUserResults = mutableMapOf>() private val getProfileResults = mutableMapOf>() private var uploadMediaResult: Result = Result.success(AN_AVATAR_URL) + private var setDisplayNameResult: Result = Result.success(Unit) + private var uploadAvatarResult: Result = Result.success(Unit) + private var removeAvatarResult: Result = Result.success(Unit) override suspend fun getRoom(roomId: RoomId): MatrixRoom? { return getRoomResults[roomId] @@ -141,6 +151,21 @@ class FakeMatrixClient( return uploadMediaResult } + override suspend fun setDisplayName(displayName: String): Result { + setDisplayNameCalled = true + return setDisplayNameResult + } + + override suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result { + uploadAvatarCalled = true + return uploadAvatarResult + } + + override suspend fun removeAvatar(): Result { + removeAvatarCalled = true + return removeAvatarResult + } + override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService override fun pushersService(): PushersService = pushersService @@ -197,4 +222,16 @@ class FakeMatrixClient( fun givenUploadMediaResult(result: Result) { uploadMediaResult = result } + + fun givenSetDisplayNameResult(result: Result) { + setDisplayNameResult = result + } + + fun givenUploadAvatarResult(result: Result) { + uploadAvatarResult = result + } + + fun givenRemoveAvatarResult(result: Result) { + removeAvatarResult = result + } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt index 923afd94ad..1240b4c1b5 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt @@ -28,9 +28,14 @@ open class MatrixUserProvider : PreviewParameterProvider { ) } -fun aMatrixUser(id: String = "@id_of_alice:server.org", displayName: String = "Alice") = MatrixUser( +fun aMatrixUser( + id: String = "@id_of_alice:server.org", + displayName: String = "Alice", + avatarUrl: String? = null, +) = MatrixUser( userId = UserId(id), displayName = displayName, + avatarUrl = avatarUrl, ) fun aMatrixUserList() = listOf(