feat(security&privacy) : add all tests for EditRoomAddress classes
This commit is contained in:
parent
73281be1af
commit
76bc87275c
10 changed files with 515 additions and 13 deletions
|
|
@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper
|
import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
|
|
||||||
interface SecurityAndPrivacyNavigator : Plugin {
|
interface SecurityAndPrivacyNavigator : Plugin {
|
||||||
fun openEditRoomAddress()
|
fun openEditRoomAddress()
|
||||||
fun closeEditorRoomAddress()
|
fun closeEditRoomAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackstackSecurityAndPrivacyNavigator(
|
class BackstackSecurityAndPrivacyNavigator(
|
||||||
|
|
@ -24,7 +24,7 @@ class BackstackSecurityAndPrivacyNavigator(
|
||||||
backStack.push(SecurityAndPrivacyFlowNode.NavTarget.EditRoomAddress)
|
backStack.push(SecurityAndPrivacyFlowNode.NavTarget.EditRoomAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeEditorRoomAddress() {
|
override fun closeEditRoomAddress() {
|
||||||
backStack.pop()
|
backStack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
||||||
room.updateCanonicalAlias(savedCanonicalAlias, newAlternativeAliases).getOrThrow()
|
room.updateCanonicalAlias(savedCanonicalAlias, newAlternativeAliases).getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
navigator.closeEditorRoomAddress()
|
navigator.closeEditRoomAddress()
|
||||||
}.runCatchingUpdatingState(saveAction)
|
}.runCatchingUpdatingState(saveAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,15 @@ import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
open class EditRoomAddressStateProvider : PreviewParameterProvider<EditRoomAddressState> {
|
open class EditRoomAddressStateProvider : PreviewParameterProvider<EditRoomAddressState> {
|
||||||
override val values: Sequence<EditRoomAddressState>
|
override val values: Sequence<EditRoomAddressState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aEditRoomAddressState(),
|
anEditRoomAddressState(),
|
||||||
aEditRoomAddressState(roomAddressValidity = RoomAddressValidity.NotAvailable),
|
anEditRoomAddressState(roomAddressValidity = RoomAddressValidity.NotAvailable),
|
||||||
aEditRoomAddressState(roomAddressValidity = RoomAddressValidity.InvalidSymbols),
|
anEditRoomAddressState(roomAddressValidity = RoomAddressValidity.InvalidSymbols),
|
||||||
aEditRoomAddressState(roomAddressValidity = RoomAddressValidity.Valid),
|
anEditRoomAddressState(roomAddressValidity = RoomAddressValidity.Valid),
|
||||||
aEditRoomAddressState(roomAddressValidity = RoomAddressValidity.Valid, saveAction = AsyncAction.Loading),
|
anEditRoomAddressState(roomAddressValidity = RoomAddressValidity.Valid, saveAction = AsyncAction.Loading),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aEditRoomAddressState(
|
fun anEditRoomAddressState(
|
||||||
roomAddress: String = "therapy",
|
roomAddress: String = "therapy",
|
||||||
roomAddressValidity: RoomAddressValidity = RoomAddressValidity.Unknown,
|
roomAddressValidity: RoomAddressValidity = RoomAddressValidity.Unknown,
|
||||||
homeserverName: String = ":myserver.org",
|
homeserverName: String = ":myserver.org",
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
class FakeSecurityAndPrivacyNavigator(
|
class FakeSecurityAndPrivacyNavigator(
|
||||||
private val openEditRoomAddressLambda: () -> Unit = { lambdaError() },
|
private val openEditRoomAddressLambda: () -> Unit = { lambdaError() },
|
||||||
private val closeEditorRoomAddressLambda: () -> Unit = { lambdaError() },
|
private val closeEditRoomAddressLambda: () -> Unit = { lambdaError() },
|
||||||
) : SecurityAndPrivacyNavigator {
|
) : SecurityAndPrivacyNavigator {
|
||||||
override fun openEditRoomAddress() {
|
override fun openEditRoomAddress() {
|
||||||
openEditRoomAddressLambda()
|
openEditRoomAddressLambda()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeEditorRoomAddress() {
|
override fun closeEditRoomAddress() {
|
||||||
closeEditorRoomAddressLambda()
|
closeEditRoomAddressLambda()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,20 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - edit room address`() = runTest {
|
||||||
|
val openEditRoomAddressLambda = lambdaRecorder<Unit> { }
|
||||||
|
val navigator = FakeSecurityAndPrivacyNavigator(openEditRoomAddressLambda)
|
||||||
|
val presenter = createSecurityAndPrivacyPresenter(navigator = navigator)
|
||||||
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(SecurityAndPrivacyEvents.EditRoomAddress)
|
||||||
|
}
|
||||||
|
assert(openEditRoomAddressLambda).isCalledOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save success`() = runTest {
|
fun `present - save success`() = runTest {
|
||||||
val enableEncryptionLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
val enableEncryptionLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,355 @@
|
||||||
|
/*
|
||||||
|
* 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.editroomaddress
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressEvents
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressPresenter
|
||||||
|
import io.element.android.features.roomdetails.securityandprivacy.FakeSecurityAndPrivacyNavigator
|
||||||
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||||
|
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
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.alias.FakeRoomAliasHelper
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.lambda.value
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
class EditRoomAddressPresenterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - initial state no address`() = runTest {
|
||||||
|
val presenter = createEditRoomAddressPresenter()
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(homeserverName).isEqualTo("matrix.org")
|
||||||
|
assertThat(canBeSaved).isFalse()
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
assertThat(roomAddress).isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - initial state address matching own homeserver`() = runTest {
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
canonicalAlias = RoomAlias("#canonical:matrix.org"),
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(room = room)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(homeserverName).isEqualTo("matrix.org")
|
||||||
|
assertThat(canBeSaved).isFalse()
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
assertThat(roomAddress).isEqualTo("canonical")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - initial state address not matching own homeserver`() = runTest {
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
canonicalAlias = RoomAlias("#canonical:notmatrix.org"),
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(room = room)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(homeserverName).isEqualTo("matrix.org")
|
||||||
|
assertThat(canBeSaved).isFalse()
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
assertThat(roomAddress).isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - room address change invalid state`() = runTest {
|
||||||
|
val roomAliasHelper = FakeRoomAliasHelper(
|
||||||
|
isRoomAliasValidLambda = { false }
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(roomAliasHelper = roomAliasHelper)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("invalid"))
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddress).isEqualTo("invalid")
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.InvalidSymbols)
|
||||||
|
assertThat(canBeSaved).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - room address change valid state`() = runTest {
|
||||||
|
val presenter = createEditRoomAddressPresenter()
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddress).isEqualTo("valid")
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid)
|
||||||
|
assertThat(canBeSaved).isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - room address change alias unavailable`() = runTest {
|
||||||
|
val client = createMatrixClient(isAliasAvailable = false)
|
||||||
|
val presenter = createEditRoomAddressPresenter(client = client)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddress).isEqualTo("valid")
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Unknown)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.NotAvailable)
|
||||||
|
assertThat(canBeSaved).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - save success no current alias`() = runTest {
|
||||||
|
val publishAliasInRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val updateCanonicalAliasResult = lambdaRecorder<RoomAlias?, List<RoomAlias>, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||||
|
val removeAliasFromRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val closeEditAddressLambda = lambdaRecorder<Unit> { }
|
||||||
|
val navigator = FakeSecurityAndPrivacyNavigator(
|
||||||
|
closeEditRoomAddressLambda = closeEditAddressLambda
|
||||||
|
)
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
|
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
skipItems(1)
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid)
|
||||||
|
assertThat(canBeSaved).isTrue()
|
||||||
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Loading)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||||
|
}
|
||||||
|
|
||||||
|
val createdAlias = RoomAlias("#valid:matrix.org")
|
||||||
|
assert(updateCanonicalAliasResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(createdAlias), value(emptyList<RoomAlias>()))
|
||||||
|
|
||||||
|
assert(publishAliasInRoomDirectoryResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(createdAlias))
|
||||||
|
|
||||||
|
assert(removeAliasFromRoomDirectoryResult).isNeverCalled()
|
||||||
|
|
||||||
|
assert(closeEditAddressLambda).isCalledOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - save success current canonical alias from own homeserver`() = runTest {
|
||||||
|
val publishAliasInRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val removeAliasFromRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val updateCanonicalAliasResult = lambdaRecorder<RoomAlias?, List<RoomAlias>, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||||
|
val closeEditAddressLambda = lambdaRecorder<Unit> { }
|
||||||
|
|
||||||
|
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||||
|
val canonicalAlias = RoomAlias("#canonical:matrix.org")
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
canonicalAlias = canonicalAlias,
|
||||||
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
|
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||||
|
removeRoomAliasFromRoomDirectoryResult = removeAliasFromRoomDirectoryResult
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
skipItems(1)
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid)
|
||||||
|
assertThat(canBeSaved).isTrue()
|
||||||
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Loading)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||||
|
}
|
||||||
|
|
||||||
|
val createdAlias = RoomAlias("#valid:matrix.org")
|
||||||
|
assert(updateCanonicalAliasResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(createdAlias), value(emptyList<RoomAlias>()))
|
||||||
|
|
||||||
|
assert(publishAliasInRoomDirectoryResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(createdAlias))
|
||||||
|
|
||||||
|
assert(removeAliasFromRoomDirectoryResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(canonicalAlias))
|
||||||
|
|
||||||
|
assert(closeEditAddressLambda).isCalledOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - save success current canonical alias from other homeserver`() = runTest {
|
||||||
|
val publishAliasInRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val removeAliasFromRoomDirectoryResult = lambdaRecorder<RoomAlias, Result<Boolean>> { _ -> Result.success(true) }
|
||||||
|
val updateCanonicalAliasResult = lambdaRecorder<RoomAlias?, List<RoomAlias>, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||||
|
val closeEditAddressLambda = lambdaRecorder<Unit> { }
|
||||||
|
|
||||||
|
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||||
|
val canonicalAlias = RoomAlias("#canonical:notmatrix.org")
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
canonicalAlias = canonicalAlias,
|
||||||
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
|
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||||
|
removeRoomAliasFromRoomDirectoryResult = removeAliasFromRoomDirectoryResult
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(room = room, navigator = navigator)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
skipItems(1)
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid)
|
||||||
|
assertThat(canBeSaved).isTrue()
|
||||||
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Loading)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||||
|
}
|
||||||
|
|
||||||
|
val createdAlias = RoomAlias("#valid:matrix.org")
|
||||||
|
assert(updateCanonicalAliasResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(canonicalAlias), value(listOf(createdAlias)))
|
||||||
|
|
||||||
|
assert(publishAliasInRoomDirectoryResult)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(createdAlias))
|
||||||
|
|
||||||
|
assert(removeAliasFromRoomDirectoryResult).isNeverCalled()
|
||||||
|
|
||||||
|
assert(closeEditAddressLambda).isCalledOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - save failure`() = runTest {
|
||||||
|
val closeEditAddressLambda = lambdaRecorder<Unit> { }
|
||||||
|
val navigator = FakeSecurityAndPrivacyNavigator(
|
||||||
|
closeEditRoomAddressLambda = closeEditAddressLambda
|
||||||
|
)
|
||||||
|
val presenter = createEditRoomAddressPresenter(navigator = navigator)
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.RoomAddressChanged("valid"))
|
||||||
|
}
|
||||||
|
skipItems(1)
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(roomAddressValidity).isEqualTo(RoomAddressValidity.Valid)
|
||||||
|
assertThat(canBeSaved).isTrue()
|
||||||
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Loading)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(closeEditAddressLambda).isNeverCalled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - dismiss error`() = runTest {
|
||||||
|
val presenter = createEditRoomAddressPresenter()
|
||||||
|
presenter.test {
|
||||||
|
with(awaitItem()) {
|
||||||
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
|
eventSink(EditRoomAddressEvents.DismissError)
|
||||||
|
}
|
||||||
|
with(awaitItem()) {
|
||||||
|
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMatrixClient(isAliasAvailable: Boolean = true) = FakeMatrixClient(
|
||||||
|
userIdServerNameLambda = { "matrix.org" },
|
||||||
|
resolveRoomAliasResult = {
|
||||||
|
val resolvedRoomAlias = if (isAliasAvailable) {
|
||||||
|
Optional.empty()
|
||||||
|
} else {
|
||||||
|
Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList()))
|
||||||
|
}
|
||||||
|
Result.success(resolvedRoomAlias)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun createEditRoomAddressPresenter(
|
||||||
|
client: FakeMatrixClient = createMatrixClient(),
|
||||||
|
room: MatrixRoom = FakeMatrixRoom(),
|
||||||
|
navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(),
|
||||||
|
roomAliasHelper: RoomAliasHelper = FakeRoomAliasHelper()
|
||||||
|
): EditRoomAddressPresenter {
|
||||||
|
return EditRoomAddressPresenter(
|
||||||
|
room = room,
|
||||||
|
client = client,
|
||||||
|
roomAliasHelper = roomAliasHelper,
|
||||||
|
navigator = navigator
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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.editroomaddress
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||||
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
|
import androidx.compose.ui.test.performTextInput
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressEvents
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressState
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.EditRoomAddressView
|
||||||
|
import io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress.anEditRoomAddressState
|
||||||
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
|
import io.element.android.libraries.testtags.TestTags
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
import io.element.android.tests.testutils.clickOn
|
||||||
|
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 EditRoomAddressViewTest {
|
||||||
|
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on back invokes expected callback`() {
|
||||||
|
ensureCalledOnce { callback ->
|
||||||
|
rule.setEditRoomAddressView(onBackClick = callback)
|
||||||
|
rule.pressBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on disabled save doesn't emit event`() {
|
||||||
|
val recorder = EventsRecorder<EditRoomAddressEvents>(expectEvents = false)
|
||||||
|
val state = anEditRoomAddressState(eventSink = recorder)
|
||||||
|
rule.setEditRoomAddressView(state)
|
||||||
|
rule.clickOn(CommonStrings.action_save)
|
||||||
|
recorder.assertEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on enabled save emits the expected event`() {
|
||||||
|
val recorder = EventsRecorder<EditRoomAddressEvents>()
|
||||||
|
val state = anEditRoomAddressState(
|
||||||
|
roomAddress = "room",
|
||||||
|
roomAddressValidity = RoomAddressValidity.Valid,
|
||||||
|
eventSink = recorder
|
||||||
|
)
|
||||||
|
rule.setEditRoomAddressView(state)
|
||||||
|
rule.clickOn(CommonStrings.action_save)
|
||||||
|
recorder.assertSingle(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `text changes on text field emits the expected event`() {
|
||||||
|
val recorder = EventsRecorder<EditRoomAddressEvents>()
|
||||||
|
val state = anEditRoomAddressState(
|
||||||
|
roomAddress = "",
|
||||||
|
eventSink = recorder
|
||||||
|
)
|
||||||
|
rule.setEditRoomAddressView(state)
|
||||||
|
|
||||||
|
rule.onNodeWithTag(TestTags.roomAddressField.value).performTextInput("alias")
|
||||||
|
recorder.assertSingle(EditRoomAddressEvents.RoomAddressChanged("alias"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on dismiss error emits the expected event`() {
|
||||||
|
val recorder = EventsRecorder<EditRoomAddressEvents>()
|
||||||
|
val state = anEditRoomAddressState(
|
||||||
|
roomAddress = "",
|
||||||
|
saveAction = AsyncAction.Failure(IllegalStateException()),
|
||||||
|
eventSink = recorder
|
||||||
|
)
|
||||||
|
rule.setEditRoomAddressView(state)
|
||||||
|
rule.clickOn(CommonStrings.action_cancel)
|
||||||
|
recorder.assertSingle(EditRoomAddressEvents.DismissError)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `click on retry error emits the expected event`() {
|
||||||
|
val recorder = EventsRecorder<EditRoomAddressEvents>()
|
||||||
|
val state = anEditRoomAddressState(
|
||||||
|
roomAddress = "",
|
||||||
|
saveAction = AsyncAction.Failure(IllegalStateException()),
|
||||||
|
eventSink = recorder
|
||||||
|
)
|
||||||
|
rule.setEditRoomAddressView(state)
|
||||||
|
rule.clickOn(CommonStrings.action_retry)
|
||||||
|
recorder.assertSingle(EditRoomAddressEvents.Save)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setEditRoomAddressView(
|
||||||
|
state: EditRoomAddressState = anEditRoomAddressState(
|
||||||
|
eventSink = EventsRecorder(expectEvents = false),
|
||||||
|
),
|
||||||
|
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||||
|
) {
|
||||||
|
setContent {
|
||||||
|
EditRoomAddressView(
|
||||||
|
state = state,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||||
|
import io.element.android.libraries.testtags.TestTags
|
||||||
|
import io.element.android.libraries.testtags.testTag
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -28,7 +30,7 @@ fun RoomAddressField(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
TextField(
|
TextField(
|
||||||
modifier = modifier,
|
modifier = modifier.testTag(TestTags.roomAddressField),
|
||||||
value = address,
|
value = address,
|
||||||
label = label,
|
label = label,
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
|
|
|
||||||
|
|
@ -111,4 +111,11 @@ object TestTags {
|
||||||
* Generic call to action.
|
* Generic call to action.
|
||||||
*/
|
*/
|
||||||
val callToAction = TestTag("call_to_action")
|
val callToAction = TestTag("call_to_action")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Room address field.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
val roomAddressField = TestTag("room_address_field")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue