feat(security&privacy) : start writing tests
This commit is contained in:
parent
5c1bd6ddb7
commit
ee4fba327c
8 changed files with 441 additions and 5 deletions
|
|
@ -69,7 +69,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
|
||||||
isEncrypted = room.isEncrypted,
|
isEncrypted = room.isEncrypted,
|
||||||
isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value,
|
isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value,
|
||||||
historyVisibility = roomInfo?.historyVisibility.map(),
|
historyVisibility = roomInfo?.historyVisibility.map(),
|
||||||
addressName = roomInfo?.firstDisplayableAlias(homeserverName)?.value
|
address = roomInfo?.firstDisplayableAlias(homeserverName)?.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +91,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
|
||||||
isEncrypted = editedIsEncrypted,
|
isEncrypted = editedIsEncrypted,
|
||||||
isVisibleInRoomDirectory = editedVisibleInRoomDirectory,
|
isVisibleInRoomDirectory = editedVisibleInRoomDirectory,
|
||||||
historyVisibility = editedHistoryVisibility,
|
historyVisibility = editedHistoryVisibility,
|
||||||
addressName = savedSettings.addressName,
|
address = savedSettings.address,
|
||||||
)
|
)
|
||||||
|
|
||||||
var showEncryptionConfirmation by remember(savedSettings.isEncrypted) { mutableStateOf(false) }
|
var showEncryptionConfirmation by remember(savedSettings.isEncrypted) { mutableStateOf(false) }
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ data class SecurityAndPrivacyState(
|
||||||
val eventSink: (SecurityAndPrivacyEvents) -> Unit
|
val eventSink: (SecurityAndPrivacyEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val canBeSaved = savedSettings != editedSettings
|
val canBeSaved = savedSettings != editedSettings
|
||||||
|
|
||||||
val availableHistoryVisibilities = buildSet {
|
val availableHistoryVisibilities = buildSet {
|
||||||
|
|
@ -38,13 +40,16 @@ data class SecurityAndPrivacyState(
|
||||||
val showRoomVisibilitySections = permissions.canChangeRoomVisibility && editedSettings.roomAccess != SecurityAndPrivacyRoomAccess.InviteOnly
|
val showRoomVisibilitySections = permissions.canChangeRoomVisibility && editedSettings.roomAccess != SecurityAndPrivacyRoomAccess.InviteOnly
|
||||||
val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility
|
val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility
|
||||||
val showEncryptionSection = permissions.canChangeEncryption
|
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(
|
data class SecurityAndPrivacySettings(
|
||||||
val roomAccess: SecurityAndPrivacyRoomAccess,
|
val roomAccess: SecurityAndPrivacyRoomAccess,
|
||||||
val isEncrypted: Boolean,
|
val isEncrypted: Boolean,
|
||||||
val historyVisibility: SecurityAndPrivacyHistoryVisibility,
|
val historyVisibility: SecurityAndPrivacyHistoryVisibility,
|
||||||
val addressName: String?,
|
val address: String?,
|
||||||
val isVisibleInRoomDirectory: AsyncData<Boolean>
|
val isVisibleInRoomDirectory: AsyncData<Boolean>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ fun aSecurityAndPrivacySettings(
|
||||||
) = SecurityAndPrivacySettings(
|
) = SecurityAndPrivacySettings(
|
||||||
roomAccess = roomAccess,
|
roomAccess = roomAccess,
|
||||||
isEncrypted = isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
addressName = formattedAddress,
|
address = formattedAddress,
|
||||||
historyVisibility = historyVisibility,
|
historyVisibility = historyVisibility,
|
||||||
isVisibleInRoomDirectory = isVisibleInRoomDirectory
|
isVisibleInRoomDirectory = isVisibleInRoomDirectory
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ fun SecurityAndPrivacyView(
|
||||||
if (state.showRoomVisibilitySections) {
|
if (state.showRoomVisibilitySections) {
|
||||||
RoomVisibilitySection(state.homeserverName)
|
RoomVisibilitySection(state.homeserverName)
|
||||||
RoomAddressSection(
|
RoomAddressSection(
|
||||||
roomAddress = state.editedSettings.addressName,
|
roomAddress = state.editedSettings.address,
|
||||||
homeserverName = state.homeserverName,
|
homeserverName = state.homeserverName,
|
||||||
onRoomAddressClick = { state.eventSink(SecurityAndPrivacyEvents.EditRoomAddress) },
|
onRoomAddressClick = { state.eventSink(SecurityAndPrivacyEvents.EditRoomAddress) },
|
||||||
isVisibleInRoomDirectory = state.editedSettings.isVisibleInRoomDirectory,
|
isVisibleInRoomDirectory = state.editedSettings.isVisibleInRoomDirectory,
|
||||||
|
|
|
||||||
|
|
@ -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")
|
@Config(qualifiers = "h1024dp")
|
||||||
@Test
|
@Test
|
||||||
fun `click on add topic emit expected event`() {
|
fun `click on add topic emit expected event`() {
|
||||||
|
|
@ -298,6 +313,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
|
||||||
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
|
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
|
||||||
onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
|
onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
|
||||||
onKnockRequestsClick: () -> Unit = EnsureNeverCalled(),
|
onKnockRequestsClick: () -> Unit = EnsureNeverCalled(),
|
||||||
|
onSecurityAndPrivacyClick: () -> Unit = EnsureNeverCalled(),
|
||||||
) {
|
) {
|
||||||
setContent {
|
setContent {
|
||||||
RoomDetailsView(
|
RoomDetailsView(
|
||||||
|
|
@ -315,6 +331,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
|
||||||
onJoinCallClick = onJoinCallClick,
|
onJoinCallClick = onJoinCallClick,
|
||||||
onPinnedMessagesClick = onPinnedMessagesClick,
|
onPinnedMessagesClick = onPinnedMessagesClick,
|
||||||
onKnockRequestsClick = onKnockRequestsClick,
|
onKnockRequestsClick = onKnockRequestsClick,
|
||||||
|
onSecurityAndPrivacyClick = onSecurityAndPrivacyClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Boolean>())
|
||||||
|
}
|
||||||
|
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<Unit>> { Result.success(Unit) }
|
||||||
|
val updateJoinRuleLambda = lambdaRecorder<JoinRule, Result<Unit>> { Result.success(Unit) }
|
||||||
|
val updateRoomVisibilityLambda = lambdaRecorder<RoomVisibility, Result<Unit>> { Result.success(Unit) }
|
||||||
|
val updateRoomHistoryVisibilityLambda = lambdaRecorder<RoomHistoryVisibility, Result<Unit>> { 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<Unit>> { Result.success(Unit) }
|
||||||
|
val updateJoinRuleLambda = lambdaRecorder<JoinRule, Result<Unit>> { Result.success(Unit) }
|
||||||
|
val updateRoomVisibilityLambda = lambdaRecorder<RoomVisibility, Result<Unit>> {
|
||||||
|
Result.failure(Exception("Failed to update room visibility"))
|
||||||
|
}
|
||||||
|
val updateRoomHistoryVisibilityLambda = lambdaRecorder<RoomHistoryVisibility, Result<Unit>> { 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<ComponentActivity>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on back invokes expected callback`() {
|
||||||
|
ensureCalledOnce { callback ->
|
||||||
|
rule.setSecurityAndPrivacyView(
|
||||||
|
onBackClick = callback,
|
||||||
|
)
|
||||||
|
rule.pressBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSecurityAndPrivacyView(
|
||||||
|
state: SecurityAndPrivacyState = aSecurityAndPrivacyState(
|
||||||
|
eventSink = EventsRecorder(expectEvents = false),
|
||||||
|
),
|
||||||
|
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||||
|
) {
|
||||||
|
setContent {
|
||||||
|
SecurityAndPrivacyView(
|
||||||
|
state = state,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue