From 06bea478648e350fec29611a110c7d1ac927303b Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Nov 2024 15:35:33 +0100 Subject: [PATCH] create room : fix tests and remove auto-fill alias for now. --- .../createroom/impl/CreateRoomDataStore.kt | 3 +- .../impl/components/RoomAccessOption.kt | 19 +- .../impl/components/RoomVisibilityOption.kt | 4 +- .../impl/configureroom/ConfigureRoomEvents.kt | 3 +- .../configureroom/ConfigureRoomPresenter.kt | 50 +++-- .../ConfigureRoomStateProvider.kt | 3 +- .../impl/configureroom/ConfigureRoomView.kt | 8 +- .../impl/configureroom/RoomAccess.kt | 3 +- .../impl/configureroom/RoomAccessItem.kt | 1 - .../ConfigureRoomPresenterTest.kt | 198 +++++++++++------- 10 files changed, 169 insertions(+), 123 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index 1c08068d67..36feb0aec3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -46,6 +46,7 @@ class CreateRoomDataStore @Inject constructor( fun setRoomName(roomName: String) { createRoomConfigFlow.getAndUpdate { config -> + /* val newVisibility = when (config.roomVisibility) { is RoomVisibilityState.Public -> { val roomAddress = config.roomVisibility.roomAddress @@ -59,9 +60,9 @@ class CreateRoomDataStore @Inject constructor( } else -> config.roomVisibility } + */ config.copy( roomName = roomName.takeIf { it.isNotEmpty() }, - roomVisibility = newVisibility, ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt index 4d39f698be..6cde477f2e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomAccessOption.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.selectable import androidx.compose.material3.MaterialTheme @@ -36,13 +35,13 @@ fun RoomAccessOption( isSelected: Boolean = false, ) { Row( - modifier - .fillMaxWidth() - .selectable( - selected = isSelected, - onClick = { onOptionClick(roomAccessItem) }, - role = Role.RadioButton, - ) + modifier + .fillMaxWidth() + .selectable( + selected = isSelected, + onClick = { onOptionClick(roomAccessItem) }, + role = Role.RadioButton, + ) ) { Column(Modifier.weight(1f)) { Text( @@ -59,8 +58,8 @@ fun RoomAccessOption( } RadioButton( modifier = Modifier - .align(Alignment.CenterVertically) - .size(48.dp), + .align(Alignment.CenterVertically) + .size(48.dp), selected = isSelected, // null recommended for accessibility with screenreaders onClick = null diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt index cf6ab077a4..15ae5c11a1 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomVisibilityOption.kt @@ -50,7 +50,7 @@ fun RoomVisibilityOption( ) ) { Box( - modifier = modifier + modifier = Modifier .size(30.dp) .clip(RoundedCornerShape(8.dp)) .background(ElementTheme.colors.bgSubtleSecondary) @@ -60,7 +60,7 @@ fun RoomVisibilityOption( Icon( resourceId = roomPrivacyItem.icon, contentDescription = null, - tint = if(isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, + tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, ) } Spacer(Modifier.size(16.dp)) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index 048cd1cfe0..26fee055ee 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -7,7 +7,6 @@ package io.element.android.features.createroom.impl.configureroom -import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -18,7 +17,7 @@ sealed interface ConfigureRoomEvents { data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents - data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents + data object CreateRoom : ConfigureRoomEvents data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents data object CancelCreateRoom : ConfigureRoomEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 41a9dc8f97..1b7623adfb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -22,14 +22,13 @@ import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore 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.architecture.runCatchingUpdatingState import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters -import io.element.android.libraries.matrix.api.createroom.JoinRuleOverride import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -61,7 +60,7 @@ class ConfigureRoomPresenter @Inject constructor( val cameraPermissionState = cameraPermissionPresenter.present() val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig()) val homeserverName = remember { matrixClient.userIdServerName() } - val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) + val isKnockFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(initial = false) val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, @@ -103,7 +102,7 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess) is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) - is ConfigureRoomEvents.CreateRoom -> createRoom(event.config) + is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig.value) is ConfigureRoomEvents.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() @@ -118,7 +117,6 @@ class ConfigureRoomPresenter @Inject constructor( } ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncAction.Uninitialized - } } @@ -137,20 +135,33 @@ class ConfigureRoomPresenter @Inject constructor( config: CreateRoomConfig, createRoomAction: MutableState> ) = launch { - runUpdatingState(createRoomAction) { + suspend { val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } - val params = CreateRoomParameters( - name = config.roomName, - topic = config.topic, - isEncrypted = config.roomVisibility is RoomVisibilityState.Private, - isDirect = false, - visibility = if (config.roomVisibility is RoomVisibilityState.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, - joinRuleOverride = if (config.roomVisibility is RoomVisibilityState.Public) config.roomVisibility.roomAccess.toJoinRule() else JoinRuleOverride.None, - preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, - invite = config.invites.map { it.userId }, - avatar = avatarUrl, - canonicalAlias = config.roomVisibility.roomAddress() - ) + val params = if (config.roomVisibility is RoomVisibilityState.Public) { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = false, + isDirect = false, + visibility = RoomVisibility.PUBLIC, + joinRuleOverride = config.roomVisibility.roomAccess.toJoinRule(), + preset = RoomPreset.PUBLIC_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + canonicalAlias = config.roomVisibility.roomAddress() + ) + } else { + CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = config.roomVisibility is RoomVisibilityState.Private, + isDirect = false, + visibility = RoomVisibility.PRIVATE, + preset = RoomPreset.PRIVATE_CHAT, + invite = config.invites.map { it.userId }, + avatar = avatarUrl, + ) + } matrixClient.createRoom(params) .onFailure { failure -> Timber.e(failure, "Failed to create room") @@ -159,7 +170,8 @@ class ConfigureRoomPresenter @Inject constructor( dataStore.clearCachedData() analyticsService.capture(CreatedRoom(isDM = false)) } - } + .getOrThrow() + }.runCatchingUpdatingState(createRoomAction) } private suspend fun uploadAvatar(avatarUri: Uri): String { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index 5b1427cd47..80445fbff4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList open class ConfigureRoomStateProvider : PreviewParameterProvider { @@ -23,6 +22,7 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider().readBytes() } returns byteArrayOf() } @@ -94,50 +75,56 @@ class ConfigureRoomPresenterTest { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() assertThat(initialState.config).isEqualTo(CreateRoomConfig()) assertThat(initialState.config.roomName).isNull() assertThat(initialState.config.topic).isNull() assertThat(initialState.config.invites).isEmpty() assertThat(initialState.config.avatarUri).isNull() assertThat(initialState.config.roomVisibility).isEqualTo(RoomVisibilityState.Private) + assertThat(initialState.createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + assertThat(initialState.homeserverName).isEqualTo("matrix.org") } } @Test fun `present - create room button is enabled only if the required fields are completed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val presenter = createConfigureRoomPresenter() + presenter.test { + val initialState = initialState() var config = initialState.config - assertThat(initialState.isCreateButtonEnabled).isFalse() + assertThat(initialState.config.isValid).isFalse() // Room name not empty initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() config = config.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isTrue() + assertThat(newState.config.isValid).isTrue() // Clear room name newState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) newState = awaitItem() config = config.copy(roomName = null) assertThat(newState.config).isEqualTo(config) - assertThat(newState.isCreateButtonEnabled).isFalse() + assertThat(newState.config.isValid).isFalse() } } @Test fun `present - state is updated when fields are changed`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val userListDataStore = UserListDataStore() + val pickerProvider = FakePickerProvider() + val permissionsPresenter = FakePermissionsPresenter() + val presenter = createConfigureRoomPresenter( + createRoomDataStore = CreateRoomDataStore(userListDataStore), + pickerProvider = pickerProvider, + permissionsPresenter = permissionsPresenter, + ) + presenter.test { + val initialState = initialState() var expectedConfig = CreateRoomConfig() assertThat(initialState.config).isEqualTo(expectedConfig) @@ -165,22 +152,22 @@ class ConfigureRoomPresenterTest { // Room avatar // Pick avatar - fakePickerProvider.givenResult(null) + pickerProvider.givenResult(null) // From gallery val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY) - fakePickerProvider.givenResult(uriFromGallery) + pickerProvider.givenResult(uriFromGallery) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery) assertThat(newState.config).isEqualTo(expectedConfig) // From camera val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA) - fakePickerProvider.givenResult(uriFromCamera) + pickerProvider.givenResult(uriFromCamera) assertThat(newState.cameraPermissionState.permissionGranted).isFalse() newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() assertThat(newState.cameraPermissionState.showDialog).isTrue() - fakePermissionsPresenter.setPermissionGranted() + permissionsPresenter.setPermissionGranted() newState = awaitItem() assertThat(newState.cameraPermissionState.permissionGranted).isTrue() newState = awaitItem() @@ -188,7 +175,7 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Do it again, no permission is requested val uriFromCamera2 = Uri.parse(AN_URI_FROM_CAMERA_2) - fakePickerProvider.givenResult(uriFromCamera2) + pickerProvider.givenResult(uriFromCamera2) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2) @@ -200,9 +187,15 @@ class ConfigureRoomPresenterTest { assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy - newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityState.Public)) + newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityItem.Public)) newState = awaitItem() - expectedConfig = expectedConfig.copy(roomVisibility = RoomVisibilityState.Public) + expectedConfig = expectedConfig.copy( + roomVisibility = RoomVisibilityState.Public( + roomAddress = RoomAddress.AutoFilled(expectedConfig.roomName ?: ""), + roomAddressErrorState = RoomAddressErrorState.None, + roomAccess = RoomAccess.Anyone, + ) + ) assertThat(newState.config).isEqualTo(expectedConfig) // Remove user @@ -215,15 +208,17 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room action`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -233,18 +228,22 @@ class ConfigureRoomPresenterTest { @Test fun `present - record analytics when creating room`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val presenter = createConfigureRoomPresenter( + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.success(RoomId("!createRoomResult:domain")) - fakeMatrixClient.givenCreateRoomResult(createRoomResult) + matrixClient.givenCreateRoomResult(createRoomResult) - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) skipItems(2) - val analyticsEvent = fakeAnalyticsService.capturedEvents.filterIsInstance().firstOrNull() + val analyticsEvent = analyticsService.capturedEvents.filterIsInstance().firstOrNull() assertThat(analyticsEvent).isNotNull() assertThat(analyticsEvent?.isDM).isFalse() } @@ -252,23 +251,31 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger create room with upload error and retry`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - skipItems(1) + val matrixClient = createMatrixClient() + val analyticsService = FakeAnalyticsService() + val mediaPreProcessor = FakeMediaPreProcessor() + val createRoomDataStore = CreateRoomDataStore(UserListDataStore()) + val presenter = createConfigureRoomPresenter( + createRoomDataStore = createRoomDataStore, + mediaPreProcessor = mediaPreProcessor, + matrixClient = matrixClient, + analyticsService = analyticsService + ) + presenter.test { + val initialState = initialState() createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) - fakeMediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) - fakeMatrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) + skipItems(1) + mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) + matrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) - val initialState = awaitItem() - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) - assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance()).isEmpty() + assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() - fakeMatrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) @@ -277,23 +284,25 @@ class ConfigureRoomPresenterTest { @Test fun `present - trigger retry and cancel actions`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() + val fakeMatrixClient = createMatrixClient() + val presenter = createConfigureRoomPresenter( + matrixClient = fakeMatrixClient + ) + presenter.test { + val initialState = initialState() val createRoomResult = Result.failure(A_THROWABLE) fakeMatrixClient.givenCreateRoomResult(createRoomResult) // Create - initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + initialState.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterCreateRoom = awaitItem() assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) // Retry - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) val stateAfterRetry = awaitItem() @@ -305,4 +314,33 @@ class ConfigureRoomPresenterTest { assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } + + private suspend fun TurbineTestContext.initialState(): ConfigureRoomState { + skipItems(1) + return awaitItem() + } + + private fun createMatrixClient() = FakeMatrixClient( + userIdServerNameLambda = { "matrix.org" }, + ) + + private fun createConfigureRoomPresenter( + createRoomDataStore: CreateRoomDataStore = CreateRoomDataStore(UserListDataStore()), + matrixClient: MatrixClient = createMatrixClient(), + pickerProvider: PickerProvider = FakePickerProvider(), + mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), + analyticsService: AnalyticsService = FakeAnalyticsService(), + permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(), + isKnockFeatureEnabled: Boolean = true, + ) = ConfigureRoomPresenter( + dataStore = createRoomDataStore, + matrixClient = matrixClient, + mediaPickerProvider = pickerProvider, + mediaPreProcessor = mediaPreProcessor, + analyticsService = analyticsService, + permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter), + featureFlagService = FakeFeatureFlagService( + mapOf(FeatureFlags.Knock.key to isKnockFeatureEnabled) + ) + ) }