From ee4fba327c4d16ebaa027db311bf2cb667c9ba76 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 27 Jan 2025 16:36:53 +0100 Subject: [PATCH] feat(security&privacy) : start writing tests --- .../SecurityAndPrivacyPresenter.kt | 4 +- .../SecurityAndPrivacyState.kt | 7 +- .../SecurityAndPrivacyStateProvider.kt | 2 +- .../SecurityAndPrivacyView.kt | 2 +- .../roomdetails/impl/RoomDetailsViewTest.kt | 17 + .../FakeSecurityAndPrivacyNavigator.kt | 24 ++ .../SecurityAndPrivacyPresenterTest.kt | 335 ++++++++++++++++++ .../SecurityAndPrivacyPresenterViewTest.kt | 55 +++ 8 files changed, 441 insertions(+), 5 deletions(-) create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/FakeSecurityAndPrivacyNavigator.kt create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterTest.kt create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterViewTest.kt diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt index 2450786dc1..0fa653c462 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt @@ -69,7 +69,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor( isEncrypted = room.isEncrypted, isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value, historyVisibility = roomInfo?.historyVisibility.map(), - addressName = roomInfo?.firstDisplayableAlias(homeserverName)?.value + address = roomInfo?.firstDisplayableAlias(homeserverName)?.value ) } } @@ -91,7 +91,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor( isEncrypted = editedIsEncrypted, isVisibleInRoomDirectory = editedVisibleInRoomDirectory, historyVisibility = editedHistoryVisibility, - addressName = savedSettings.addressName, + address = savedSettings.address, ) var showEncryptionConfirmation by remember(savedSettings.isEncrypted) { mutableStateOf(false) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyState.kt index 6d3c835d9a..3d2fc250e2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyState.kt @@ -23,6 +23,8 @@ data class SecurityAndPrivacyState( val eventSink: (SecurityAndPrivacyEvents) -> Unit ) { + + val canBeSaved = savedSettings != editedSettings val availableHistoryVisibilities = buildSet { @@ -38,13 +40,16 @@ data class SecurityAndPrivacyState( val showRoomVisibilitySections = permissions.canChangeRoomVisibility && editedSettings.roomAccess != SecurityAndPrivacyRoomAccess.InviteOnly val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility val showEncryptionSection = permissions.canChangeEncryption + override fun toString(): String { + return "SecurityAndPrivacyState(savedSettings=$savedSettings, editedSettings=$editedSettings, homeserverName='$homeserverName', showEncryptionConfirmation=$showEncryptionConfirmation, saveAction=$saveAction, canBeSaved=$canBeSaved)" + } } data class SecurityAndPrivacySettings( val roomAccess: SecurityAndPrivacyRoomAccess, val isEncrypted: Boolean, val historyVisibility: SecurityAndPrivacyHistoryVisibility, - val addressName: String?, + val address: String?, val isVisibleInRoomDirectory: AsyncData ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyStateProvider.kt index d09bcf617f..78dba62ca1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyStateProvider.kt @@ -57,7 +57,7 @@ fun aSecurityAndPrivacySettings( ) = SecurityAndPrivacySettings( roomAccess = roomAccess, isEncrypted = isEncrypted, - addressName = formattedAddress, + address = formattedAddress, historyVisibility = historyVisibility, isVisibleInRoomDirectory = isVisibleInRoomDirectory ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyView.kt index 10d2a7d546..24fdf8bc2a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyView.kt @@ -87,7 +87,7 @@ fun SecurityAndPrivacyView( if (state.showRoomVisibilitySections) { RoomVisibilitySection(state.homeserverName) RoomAddressSection( - roomAddress = state.editedSettings.addressName, + roomAddress = state.editedSettings.address, homeserverName = state.homeserverName, onRoomAddressClick = { state.eventSink(SecurityAndPrivacyEvents.EditRoomAddress) }, isVisibleInRoomDirectory = state.editedSettings.isVisibleInRoomDirectory, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index b620946489..8cc1af1638 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -144,6 +144,21 @@ class RoomDetailsViewTest { } } + @Config(qualifiers = "h1024dp") + @Test + fun `click on security and privacy invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + canShowSecurityAndPrivacy = true, + ), + onSecurityAndPrivacyClick = callback, + ) + rule.clickOn(R.string.screen_room_details_security_and_privacy_title) + } + } + @Config(qualifiers = "h1024dp") @Test fun `click on add topic emit expected event`() { @@ -298,6 +313,7 @@ private fun AndroidComposeTestRule.setRoomD onJoinCallClick: () -> Unit = EnsureNeverCalled(), onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(), onKnockRequestsClick: () -> Unit = EnsureNeverCalled(), + onSecurityAndPrivacyClick: () -> Unit = EnsureNeverCalled(), ) { setContent { RoomDetailsView( @@ -315,6 +331,7 @@ private fun AndroidComposeTestRule.setRoomD onJoinCallClick = onJoinCallClick, onPinnedMessagesClick = onPinnedMessagesClick, onKnockRequestsClick = onKnockRequestsClick, + onSecurityAndPrivacyClick = onSecurityAndPrivacyClick, ) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/FakeSecurityAndPrivacyNavigator.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/FakeSecurityAndPrivacyNavigator.kt new file mode 100644 index 0000000000..e6c91096f3 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/FakeSecurityAndPrivacyNavigator.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.roomdetails.securityandprivacy + +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeSecurityAndPrivacyNavigator( + private val openEditRoomAddressLambda: () -> Unit = { lambdaError() }, + private val closeEditorRoomAddressLambda: () -> Unit = { lambdaError() }, +) : SecurityAndPrivacyNavigator { + override fun openEditRoomAddress() { + openEditRoomAddressLambda() + } + + override fun closeEditorRoomAddress() { + closeEditorRoomAddressLambda() + } +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterTest.kt new file mode 100644 index 0000000000..aca3e97c54 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterTest.kt @@ -0,0 +1,335 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.roomdetails.securityandprivacy + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyEvents +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyHistoryVisibility +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyPresenter +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyRoomAccess +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility +import io.element.android.libraries.matrix.test.A_ROOM_ALIAS +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.tests.testutils.lambda.assert +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class SecurityAndPrivacyPresenterTest { + + @Test + fun `present - initial states`() = runTest { + val presenter = createSecurityAndPrivacyPresenter() + presenter.test { + with(awaitItem()) { + assertThat(editedSettings).isEqualTo(savedSettings) + assertThat(canBeSaved).isFalse() + assertThat(showEncryptionConfirmation).isFalse() + assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(showRoomAccessSection).isFalse() + assertThat(showRoomVisibilitySections).isFalse() + assertThat(showHistoryVisibilitySection).isFalse() + assertThat(showEncryptionSection).isFalse() + } + with(awaitItem()) { + assertThat(editedSettings).isEqualTo(savedSettings) + assertThat(canBeSaved).isFalse() + assertThat(showEncryptionConfirmation).isFalse() + assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized) + assertThat(showRoomAccessSection).isTrue() + assertThat(showRoomVisibilitySections).isFalse() + assertThat(showHistoryVisibilitySection).isTrue() + assertThat(showEncryptionSection).isTrue() + } + } + } + + @Test + fun `present - room info change updates saved and edited settings`() = runTest { + val room = FakeMatrixRoom( + canSendStateResult = { _, _ -> Result.success(true) }, + ) + val presenter = createSecurityAndPrivacyPresenter(room = room) + presenter.test { + skipItems(2) + room.givenRoomInfo( + aRoomInfo( + joinRule = JoinRule.Public, + historyVisibility = RoomHistoryVisibility.WorldReadable, + canonicalAlias = A_ROOM_ALIAS, + ) + ) + with(awaitItem()) { + assertThat(editedSettings).isEqualTo(savedSettings) + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone) + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.Anyone) + assertThat(editedSettings.address).isEqualTo(A_ROOM_ALIAS.value) + assertThat(canBeSaved).isFalse() + } + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - change room access`() = runTest { + val presenter = createSecurityAndPrivacyPresenter() + presenter.test { + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) + assertThat(showRoomVisibilitySections).isFalse() + eventSink(SecurityAndPrivacyEvents.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.Anyone)) + } + with(awaitItem()) { + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone) + assertThat(showRoomVisibilitySections).isTrue() + assertThat(canBeSaved).isTrue() + eventSink(SecurityAndPrivacyEvents.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.InviteOnly)) + } + with(awaitItem()) { + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) + assertThat(showRoomVisibilitySections).isFalse() + assertThat(canBeSaved).isFalse() + } + } + } + + @Test + fun `present - change history visibility`() = runTest { + val presenter = createSecurityAndPrivacyPresenter() + presenter.test { + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.SinceSelection) + eventSink(SecurityAndPrivacyEvents.ChangeHistoryVisibility(SecurityAndPrivacyHistoryVisibility.SinceInvite)) + } + with(awaitItem()) { + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.SinceInvite) + assertThat(canBeSaved).isTrue() + eventSink(SecurityAndPrivacyEvents.ChangeHistoryVisibility(SecurityAndPrivacyHistoryVisibility.SinceSelection)) + } + with(awaitItem()) { + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.SinceSelection) + assertThat(canBeSaved).isFalse() + } + } + } + + @Test + fun `present - enable encryption`() = runTest { + val presenter = createSecurityAndPrivacyPresenter() + presenter.test { + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.isEncrypted).isFalse() + eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState) + } + with(awaitItem()) { + assertThat(showEncryptionConfirmation).isTrue() + eventSink(SecurityAndPrivacyEvents.CancelEnableEncryption) + } + with(awaitItem()) { + assertThat(showEncryptionConfirmation).isFalse() + eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState) + } + with(awaitItem()) { + assertThat(showEncryptionConfirmation).isTrue() + eventSink(SecurityAndPrivacyEvents.ConfirmEnableEncryption) + } + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.isEncrypted).isTrue() + assertThat(showEncryptionConfirmation).isFalse() + assertThat(canBeSaved).isTrue() + eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState) + } + with(awaitItem()) { + assertThat(editedSettings.isEncrypted).isFalse() + assertThat(canBeSaved).isFalse() + } + } + } + + @Test + fun `present - room visibility loading and change`() = runTest { + val room = FakeMatrixRoom( + canSendStateResult = { _, _ -> Result.success(true) }, + roomVisibilityResult = { Result.success(RoomVisibility.Private) } + ) + val presenter = createSecurityAndPrivacyPresenter(room = room) + presenter.test { + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Loading()) + } + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Success(false)) + eventSink(SecurityAndPrivacyEvents.ToggleRoomVisibility) + } + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Success(true)) + assertThat(canBeSaved).isTrue() + eventSink(SecurityAndPrivacyEvents.ToggleRoomVisibility) + } + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Success(false)) + assertThat(canBeSaved).isFalse() + } + } + } + + @Test + fun `present - save success`() = runTest { + val enableEncryptionLambda = lambdaRecorder> { Result.success(Unit) } + val updateJoinRuleLambda = lambdaRecorder> { Result.success(Unit) } + val updateRoomVisibilityLambda = lambdaRecorder> { Result.success(Unit) } + val updateRoomHistoryVisibilityLambda = lambdaRecorder> { Result.success(Unit) } + val room = FakeMatrixRoom( + canSendStateResult = { _, _ -> Result.success(true) }, + enableEncryptionResult = enableEncryptionLambda, + updateJoinRuleResult = updateJoinRuleLambda, + updateRoomVisibilityResult = updateRoomVisibilityLambda, + updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda, + roomVisibilityResult = { Result.success(RoomVisibility.Private) } + ) + val presenter = createSecurityAndPrivacyPresenter(room = room) + presenter.test { + skipItems(2) + with(awaitItem()) { + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) + eventSink(SecurityAndPrivacyEvents.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.Anyone)) + } + with(awaitItem()) { + eventSink(SecurityAndPrivacyEvents.ChangeHistoryVisibility(SecurityAndPrivacyHistoryVisibility.Anyone)) + } + with(awaitItem()) { + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.Anyone) + eventSink(SecurityAndPrivacyEvents.ConfirmEnableEncryption) + } + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.isEncrypted).isTrue() + eventSink(SecurityAndPrivacyEvents.ToggleRoomVisibility) + } + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Success(true)) + eventSink(SecurityAndPrivacyEvents.Save) + } + with(awaitItem()) { + assertThat(saveAction).isEqualTo(AsyncAction.Loading) + } + + room.givenRoomInfo( + aRoomInfo( + joinRule = JoinRule.Public, + historyVisibility = RoomHistoryVisibility.WorldReadable, + ) + ) + // Saved settings are updated 3 times to match the edited settings + skipItems(3) + with(awaitItem()) { + assertThat(saveAction).isEqualTo(AsyncAction.Success(Unit)) + assertThat(savedSettings).isEqualTo(editedSettings) + assertThat(canBeSaved).isFalse() + } + assert(enableEncryptionLambda).isCalledOnce() + assert(updateJoinRuleLambda).isCalledOnce() + assert(updateRoomVisibilityLambda).isCalledOnce() + assert(updateRoomHistoryVisibilityLambda).isCalledOnce() + } + } + + @Test + fun `present - save failure`() = runTest { + val enableEncryptionLambda = lambdaRecorder> { Result.success(Unit) } + val updateJoinRuleLambda = lambdaRecorder> { Result.success(Unit) } + val updateRoomVisibilityLambda = lambdaRecorder> { + Result.failure(Exception("Failed to update room visibility")) + } + val updateRoomHistoryVisibilityLambda = lambdaRecorder> { Result.success(Unit) } + val room = FakeMatrixRoom( + canSendStateResult = { _, _ -> Result.success(true) }, + enableEncryptionResult = enableEncryptionLambda, + updateJoinRuleResult = updateJoinRuleLambda, + updateRoomVisibilityResult = updateRoomVisibilityLambda, + updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda, + roomVisibilityResult = { Result.success(RoomVisibility.Private) } + ) + val presenter = createSecurityAndPrivacyPresenter(room = room) + presenter.test { + skipItems(2) + with(awaitItem()) { + assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) + eventSink(SecurityAndPrivacyEvents.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.Anyone)) + } + with(awaitItem()) { + eventSink(SecurityAndPrivacyEvents.ChangeHistoryVisibility(SecurityAndPrivacyHistoryVisibility.Anyone)) + } + with(awaitItem()) { + assertThat(editedSettings.historyVisibility).isEqualTo(SecurityAndPrivacyHistoryVisibility.Anyone) + eventSink(SecurityAndPrivacyEvents.ConfirmEnableEncryption) + } + skipItems(1) + with(awaitItem()) { + assertThat(editedSettings.isEncrypted).isTrue() + eventSink(SecurityAndPrivacyEvents.ToggleRoomVisibility) + } + with(awaitItem()) { + assertThat(editedSettings.isVisibleInRoomDirectory).isEqualTo(AsyncData.Success(true)) + eventSink(SecurityAndPrivacyEvents.Save) + } + with(awaitItem()) { + assertThat(saveAction).isEqualTo(AsyncAction.Loading) + } + + room.givenRoomInfo( + aRoomInfo( + joinRule = JoinRule.Public, + historyVisibility = RoomHistoryVisibility.WorldReadable, + ) + ) + // Saved settings are updated 2 times to match the edited settings + skipItems(2) + with(awaitItem()) { + assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java) + assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory) + assertThat(canBeSaved).isTrue() + } + assert(enableEncryptionLambda).isCalledOnce() + assert(updateJoinRuleLambda).isCalledOnce() + assert(updateRoomVisibilityLambda).isCalledOnce() + assert(updateRoomHistoryVisibilityLambda).isCalledOnce() + } + } + + private fun createSecurityAndPrivacyPresenter( + serverName: String = "matrix.org", + room: MatrixRoom = FakeMatrixRoom( + canSendStateResult = { _, _ -> Result.success(true) }, + roomVisibilityResult = { Result.success(RoomVisibility.Private) } + ), + navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(), + ): SecurityAndPrivacyPresenter { + return SecurityAndPrivacyPresenter( + room = room, + matrixClient = FakeMatrixClient( + userIdServerNameLambda = { serverName }, + ), + navigator = navigator + ) + } +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterViewTest.kt new file mode 100644 index 0000000000..66b05bf01b --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/securityandprivacy/SecurityAndPrivacyPresenterViewTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.roomdetails.securityandprivacy + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyState +import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyView +import io.element.android.features.roomdetails.impl.securityandprivacy.aSecurityAndPrivacyState +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.pressBack +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SecurityAndPrivacyPresenterViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `click on back invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setSecurityAndPrivacyView( + onBackClick = callback, + ) + rule.pressBack() + } + } + +} + + +private fun AndroidComposeTestRule.setSecurityAndPrivacyView( + state: SecurityAndPrivacyState = aSecurityAndPrivacyState( + eventSink = EventsRecorder(expectEvents = false), + ), + onBackClick: () -> Unit = EnsureNeverCalled(), +) { + setContent { + SecurityAndPrivacyView( + state = state, + onBackClick = onBackClick, + ) + } +}