Add test on DefaultEntryPoints

This commit is contained in:
Benoit Marty 2025-09-12 21:32:24 +02:00
parent a575019760
commit a1aeb24f23
93 changed files with 2426 additions and 418 deletions

View file

@ -9,6 +9,6 @@ package io.element.android.appnav.di
import io.element.android.libraries.matrix.api.room.JoinedRoom
interface RoomComponentFactory {
fun interface RoomComponentFactory {
fun create(room: JoinedRoom): Any
}

View file

@ -9,4 +9,4 @@ package io.element.android.features.analytics.api
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
interface AnalyticsEntryPoint : SimpleFeatureEntryPoint
fun interface AnalyticsEntryPoint : SimpleFeatureEntryPoint

View file

@ -14,7 +14,7 @@ import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.JoinedRoom
interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint {
fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint {
fun builder(parentNode: Node, buildContext: BuildContext): Builder
interface Builder {

View file

@ -55,7 +55,7 @@ class ChangeRolesPresenter(
private val analyticsService: AnalyticsService,
) : Presenter<ChangeRolesState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(role: RoomMember.Role): ChangeRolesPresenter
}

View file

@ -37,7 +37,6 @@ import kotlinx.collections.immutable.toPersistentMap
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.collections.plus
class ChangeRolesPresenterTest {
@Test
@ -556,18 +555,18 @@ class ChangeRolesPresenterTest {
users = pairs.associate { (userId, role) -> userId to role.powerLevel }.toPersistentMap()
)
}
private fun TestScope.createChangeRolesPresenter(
role: RoomMember.Role = RoomMember.Role.Admin,
room: FakeJoinedRoom = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {})),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
): ChangeRolesPresenter {
return ChangeRolesPresenter(
role = role,
room = room,
dispatchers = dispatchers,
analyticsService = analyticsService,
)
}
}
internal fun TestScope.createChangeRolesPresenter(
role: RoomMember.Role = RoomMember.Role.Admin,
room: FakeJoinedRoom = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {})),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
): ChangeRolesPresenter {
return ChangeRolesPresenter(
role = role,
room = room,
dispatchers = dispatchers,
analyticsService = analyticsService,
)
}

View file

@ -0,0 +1,44 @@
/*
* 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.changeroommemberroles.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultChangeRoomMemberRolesEntyPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultChangeRoomMemberRolesEntyPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ChangeRoomMemberRolesRootNode(
buildContext = buildContext,
plugins = plugins,
roomComponentFactory = { },
)
}
val room = FakeJoinedRoom()
val listType = ChangeRoomMemberRolesListType.Admins
val result = entryPoint.builder(parentNode, BuildContext.root(null))
.room(FakeJoinedRoom())
.listType(listType)
.build()
assertThat(result).isInstanceOf(ChangeRoomMemberRolesRootNode::class.java)
// Search for the Inputs plugin
val input = result.plugins.filterIsInstance<ChangeRoomMemberRolesRootNode.Inputs>().single()
assertThat(input.joinedRoom.roomId).isEqualTo(room.roomId)
assertThat(input.listType).isEqualTo(listType)
}
}

View file

@ -0,0 +1,40 @@
/*
* 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.createroom.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.createroom.api.CreateRoomEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultCreateRoomEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultCreateRoomEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
CreateRoomFlowNode(
buildContext = buildContext,
plugins = plugins,
)
}
val callback = object : CreateRoomEntryPoint.Callback {
override fun onRoomCreated(roomId: RoomId) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result.plugins).contains(callback)
}
}

View file

@ -149,9 +149,10 @@ class AccountDeactivationPresenterTest {
}
}
private fun createPresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
) = AccountDeactivationPresenter(
matrixClient = matrixClient,
)
}
internal fun createPresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
) = AccountDeactivationPresenter(
matrixClient = matrixClient,
)

View file

@ -0,0 +1,32 @@
/*
* 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.logout.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultAccountDeactivationEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultAccountDeactivationEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
AccountDeactivationNode(
buildContext = buildContext,
plugins = plugins,
presenter = createPresenter(),
)
}
val result = entryPoint.createNode(parentNode, BuildContext.root(null))
assertThat(result).isInstanceOf(AccountDeactivationNode::class.java)
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.ftue.impl
import android.content.Context
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultFtueEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultFtueEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
FtueFlowNode(
buildContext = buildContext,
plugins = plugins,
analyticsEntryPoint = { _, _ -> lambdaError() },
ftueState = createDefaultFtueService(),
analyticsService = FakeAnalyticsService(),
lockScreenEntryPoint = object : LockScreenEntryPoint {
override fun nodeBuilder(
parentNode: com.bumble.appyx.core.node.Node,
buildContext: BuildContext,
navTarget: LockScreenEntryPoint.Target
): LockScreenEntryPoint.NodeBuilder {
lambdaError()
}
override fun pinUnlockIntent(context: Context): Intent {
lambdaError()
}
},
)
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.build()
assertThat(result).isInstanceOf(FtueFlowNode::class.java)
}
}

View file

@ -226,22 +226,22 @@ class DefaultFtueServiceTest {
resetPermissionLambda.assertions().isCalledOnce()
.with(value("android.permission.POST_NOTIFICATIONS"))
}
private fun TestScope.createDefaultFtueService(
sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
analyticsService: AnalyticsService = FakeAnalyticsService(),
permissionStateProvider: PermissionStateProvider = FakePermissionStateProvider(permissionGranted = false),
lockScreenService: LockScreenService = FakeLockScreenService(),
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
// First version where notification permission is required
sdkIntVersion: Int = Build.VERSION_CODES.TIRAMISU,
) = DefaultFtueService(
sessionCoroutineScope = backgroundScope,
sessionVerificationService = sessionVerificationService,
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkIntVersion),
analyticsService = analyticsService,
permissionStateProvider = permissionStateProvider,
lockScreenService = lockScreenService,
sessionPreferencesStore = sessionPreferencesStore,
)
}
internal fun TestScope.createDefaultFtueService(
sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
analyticsService: AnalyticsService = FakeAnalyticsService(),
permissionStateProvider: PermissionStateProvider = FakePermissionStateProvider(permissionGranted = false),
lockScreenService: LockScreenService = FakeLockScreenService(),
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
// First version where notification permission is required
sdkIntVersion: Int = Build.VERSION_CODES.TIRAMISU,
) = DefaultFtueService(
sessionCoroutineScope = backgroundScope,
sessionVerificationService = sessionVerificationService,
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkIntVersion),
analyticsService = analyticsService,
permissionStateProvider = permissionStateProvider,
lockScreenService = lockScreenService,
sessionPreferencesStore = sessionPreferencesStore,
)

View file

@ -0,0 +1,59 @@
/*
* 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.home.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.home.api.HomeEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultHomeEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultHomeEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
HomeFlowNode(
buildContext = buildContext,
plugins = plugins,
matrixClient = FakeMatrixClient(),
presenter = createHomePresenter(),
inviteFriendsUseCase = { lambdaError() },
analyticsService = FakeAnalyticsService(),
acceptDeclineInviteView = { _, _, _, _ -> lambdaError() },
directLogoutView = { _ -> lambdaError() },
reportRoomEntryPoint = { _, _, _ -> lambdaError() },
declineInviteAndBlockUserEntryPoint = { _, _, _ -> lambdaError() },
changeRoomMemberRolesEntryPoint = { _, _ -> lambdaError() },
leaveRoomRenderer = { _, _, _ -> lambdaError() },
)
}
val callback = object : HomeEntryPoint.Callback {
override fun onRoomClick(roomId: RoomId) = lambdaError()
override fun onStartChatClick() = lambdaError()
override fun onSettingsClick() = lambdaError()
override fun onSetUpRecoveryClick() = lambdaError()
override fun onSessionConfirmRecoveryKeyClick() = lambdaError()
override fun onRoomSettingsClick(roomId: RoomId) = lambdaError()
override fun onReportBugClick() = lambdaError()
override fun onLogoutForNativeSlidingSyncMigrationNeeded() = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(HomeFlowNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -175,24 +175,24 @@ class HomePresenterTest {
assertThat(finalState.showNavigationBar).isFalse()
}
}
private fun createHomePresenter(
client: MatrixClient = FakeMatrixClient(),
syncService: SyncService = FakeSyncService(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { flowOf(false) },
indicatorService: IndicatorService = FakeIndicatorService(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() },
) = HomePresenter(
client = client,
syncService = syncService,
snackbarDispatcher = snackbarDispatcher,
indicatorService = indicatorService,
logoutPresenter = { aDirectLogoutState() },
roomListPresenter = { aRoomListState() },
homeSpacesPresenter = homeSpacesPresenter,
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
featureFlagService = featureFlagService,
)
}
internal fun createHomePresenter(
client: MatrixClient = FakeMatrixClient(),
syncService: SyncService = FakeSyncService(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { flowOf(false) },
indicatorService: IndicatorService = FakeIndicatorService(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() },
) = HomePresenter(
client = client,
syncService = syncService,
snackbarDispatcher = snackbarDispatcher,
indicatorService = indicatorService,
logoutPresenter = { aDirectLogoutState() },
roomListPresenter = { aRoomListState() },
homeSpacesPresenter = homeSpacesPresenter,
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
featureFlagService = featureFlagService,
)

View file

@ -11,7 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.libraries.matrix.api.core.RoomId
interface AcceptDeclineInviteView {
fun interface AcceptDeclineInviteView {
@Composable
fun Render(
state: AcceptDeclineInviteState,

View file

@ -12,6 +12,6 @@ import com.bumble.appyx.core.node.Node
import io.element.android.features.invite.api.InviteData
import io.element.android.libraries.architecture.FeatureEntryPoint
interface DeclineInviteAndBlockEntryPoint : FeatureEntryPoint {
fun interface DeclineInviteAndBlockEntryPoint : FeatureEntryPoint {
fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData): Node
}

View file

@ -35,7 +35,7 @@ class DeclineAndBlockPresenter(
private val snackbarDispatcher: SnackbarDispatcher,
) : Presenter<DeclineAndBlockState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(inviteData: InviteData): DeclineAndBlockPresenter
}

View file

@ -11,6 +11,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.invite.api.InviteData
import io.element.android.features.invite.impl.DeclineInvite
import io.element.android.features.invite.impl.fake.FakeDeclineInvite
import io.element.android.features.invite.test.anInviteData
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.core.RoomId
@ -148,28 +149,16 @@ class DeclineAndBlockPresenterTest {
.isCalledOnce()
.with(value(A_ROOM_ID), value(true), value(false), value(""))
}
private fun anInviteData(
roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME,
isDm: Boolean = false,
): InviteData {
return InviteData(
roomId = roomId,
roomName = name,
isDm = isDm,
)
}
private fun createDeclineAndBlockPresenter(
inviteData: InviteData = anInviteData(),
declineInvite: DeclineInvite = FakeDeclineInvite(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
): DeclineAndBlockPresenter {
return DeclineAndBlockPresenter(
inviteData = inviteData,
declineInvite = declineInvite,
snackbarDispatcher = snackbarDispatcher,
)
}
}
internal fun createDeclineAndBlockPresenter(
inviteData: InviteData = anInviteData(),
declineInvite: DeclineInvite = FakeDeclineInvite(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
): DeclineAndBlockPresenter {
return DeclineAndBlockPresenter(
inviteData = inviteData,
declineInvite = declineInvite,
snackbarDispatcher = snackbarDispatcher,
)
}

View file

@ -0,0 +1,39 @@
/*
* 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.invite.impl.declineandblock
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.invite.test.anInviteData
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultDeclineAndBlockEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultDeclineAndBlockEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
DeclineAndBlockNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { inviteData -> createDeclineAndBlockPresenter() }
)
}
val inviteData = anInviteData()
val result = entryPoint.createNode(
parentNode = parentNode,
buildContext = BuildContext.root(null),
inviteData = inviteData
)
assertThat(result).isInstanceOf(DeclineAndBlockNode::class.java)
assertThat(result.plugins).contains(DeclineAndBlockNode.Inputs(inviteData))
}
}

View file

@ -70,7 +70,7 @@ class JoinRoomPresenter(
private val buildMeta: BuildMeta,
private val seenInvitesStore: SeenInvitesStore,
) : Presenter<JoinRoomState> {
interface Factory {
fun interface Factory {
fun create(
roomId: RoomId,
roomIdOrAlias: RoomIdOrAlias,

View file

@ -0,0 +1,54 @@
/*
* 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.joinroom.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.JoinedRoom
import io.element.android.features.invite.api.InviteData
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
import io.element.android.features.joinroom.api.JoinRoomEntryPoint
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
import java.util.Optional
@RunWith(AndroidJUnit4::class)
class DefaultJoinRoomEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultJoinRoomEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
JoinRoomFlowNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { _, _, _, _, _ -> createJoinRoomPresenter() },
acceptDeclineInviteView = { _, _, _, _ -> lambdaError() },
declineAndBlockEntryPoint = object : DeclineInviteAndBlockEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData) = lambdaError()
}
)
}
val inputs = JoinRoomEntryPoint.Inputs(
roomId = A_ROOM_ID,
roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(),
roomDescription = Optional.ofNullable(null),
serverNames = emptyList(),
trigger = JoinedRoom.Trigger.RoomDirectory,
)
val result = entryPoint.createNode(parentNode, BuildContext.root(null), inputs)
assertThat(result).isInstanceOf(JoinRoomFlowNode::class.java)
assertThat(result.plugins).contains(inputs)
}
}

View file

@ -1042,39 +1042,6 @@ class JoinRoomPresenterTest {
}
}
private fun createJoinRoomPresenter(
roomId: RoomId = A_ROOM_ID,
roomDescription: Optional<RoomDescription> = Optional.empty(),
serverNames: List<String> = emptyList(),
trigger: JoinedRoom.Trigger = JoinedRoom.Trigger.Invite,
matrixClient: MatrixClient = FakeMatrixClient(),
joinRoomLambda: (RoomIdOrAlias, List<String>, JoinedRoom.Trigger) -> Result<Unit> = { _, _, _ ->
Result.success(Unit)
},
knockRoom: KnockRoom = FakeKnockRoom(),
cancelKnockRoom: CancelKnockRoom = FakeCancelKnockRoom(),
forgetRoom: ForgetRoom = FakeForgetRoom(),
buildMeta: BuildMeta = aBuildMeta(applicationName = "AppName"),
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
): JoinRoomPresenter {
return JoinRoomPresenter(
roomId = roomId,
roomIdOrAlias = roomId.toRoomIdOrAlias(),
roomDescription = roomDescription,
serverNames = serverNames,
trigger = trigger,
matrixClient = matrixClient,
joinRoom = FakeJoinRoom(joinRoomLambda),
knockRoom = knockRoom,
cancelKnockRoom = cancelKnockRoom,
forgetRoom = forgetRoom,
buildMeta = buildMeta,
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
seenInvitesStore = seenInvitesStore,
)
}
private fun aRoomDescription(
roomId: RoomId = A_ROOM_ID,
name: String? = A_ROOM_NAME,
@ -1095,3 +1062,36 @@ class JoinRoomPresenterTest {
)
}
}
internal fun createJoinRoomPresenter(
roomId: RoomId = A_ROOM_ID,
roomDescription: Optional<RoomDescription> = Optional.empty(),
serverNames: List<String> = emptyList(),
trigger: JoinedRoom.Trigger = JoinedRoom.Trigger.Invite,
matrixClient: MatrixClient = FakeMatrixClient(),
joinRoomLambda: (RoomIdOrAlias, List<String>, JoinedRoom.Trigger) -> Result<Unit> = { _, _, _ ->
Result.success(Unit)
},
knockRoom: KnockRoom = FakeKnockRoom(),
cancelKnockRoom: CancelKnockRoom = FakeCancelKnockRoom(),
forgetRoom: ForgetRoom = FakeForgetRoom(),
buildMeta: BuildMeta = aBuildMeta(applicationName = "AppName"),
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
): JoinRoomPresenter {
return JoinRoomPresenter(
roomId = roomId,
roomIdOrAlias = roomId.toRoomIdOrAlias(),
roomDescription = roomDescription,
serverNames = serverNames,
trigger = trigger,
matrixClient = matrixClient,
joinRoom = FakeJoinRoom(joinRoomLambda),
knockRoom = knockRoom,
cancelKnockRoom = cancelKnockRoom,
forgetRoom = forgetRoom,
buildMeta = buildMeta,
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
seenInvitesStore = seenInvitesStore,
)
}

View file

@ -0,0 +1,34 @@
/*
* 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.knockrequests.impl.list
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultKnockRequestsListEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultKnockRequestsListEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
KnockRequestsListNode(
buildContext = buildContext,
plugins = plugins,
presenter = createKnockRequestsListPresenter(),
)
}
val result = entryPoint.createNode(parentNode, BuildContext.root(null))
assertThat(result).isInstanceOf(KnockRequestsListNode::class.java)
}
}

View file

@ -286,19 +286,19 @@ class KnockRequestsListPresenterTest {
assert(acceptFailureLambda).isCalledOnce()
assert(acceptSuccessLambda).isCalledOnce()
}
private fun TestScope.createKnockRequestsListPresenter(
canAccept: Boolean = true,
canDecline: Boolean = true,
canBan: Boolean = true,
knockRequestsFlow: Flow<List<KnockRequest>> = flowOf(emptyList())
): KnockRequestsListPresenter {
val knockRequestsService = KnockRequestsService(
knockRequestsFlow = knockRequestsFlow,
coroutineScope = backgroundScope,
isKnockFeatureEnabledFlow = flowOf(true),
permissionsFlow = flowOf(KnockRequestPermissions(canAccept, canDecline, canBan)),
)
return KnockRequestsListPresenter(knockRequestsService = knockRequestsService)
}
}
internal fun TestScope.createKnockRequestsListPresenter(
canAccept: Boolean = true,
canDecline: Boolean = true,
canBan: Boolean = true,
knockRequestsFlow: Flow<List<KnockRequest>> = flowOf(emptyList())
): KnockRequestsListPresenter {
val knockRequestsService = KnockRequestsService(
knockRequestsFlow = knockRequestsFlow,
coroutineScope = backgroundScope,
isKnockFeatureEnabledFlow = flowOf(true),
permissionsFlow = flowOf(KnockRequestPermissions(canAccept, canDecline, canBan)),
)
return KnockRequestsListPresenter(knockRequestsService = knockRequestsService)
}

View file

@ -11,7 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.libraries.matrix.api.core.RoomId
interface LeaveRoomRenderer {
fun interface LeaveRoomRenderer {
@Composable
fun Render(
state: LeaveRoomState,

View file

@ -0,0 +1,31 @@
/*
* 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.licenses.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultOpenSourcesLicensesEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultOpenSourcesLicensesEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
DependenciesFlowNode(
buildContext = buildContext,
plugins = plugins,
)
}
val result = entryPoint.getNode(parentNode, BuildContext.root(null))
assertThat(result).isInstanceOf(DependenciesFlowNode::class.java)
}
}

View file

@ -10,7 +10,7 @@ package io.element.android.features.location.impl.common.permissions
import io.element.android.libraries.architecture.Presenter
interface PermissionsPresenter : Presenter<PermissionsState> {
interface Factory {
fun interface Factory {
fun create(permissions: List<String>): PermissionsPresenter
}
}

View file

@ -47,7 +47,7 @@ class SendLocationPresenter(
private val buildMeta: BuildMeta,
) : Presenter<SendLocationState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(timelineMode: Timeline.Mode): SendLocationPresenter
}

View file

@ -25,10 +25,10 @@ import io.element.android.services.analytics.api.AnalyticsService
@ContributesNode(RoomScope::class)
@Inject
class ShowLocationNode(
presenterFactory: ShowLocationPresenter.Factory,
analyticsService: AnalyticsService,
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: ShowLocationPresenter.Factory,
analyticsService: AnalyticsService,
) : Node(buildContext, plugins = plugins) {
init {
lifecycle.subscribe(

View file

@ -28,14 +28,14 @@ import io.element.android.libraries.core.meta.BuildMeta
@Inject
class ShowLocationPresenter(
@Assisted private val location: Location,
@Assisted private val description: String?,
permissionsPresenterFactory: PermissionsPresenter.Factory,
private val locationActions: LocationActions,
private val buildMeta: BuildMeta,
@Assisted private val location: Location,
@Assisted private val description: String?
) : Presenter<ShowLocationState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(location: Location, description: String?): ShowLocationPresenter
}

View file

@ -0,0 +1,53 @@
/*
* 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.location.impl.send
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultSendLocationEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultSendLocationEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
SendLocationNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { timelineMode: Timeline.Mode ->
SendLocationPresenter(
permissionsPresenterFactory = { FakePermissionsPresenter() },
room = FakeJoinedRoom(),
timelineMode = timelineMode,
analyticsService = FakeAnalyticsService(),
messageComposerContext = FakeMessageComposerContext(),
locationActions = FakeLocationActions(),
buildMeta = aBuildMeta(),
)
},
analyticsService = FakeAnalyticsService(),
)
}
val timelineMode = Timeline.Mode.Live
val result = entryPoint.builder(timelineMode)
.build(parentNode, BuildContext.root(null))
assertThat(result).isInstanceOf(SendLocationNode::class.java)
assertThat(result.plugins).contains(SendLocationNode.Inputs(timelineMode))
}
}

View file

@ -0,0 +1,56 @@
/*
* 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.location.impl.show
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.location.api.Location
import io.element.android.features.location.api.ShowLocationEntryPoint
import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultShowLocationEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultShowLocationEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ShowLocationNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { location: Location, description: String? ->
ShowLocationPresenter(
permissionsPresenterFactory = { FakePermissionsPresenter() },
locationActions = FakeLocationActions(),
buildMeta = aBuildMeta(),
location = location,
description = description,
)
},
analyticsService = FakeAnalyticsService(),
)
}
val inputs = ShowLocationEntryPoint.Inputs(
location = Location(37.4219983, -122.084, 10f),
description = "My location",
)
val result = entryPoint.createNode(
parentNode,
BuildContext.root(null),
inputs = inputs,
)
assertThat(result).isInstanceOf(ShowLocationNode::class.java)
assertThat(result.plugins).contains(inputs)
}
}

View file

@ -37,10 +37,10 @@ class ShowLocationPresenterTest {
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
},
fakeLocationActions,
fakeBuildMeta,
location,
A_DESCRIPTION,
locationActions = fakeLocationActions,
buildMeta = fakeBuildMeta,
location = location,
description = A_DESCRIPTION,
)
@Test

View file

@ -10,7 +10,7 @@ package io.element.android.features.location.test
import io.element.android.features.location.api.LocationService
class FakeLocationService(
private val isServiceAvailable: Boolean,
private val isServiceAvailable: Boolean = false,
) : LocationService {
override fun isServiceAvailable() = isServiceAvailable
}

View file

@ -0,0 +1,62 @@
/*
* 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.lockscreen.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultLockScreenEntryPointTest {
@Test
fun `test node builder Setup`() {
val entryPoint = DefaultLockScreenEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
LockScreenFlowNode(
buildContext = buildContext,
plugins = plugins,
)
}
val callback = object : LockScreenEntryPoint.Callback {
override fun onSetupDone() = lambdaError()
}
val navTarget = LockScreenEntryPoint.Target.Setup
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget)
.callback(callback)
.build()
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Setup))
assertThat(result.plugins).contains(callback)
}
@Test
fun `test node builder Settings`() {
val entryPoint = DefaultLockScreenEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
LockScreenFlowNode(
buildContext = buildContext,
plugins = plugins,
)
}
val callback = object : LockScreenEntryPoint.Callback {
override fun onSetupDone() = lambdaError()
}
val navTarget = LockScreenEntryPoint.Target.Settings
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget)
.callback(callback)
.build()
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Settings))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -0,0 +1,50 @@
/*
* 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.login.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultLoginEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultLoginEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
LoginFlowNode(
buildContext = buildContext,
plugins = plugins,
accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()),
oidcActionFlow = FakeOidcActionFlow(),
)
}
val callback = object : LoginEntryPoint.Callback {
override fun onReportProblem() = lambdaError()
}
val params = LoginEntryPoint.Params(
accountProvider = "ac",
loginHint = "lh",
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(LoginFlowNode::class.java)
assertThat(result.plugins).contains(LoginFlowNode.Params(params.accountProvider, params.loginHint))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -9,7 +9,7 @@ package io.element.android.features.logout.api.direct
import androidx.compose.runtime.Composable
interface DirectLogoutView {
fun interface DirectLogoutView {
@Composable
fun Render(state: DirectLogoutState)
}

View file

@ -0,0 +1,41 @@
/*
* 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.logout.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.logout.api.LogoutEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultLogoutEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultLogoutEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
LogoutNode(
buildContext = buildContext,
plugins = plugins,
presenter = createLogoutPresenter(),
)
}
val callback = object : LogoutEntryPoint.Callback {
override fun onChangeRecoveryKeyClick() = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(LogoutNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -225,12 +225,12 @@ class LogoutPresenterTest {
skipItems(2)
return awaitItem()
}
private fun createLogoutPresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
encryptionService: EncryptionService = FakeEncryptionService(),
): LogoutPresenter = LogoutPresenter(
matrixClient = matrixClient,
encryptionService = encryptionService,
)
}
internal fun createLogoutPresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
encryptionService: EncryptionService = FakeEncryptionService(),
): LogoutPresenter = LogoutPresenter(
matrixClient = matrixClient,
encryptionService = encryptionService,
)

View file

@ -0,0 +1,121 @@
/*
* 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.messages.impl
import androidx.compose.runtime.Composable
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
import io.element.android.features.location.api.SendLocationEntryPoint
import io.element.android.features.location.api.ShowLocationEntryPoint
import io.element.android.features.location.test.FakeLocationService
import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.messages.impl.pinned.banner.createPinnedEventsTimelineProvider
import io.element.android.features.messages.impl.timeline.createTimelineController
import io.element.android.features.poll.api.create.CreatePollEntryPoint
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
import io.element.android.libraries.matrix.ui.messages.RoomNamesCache
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.textcomposer.mentions.MentionSpanTheme
import io.element.android.libraries.textcomposer.mentions.MentionSpanUpdater
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultMessagesEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultMessagesEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
MessagesFlowNode(
buildContext = buildContext,
plugins = plugins,
matrixClient = FakeMatrixClient(),
sendLocationEntryPoint = object : SendLocationEntryPoint {
override fun builder(timelineMode: Timeline.Mode) = lambdaError()
},
showLocationEntryPoint = object : ShowLocationEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ShowLocationEntryPoint.Inputs) = lambdaError()
},
createPollEntryPoint = object : CreatePollEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
elementCallEntryPoint = object : ElementCallEntryPoint {
override fun startCall(callType: CallType) = lambdaError()
override suspend fun handleIncomingCall(
callType: CallType.RoomCall,
eventId: EventId,
senderId: UserId,
roomName: String?,
senderName: String?,
avatarUrl: String?,
timestamp: Long,
notificationChannelId: String,
textContent: String?,
) = lambdaError()
},
mediaViewerEntryPoint = object : MediaViewerEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
analyticsService = FakeAnalyticsService(),
locationService = FakeLocationService(),
room = FakeBaseRoom(),
roomMemberProfilesCache = RoomMemberProfilesCache(),
roomNamesCache = RoomNamesCache(),
mentionSpanUpdater = object : MentionSpanUpdater {
override fun updateMentionSpans(text: CharSequence) = text
@Composable
override fun rememberMentionSpans(text: CharSequence) = text
},
mentionSpanTheme = MentionSpanTheme(A_USER_ID),
pinnedEventsTimelineProvider = createPinnedEventsTimelineProvider(),
timelineController = createTimelineController(),
knockRequestsListEntryPoint = object : KnockRequestsListEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
dateFormatter = FakeDateFormatter(),
coroutineDispatchers = testCoroutineDispatchers(),
)
}
val callback = object : MessagesEntryPoint.Callback {
override fun onRoomDetailsClick() = lambdaError()
override fun onUserDataClick(userId: UserId) = lambdaError()
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError()
override fun onForwardedToSingleRoom(roomId: RoomId) = lambdaError()
}
val initialTarget = MessagesEntryPoint.InitialTarget.Messages(focusedEventId = AN_EVENT_ID)
val params = MessagesEntryPoint.Params(initialTarget)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(MessagesFlowNode::class.java)
assertThat(result.plugins).contains(MessagesEntryPoint.Params(initialTarget))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -178,10 +178,9 @@ class PinnedMessagesBannerPresenterTest {
),
syncService: SyncService = FakeSyncService(),
): PinnedMessagesBannerPresenter {
val timelineProvider = PinnedEventsTimelineProvider(
val timelineProvider = createPinnedEventsTimelineProvider(
room = room,
syncService = syncService,
dispatchers = testCoroutineDispatchers(),
)
timelineProvider.launchIn(backgroundScope)
@ -192,3 +191,12 @@ class PinnedMessagesBannerPresenterTest {
)
}
}
internal fun TestScope.createPinnedEventsTimelineProvider(
room: JoinedRoom = FakeJoinedRoom(),
syncService: SyncService = FakeSyncService(),
) = PinnedEventsTimelineProvider(
room = room,
syncService = syncService,
dispatchers = testCoroutineDispatchers(),
)

View file

@ -217,3 +217,13 @@ class TimelineControllerTest {
}
}
}
internal fun createTimelineController(
room: FakeJoinedRoom = FakeJoinedRoom(liveTimeline = FakeTimeline()),
liveTimeline: Timeline = FakeTimeline(name = "live"),
): TimelineController {
return TimelineController(
room = room,
liveTimeline = liveTimeline
)
}

View file

@ -47,7 +47,7 @@ class CreatePollPresenter(
@Assisted private val timelineMode: Timeline.Mode,
) : Presenter<CreatePollState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(
timelineMode: Timeline.Mode,
backNavigator: () -> Unit,

View file

@ -33,7 +33,7 @@ class PollRepository(
@Assisted private val timelineMode: Timeline.Mode,
) {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(
timelineMode: Timeline.Mode,
): PollRepository

View file

@ -0,0 +1,60 @@
/*
* 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.poll.impl.create
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.features.poll.api.create.CreatePollEntryPoint
import io.element.android.features.poll.api.create.CreatePollMode
import io.element.android.features.poll.impl.data.PollRepository
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultCreatePollEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultCreatePollEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
CreatePollNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { timelineMode: Timeline.Mode, backNavigator: () -> Unit, mode: CreatePollMode ->
CreatePollPresenter(
repositoryFactory = {
val room = FakeJoinedRoom()
PollRepository(room, LiveTimelineProvider(room), timelineMode)
},
analyticsService = FakeAnalyticsService(),
messageComposerContext = FakeMessageComposerContext(),
navigateUp = backNavigator,
mode = mode,
timelineMode = timelineMode,
)
},
analyticsService = FakeAnalyticsService(),
)
}
val params = CreatePollEntryPoint.Params(
timelineMode = Timeline.Mode.Live,
mode = CreatePollMode.NewPoll,
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.build()
assertThat(result).isInstanceOf(CreatePollNode::class.java)
assertThat(result.plugins).contains(CreatePollNode.Inputs(timelineMode = params.timelineMode, mode = params.mode))
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.poll.impl.history
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.poll.api.create.CreatePollEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultPollHistoryEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultPollHistoryEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
PollHistoryFlowNode(
buildContext = buildContext,
plugins = plugins,
createPollEntryPoint = object : CreatePollEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
}
)
}
val result = entryPoint.createNode(parentNode, BuildContext.root(null))
assertThat(result).isInstanceOf(PollHistoryFlowNode::class.java)
}
}

View file

@ -151,23 +151,23 @@ class PollHistoryPresenterTest {
assert(paginateLambda).isCalledExactly(2)
}
}
private fun TestScope.createPollHistoryPresenter(
room: FakeJoinedRoom = FakeJoinedRoom(),
endPollAction: EndPollAction = FakeEndPollAction(),
sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(),
pollHistoryItemFactory: PollHistoryItemsFactory = PollHistoryItemsFactory(
pollContentStateFactory = DefaultPollContentStateFactory(FakeMatrixClient()),
dateFormatter = FakeDateFormatter(),
dispatchers = testCoroutineDispatchers(),
),
): PollHistoryPresenter {
return PollHistoryPresenter(
sessionCoroutineScope = this,
sendPollResponseAction = sendPollResponseAction,
endPollAction = endPollAction,
pollHistoryItemFactory = pollHistoryItemFactory,
room = room,
)
}
}
internal fun TestScope.createPollHistoryPresenter(
room: FakeJoinedRoom = FakeJoinedRoom(),
endPollAction: EndPollAction = FakeEndPollAction(),
sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(),
pollHistoryItemFactory: PollHistoryItemsFactory = PollHistoryItemsFactory(
pollContentStateFactory = DefaultPollContentStateFactory(FakeMatrixClient()),
dateFormatter = FakeDateFormatter(),
dispatchers = testCoroutineDispatchers(),
),
): PollHistoryPresenter {
return PollHistoryPresenter(
sessionCoroutineScope = this,
sendPollResponseAction = sendPollResponseAction,
endPollAction = endPollAction,
pollHistoryItemFactory = pollHistoryItemFactory,
room = room,
)
}

View file

@ -0,0 +1,77 @@
/*
* 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.preferences.impl
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.deactivation.api.AccountDeactivationEntryPoint
import io.element.android.features.licenses.api.OpenSourceLicensesEntryPoint
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.logout.api.LogoutEntryPoint
import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultPreferencesEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultPreferencesEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
PreferencesFlowNode(
buildContext = buildContext,
plugins = plugins,
lockScreenEntryPoint = object : LockScreenEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: LockScreenEntryPoint.Target) = lambdaError()
override fun pinUnlockIntent(context: Context) = lambdaError()
},
notificationTroubleShootEntryPoint = object : NotificationTroubleShootEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
pushHistoryEntryPoint = object : PushHistoryEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
logoutEntryPoint = object : LogoutEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
openSourceLicensesEntryPoint = object : OpenSourceLicensesEntryPoint {
override fun getNode(node: Node, buildContext: BuildContext) = lambdaError()
},
accountDeactivationEntryPoint = object : AccountDeactivationEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
)
}
val callback = object : PreferencesEntryPoint.Callback {
override fun onOpenBugReport() = lambdaError()
override fun onSecureBackupClick() = lambdaError()
override fun onOpenRoomNotificationSettings(roomId: RoomId) = lambdaError()
override fun navigateTo(sessionId: SessionId, roomId: RoomId, eventId: EventId) = lambdaError()
}
val params = PreferencesEntryPoint.Params(
initialElement = PreferencesEntryPoint.InitialTarget.NotificationSettings,
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(PreferencesFlowNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -225,15 +225,15 @@ class BugReportPresenterTest {
assertThat(awaitItem().sending).isEqualTo(AsyncAction.Uninitialized)
}
}
private fun TestScope.createPresenter(
bugReporter: BugReporter = FakeBugReporter(),
crashDataStore: CrashDataStore = FakeCrashDataStore(),
screenshotHolder: ScreenshotHolder = FakeScreenshotHolder(),
) = BugReportPresenter(
bugReporter = bugReporter,
crashDataStore = crashDataStore,
screenshotHolder = screenshotHolder,
appCoroutineScope = this,
)
}
internal fun TestScope.createPresenter(
bugReporter: BugReporter = FakeBugReporter(),
crashDataStore: CrashDataStore = FakeCrashDataStore(),
screenshotHolder: ScreenshotHolder = FakeScreenshotHolder(),
) = BugReportPresenter(
bugReporter = bugReporter,
crashDataStore = crashDataStore,
screenshotHolder = screenshotHolder,
appCoroutineScope = this,
)

View file

@ -0,0 +1,43 @@
/*
* 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.rageshake.impl.bugreport
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultBugReportEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultBugReportEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
BugReportNode(
buildContext = buildContext,
plugins = plugins,
presenter = createPresenter(),
bugReporter = FakeBugReporter(),
)
}
val callback = object : BugReportEntryPoint.Callback {
override fun onBugReportSent() = lambdaError()
override fun onViewLogs(basePath: String) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(BugReportNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -12,6 +12,6 @@ import com.bumble.appyx.core.node.Node
import io.element.android.libraries.architecture.FeatureEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
interface ReportRoomEntryPoint : FeatureEntryPoint {
fun interface ReportRoomEntryPoint : FeatureEntryPoint {
fun createNode(parentNode: Node, buildContext: BuildContext, roomId: RoomId): Node
}

View file

@ -31,7 +31,7 @@ class ReportRoomPresenter(
private val reportRoom: ReportRoom,
) : Presenter<ReportRoomState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(roomId: RoomId): ReportRoomPresenter
}

View file

@ -0,0 +1,37 @@
/*
* 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.reportroom.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultReportRoomEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultReportRoomEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ReportRoomNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { roomId ->
assertThat(roomId).isEqualTo(A_ROOM_ID)
createReportRoomPresenter()
}
)
}
val result = entryPoint.createNode(parentNode, BuildContext.root(null), A_ROOM_ID)
assertThat(result).isInstanceOf(ReportRoomNode::class.java)
assertThat(result.plugins).contains(ReportRoomNode.Inputs(A_ROOM_ID))
}
}

View file

@ -141,11 +141,11 @@ class ReportRoomPresenterTest {
)
}
}
fun createReportRoomPresenter(
roomId: RoomId = A_ROOM_ID,
reportRoom: ReportRoom = FakeReportRoom()
): ReportRoomPresenter {
return ReportRoomPresenter(roomId, reportRoom)
}
}
internal fun createReportRoomPresenter(
roomId: RoomId = A_ROOM_ID,
reportRoom: ReportRoom = FakeReportRoom()
): ReportRoomPresenter {
return ReportRoomPresenter(roomId, reportRoom)
}

View file

@ -30,7 +30,7 @@ class RoomAliasResolverPresenter(
@Assisted private val roomAlias: RoomAlias,
private val matrixClient: MatrixClient,
) : Presenter<RoomAliasResolverState> {
interface Factory {
fun interface Factory {
fun create(
roomAlias: RoomAlias,
): RoomAliasResolverPresenter

View file

@ -0,0 +1,53 @@
/*
* 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.roomaliasresolver.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultRoomAliasResolverEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultRoomAliasResolverEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
RoomAliasResolverNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { alias ->
assertThat(alias).isEqualTo(A_ROOM_ALIAS)
createPresenter(
alias,
)
}
)
}
val callback = object : RoomAliasResolverEntryPoint.Callback {
override fun onAliasResolved(data: ResolvedRoomAlias) = lambdaError()
}
val params = RoomAliasResolverEntryPoint.Params(
roomAlias = A_ROOM_ALIAS
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(RoomAliasResolverNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -79,16 +79,16 @@ class RoomAliasHelperPresenterTest {
assertThat(retryState.resolveState.errorOrNull()).isEqualTo(AN_EXCEPTION)
}
}
private fun createPresenter(
roomAlias: RoomAlias = A_ROOM_ALIAS,
matrixClient: MatrixClient = FakeMatrixClient(),
) = RoomAliasResolverPresenter(
roomAlias = roomAlias,
matrixClient = matrixClient,
)
}
internal fun createPresenter(
roomAlias: RoomAlias = A_ROOM_ALIAS,
matrixClient: MatrixClient = FakeMatrixClient(),
) = RoomAliasResolverPresenter(
roomAlias = roomAlias,
matrixClient = matrixClient,
)
internal fun aResolvedRoomAlias(
roomId: RoomId = A_ROOM_ID,
servers: List<String> = A_SERVER_LIST,

View file

@ -0,0 +1,105 @@
/*
* 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.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.poll.api.history.PollHistoryEntryPoint
import io.element.android.features.reportroom.api.ReportRoomEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultRoomDetailsEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultRoomDetailsEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
RoomDetailsFlowNode(
buildContext = buildContext,
plugins = plugins,
pollHistoryEntryPoint = object : PollHistoryEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
elementCallEntryPoint = object : ElementCallEntryPoint {
override fun startCall(callType: CallType) = lambdaError()
override suspend fun handleIncomingCall(
callType: CallType.RoomCall,
eventId: EventId,
senderId: UserId,
roomName: String?,
senderName: String?,
avatarUrl: String?,
timestamp: Long,
notificationChannelId: String,
textContent: String?
) = lambdaError()
},
room = FakeJoinedRoom(),
analyticsService = FakeAnalyticsService(),
messagesEntryPoint = object : MessagesEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
knockRequestsListEntryPoint = object : KnockRequestsListEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
mediaViewerEntryPoint = object : MediaViewerEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
mediaGalleryEntryPoint = object : MediaGalleryEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
outgoingVerificationEntryPoint = object : OutgoingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
reportRoomEntryPoint = object : ReportRoomEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext, roomId: RoomId) = lambdaError()
},
changeRoomMemberRolesEntryPoint = object : ChangeRoomMemberRolesEntryPoint {
override fun builder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
)
}
val callback = object : RoomDetailsEntryPoint.Callback {
override fun onOpenGlobalNotificationSettings() = lambdaError()
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) = lambdaError()
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError()
override fun onForwardedToSingleRoom(roomId: RoomId) = lambdaError()
}
val params = RoomDetailsEntryPoint.Params(
initialElement = RoomDetailsEntryPoint.InitialTarget.RoomDetails,
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(RoomDetailsFlowNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -0,0 +1,44 @@
/*
* 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.roomdirectory.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomdirectory.api.RoomDescription
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
import io.element.android.features.roomdirectory.impl.root.RoomDirectoryNode
import io.element.android.features.roomdirectory.impl.root.createRoomDirectoryPresenter
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultRoomDirectoryEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultRoomDirectoryEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
RoomDirectoryNode(
buildContext = buildContext,
plugins = plugins,
presenter = createRoomDirectoryPresenter(),
)
}
val callback = object : RoomDirectoryEntryPoint.Callback {
override fun onResultClick(roomDescription: RoomDescription) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(RoomDirectoryNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -122,15 +122,15 @@ class RoomDirectoryPresenterTest {
.isCalledOnce()
.withNoParameter()
}
private fun TestScope.createRoomDirectoryPresenter(
roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(
createRoomDirectoryListFactory = { FakeRoomDirectoryList() }
),
): RoomDirectoryPresenter {
return RoomDirectoryPresenter(
dispatchers = testCoroutineDispatchers(),
roomDirectoryService = roomDirectoryService,
)
}
}
internal fun TestScope.createRoomDirectoryPresenter(
roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(
createRoomDirectoryListFactory = { FakeRoomDirectoryList() }
),
): RoomDirectoryPresenter {
return RoomDirectoryPresenter(
dispatchers = testCoroutineDispatchers(),
roomDirectoryService = roomDirectoryService,
)
}

View file

@ -0,0 +1,42 @@
/*
* 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.securebackup.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultSecureBackupEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultSecureBackupEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
SecureBackupFlowNode(
buildContext = buildContext,
plugins = plugins,
)
}
val callback = object : SecureBackupEntryPoint.Callback {
override fun onDone() = lambdaError()
}
val params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(SecureBackupFlowNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -43,7 +43,7 @@ class SharePresenter(
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
) : Presenter<ShareState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(intent: Intent): SharePresenter
}

View file

@ -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.share.impl
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.share.api.ShareEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultShareEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultShareEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ShareNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { createSharePresenter() },
roomSelectEntryPoint = object : RoomSelectEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder {
lambdaError()
}
},
)
}
val callback = object : ShareEntryPoint.Callback {
override fun onDone(roomIds: List<RoomId>) = lambdaError()
}
val params = ShareEntryPoint.Params(
intent = Intent(),
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(ShareNode::class.java)
assertThat(result.plugins).contains(ShareNode.Inputs(params.intent))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -158,23 +158,23 @@ class SharePresenterTest {
sendFileResult.assertions().isCalledOnce()
}
}
private fun TestScope.createSharePresenter(
intent: Intent = Intent(),
shareIntentHandler: ShareIntentHandler = FakeShareIntentHandler(),
matrixClient: MatrixClient = FakeMatrixClient(),
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
activeRoomsHolder: ActiveRoomsHolder = ActiveRoomsHolder(),
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
): SharePresenter {
return SharePresenter(
intent = intent,
sessionCoroutineScope = this,
shareIntentHandler = shareIntentHandler,
matrixClient = matrixClient,
mediaPreProcessor = mediaPreProcessor,
activeRoomsHolder = activeRoomsHolder,
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
)
}
}
internal fun TestScope.createSharePresenter(
intent: Intent = Intent(),
shareIntentHandler: ShareIntentHandler = FakeShareIntentHandler(),
matrixClient: MatrixClient = FakeMatrixClient(),
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
activeRoomsHolder: ActiveRoomsHolder = ActiveRoomsHolder(),
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
): SharePresenter {
return SharePresenter(
intent = intent,
sessionCoroutineScope = this,
shareIntentHandler = shareIntentHandler,
matrixClient = matrixClient,
mediaPreProcessor = mediaPreProcessor,
activeRoomsHolder = activeRoomsHolder,
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
)
}

View file

@ -29,7 +29,7 @@ class SignedOutPresenter(
private val buildMeta: BuildMeta,
) : Presenter<SignedOutState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(sessionId: String): SignedOutPresenter
}

View file

@ -0,0 +1,41 @@
/*
* 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.signedout.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.signedout.api.SignedOutEntryPoint
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultSignedOutEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultSignedOutEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
SignedOutNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { sessionId ->
assertThat(sessionId).isEqualTo(A_SESSION_ID.value)
createSignedOutPresenter()
}
)
}
val params = SignedOutEntryPoint.Params(A_SESSION_ID)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.build()
assertThat(result).isInstanceOf(SignedOutNode::class.java)
assertThat(result.plugins).contains(SignedOutNode.Inputs(params.sessionId))
}
}

View file

@ -22,12 +22,12 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
private val appName = "AppName"
class SignedOutPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
private val appName = "AppName"
@Test
fun `present - initial state`() = runTest {
val aSessionData = aSessionData()
@ -64,15 +64,15 @@ class SignedOutPresenterTest {
assertThat(sessionStore.getAllSessions()).isEmpty()
}
}
private fun createSignedOutPresenter(
sessionId: SessionId = A_SESSION_ID,
sessionStore: SessionStore = InMemorySessionStore(),
): SignedOutPresenter {
return SignedOutPresenter(
sessionId = sessionId.value,
sessionStore = sessionStore,
buildMeta = aBuildMeta(applicationName = appName),
)
}
}
internal fun createSignedOutPresenter(
sessionId: SessionId = A_SESSION_ID,
sessionStore: SessionStore = InMemorySessionStore(),
): SignedOutPresenter {
return SignedOutPresenter(
sessionId = sessionId.value,
sessionStore = sessionStore,
buildMeta = aBuildMeta(applicationName = appName),
)
}

View file

@ -25,7 +25,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultSpaceEntryPointTest {
@Test
fun `test DefaultSpaceEntryPoint`() {
fun `test node builder`() {
val entryPoint = DefaultSpaceEntryPoint()
val nodeInputs = SpaceEntryPoint.Inputs(A_ROOM_ID)
val parentNode = TestParentNode.create { buildContext, plugins ->

View file

@ -0,0 +1,47 @@
/*
* 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.startchat.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.createroom.api.CreateRoomEntryPoint
import io.element.android.features.startchat.api.StartChatEntryPoint
import io.element.android.features.startchat.impl.root.StartChatNode
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultStartChatEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultStartChatEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
StartChatFlowNode(
buildContext = buildContext,
plugins = plugins,
createRoomEntryPoint = object : CreateRoomEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
)
}
val callback = object : StartChatEntryPoint.Callback {
override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) = lambdaError()
override fun onOpenRoomDirectory() = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(StartChatFlowNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -177,23 +177,23 @@ class StartChatPresenterTest {
}
}
}
private fun createStartChatPresenter(
startDMAction: StartDMAction = FakeStartDMAction(),
isRoomDirectorySearchEnabled: Boolean = false,
): StartChatPresenter {
val featureFlagService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.RoomDirectorySearch.key to isRoomDirectorySearchEnabled,
),
)
return StartChatPresenter(
presenterFactory = FakeUserListPresenterFactory(FakeUserListPresenter()),
userRepository = FakeUserRepository(),
userListDataStore = UserListDataStore(),
startDMAction = startDMAction,
featureFlagService = featureFlagService,
buildMeta = aBuildMeta(),
)
}
}
internal fun createStartChatPresenter(
startDMAction: StartDMAction = FakeStartDMAction(),
isRoomDirectorySearchEnabled: Boolean = false,
): StartChatPresenter {
val featureFlagService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.RoomDirectorySearch.key to isRoomDirectorySearchEnabled,
),
)
return StartChatPresenter(
presenterFactory = FakeUserListPresenterFactory(FakeUserListPresenter()),
userRepository = FakeUserRepository(),
userListDataStore = UserListDataStore(),
startDMAction = startDMAction,
featureFlagService = featureFlagService,
buildMeta = aBuildMeta(),
)
}

View file

@ -0,0 +1,79 @@
/*
* 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.userprofile.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.userprofile.api.UserProfileEntryPoint
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultUserProfileEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultUserProfileEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
UserProfileFlowNode(
buildContext = buildContext,
plugins = plugins,
sessionIdHolder = CurrentSessionIdHolder(FakeMatrixClient()),
elementCallEntryPoint = object : ElementCallEntryPoint {
override fun startCall(callType: CallType) = lambdaError()
override suspend fun handleIncomingCall(
callType: CallType.RoomCall,
eventId: EventId,
senderId: UserId,
roomName: String?,
senderName: String?,
avatarUrl: String?,
timestamp: Long,
notificationChannelId: String,
textContent: String?
) = lambdaError()
},
mediaViewerEntryPoint = object : MediaViewerEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
outgoingVerificationEntryPoint = object : OutgoingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
},
)
}
val callback = object : UserProfileEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId) {
lambdaError()
}
}
val params = UserProfileEntryPoint.Params(
userId = A_USER_ID,
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(UserProfileFlowNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -48,7 +48,7 @@ class IncomingVerificationPresenter(
private val dateFormatter: DateFormatter,
) : Presenter<IncomingVerificationState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(
verificationRequest: VerificationRequest.Incoming,
navigator: IncomingVerificationNavigator,

View file

@ -42,7 +42,7 @@ class OutgoingVerificationPresenter(
private val encryptionService: EncryptionService,
) : Presenter<OutgoingVerificationState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(
verificationRequest: VerificationRequest.Outgoing,
showDeviceVerifiedScreen: Boolean,

View file

@ -0,0 +1,46 @@
/*
* 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.verifysession.impl.incoming
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultIncomingVerificationEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultIncomingVerificationEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
IncomingVerificationNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { _, _ -> createPresenter() }
)
}
val callback = object : IncomingVerificationEntryPoint.Callback {
override fun onDone() = lambdaError()
}
val params = IncomingVerificationEntryPoint.Params(
verificationRequest = anIncomingSessionVerificationRequest()
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(IncomingVerificationNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -289,31 +289,31 @@ class IncomingVerificationPresenterTest {
navigatorLambda.assertions().isCalledOnce()
}
}
private val anIncomingSessionVerificationRequest = VerificationRequest.Incoming.OtherSession(
details = SessionVerificationRequestDetails(
senderProfile = SessionVerificationRequestDetails.SenderProfile(
userId = A_USER_ID,
displayName = "a device name",
avatarUrl = null,
),
flowId = FlowId("flowId"),
deviceId = A_DEVICE_ID,
firstSeenTimestamp = A_TIMESTAMP,
)
)
private fun TestScope.createPresenter(
verificationRequest: VerificationRequest.Incoming = anIncomingSessionVerificationRequest,
navigator: IncomingVerificationNavigator = IncomingVerificationNavigator { lambdaError() },
service: SessionVerificationService = FakeSessionVerificationService(),
dateFormatter: DateFormatter = FakeDateFormatter(),
) = IncomingVerificationPresenter(
verificationRequest = verificationRequest,
navigator = navigator,
sessionVerificationService = service,
stateMachine = IncomingVerificationStateMachine(service),
dateFormatter = dateFormatter,
sessionCoroutineScope = backgroundScope,
)
}
private val anIncomingSessionVerificationRequest = VerificationRequest.Incoming.OtherSession(
details = SessionVerificationRequestDetails(
senderProfile = SessionVerificationRequestDetails.SenderProfile(
userId = A_USER_ID,
displayName = "a device name",
avatarUrl = null,
),
flowId = FlowId("flowId"),
deviceId = A_DEVICE_ID,
firstSeenTimestamp = A_TIMESTAMP,
)
)
internal fun TestScope.createPresenter(
verificationRequest: VerificationRequest.Incoming = anIncomingSessionVerificationRequest,
navigator: IncomingVerificationNavigator = IncomingVerificationNavigator { lambdaError() },
service: SessionVerificationService = FakeSessionVerificationService(),
dateFormatter: DateFormatter = FakeDateFormatter(),
) = IncomingVerificationPresenter(
verificationRequest = verificationRequest,
navigator = navigator,
sessionVerificationService = service,
stateMachine = IncomingVerificationStateMachine(service),
dateFormatter = dateFormatter,
sessionCoroutineScope = backgroundScope,
)

View file

@ -0,0 +1,51 @@
/*
* 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.verifysession.impl.outgoing
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultOutgoingVerificationEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultOutgoingVerificationEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
OutgoingVerificationNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { _, _ ->
createOutgoingVerificationPresenter()
}
)
}
val callback = object : OutgoingVerificationEntryPoint.Callback {
override fun onLearnMoreAboutEncryption() = lambdaError()
override fun onBack() = lambdaError()
override fun onDone() = lambdaError()
}
val params = OutgoingVerificationEntryPoint.Params(
showDeviceVerifiedScreen = true,
verificationRequest = anOutgoingSessionVerificationRequest(),
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(OutgoingVerificationNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -321,18 +321,18 @@ class OutgoingVerificationPresenterTest {
emitVerifiedStatus(SessionVerifiedStatus.NotVerified)
}
}
private fun createOutgoingVerificationPresenter(
service: SessionVerificationService,
verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
encryptionService: EncryptionService = FakeEncryptionService(),
showDeviceVerifiedScreen: Boolean = false,
): OutgoingVerificationPresenter {
return OutgoingVerificationPresenter(
showDeviceVerifiedScreen = showDeviceVerifiedScreen,
verificationRequest = verificationRequest,
sessionVerificationService = service,
encryptionService = encryptionService,
)
}
}
internal fun createOutgoingVerificationPresenter(
service: SessionVerificationService = FakeSessionVerificationService(),
verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
encryptionService: EncryptionService = FakeEncryptionService(),
showDeviceVerifiedScreen: Boolean = false,
): OutgoingVerificationPresenter {
return OutgoingVerificationPresenter(
showDeviceVerifiedScreen = showDeviceVerifiedScreen,
verificationRequest = verificationRequest,
sessionVerificationService = service,
encryptionService = encryptionService,
)
}

View file

@ -0,0 +1,45 @@
/*
* 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.viewfolder.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.viewfolder.api.ViewFolderEntryPoint
import io.element.android.features.viewfolder.impl.root.ViewFolderRootNode
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultViewFolderEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultViewFolderEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ViewFolderRootNode(
buildContext = buildContext,
plugins = plugins,
)
}
val callback = object : ViewFolderEntryPoint.Callback {
override fun onDone() = lambdaError()
}
val params = ViewFolderEntryPoint.Params(
rootPath = "path",
)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(ViewFolderRootNode::class.java)
assertThat(result.plugins).contains(ViewFolderRootNode.Inputs(params.rootPath))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -18,6 +18,6 @@ interface FeatureEntryPoint
/**
* Can be used when the feature only exposes a simple node without the need of plugins.
*/
interface SimpleFeatureEntryPoint : FeatureEntryPoint {
fun interface SimpleFeatureEntryPoint : FeatureEntryPoint {
fun createNode(parentNode: Node, buildContext: BuildContext): Node
}

View file

@ -9,6 +9,6 @@ package io.element.android.libraries.deeplink.api.usecase
import android.app.Activity
interface InviteFriendsUseCase {
fun interface InviteFriendsUseCase {
fun execute(activity: Activity)
}

View file

@ -54,6 +54,7 @@ dependencies {
implementation(projects.libraries.matrix.api)
testCommonDependencies(libs, true)
testImplementation(projects.libraries.audio.test)
testImplementation(projects.libraries.dateformatter.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.matrix.test)

View file

@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
interface FocusedTimelineMediaGalleryDataSourceFactory {
fun interface FocusedTimelineMediaGalleryDataSourceFactory {
fun createFor(
eventId: EventId,
mediaItem: MediaItem.Event,

View file

@ -58,7 +58,7 @@ class MediaViewerPresenter(
private val localMediaActions: LocalMediaActions,
) : Presenter<MediaViewerState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(
inputs: MediaViewerEntryPoint.Params,
navigator: MediaViewerNavigator,

View file

@ -0,0 +1,47 @@
/*
* 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.libraries.mediaviewer.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.mediaviewer.impl.gallery.root.MediaGalleryRootNode
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultMediaGalleryEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultMediaGalleryEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
MediaGalleryRootNode(
buildContext = buildContext,
plugins = plugins,
mediaViewerEntryPoint = object : MediaViewerEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError()
}
)
}
val callback = object : MediaGalleryEntryPoint.Callback {
override fun onBackClick() = lambdaError()
override fun onViewInTimeline(eventId: EventId) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(MediaGalleryRootNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -0,0 +1,145 @@
/*
* 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.libraries.mediaviewer.impl
import android.net.Uri
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader
import io.element.android.libraries.mediaplayer.test.FakeAudioFocus
import io.element.android.libraries.mediaviewer.api.MediaInfo
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.mediaviewer.impl.datasource.createTimelineMediaGalleryDataSource
import io.element.android.libraries.mediaviewer.impl.viewer.MediaViewerNode
import io.element.android.libraries.mediaviewer.impl.viewer.PagerKeysHandler
import io.element.android.libraries.mediaviewer.impl.viewer.createMediaViewerEntryPointParams
import io.element.android.libraries.mediaviewer.impl.viewer.createMediaViewerPresenter
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import io.element.android.tests.testutils.testCoroutineDispatchers
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultMediaViewerEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultMediaViewerEntryPoint()
val mockMediaUri: Uri = mockk("localMediaUri")
val localMediaFactory = FakeLocalMediaFactory(mockMediaUri)
val parentNode = TestParentNode.create { buildContext, plugins ->
MediaViewerNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { _, _, _ ->
createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
)
},
timelineMediaGalleryDataSource = createTimelineMediaGalleryDataSource(),
focusedTimelineMediaGalleryDataSourceFactory = { _, _, _ ->
lambdaError()
},
mediaLoader = FakeMatrixMediaLoader(),
localMediaFactory = FakeLocalMediaFactory(mockMediaUri),
coroutineDispatchers = testCoroutineDispatchers(),
systemClock = FakeSystemClock(),
pagerKeysHandler = PagerKeysHandler(),
textFileViewer = { _, _ -> lambdaError() },
audioFocus = FakeAudioFocus(),
)
}
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun onViewInTimeline(eventId: EventId) = lambdaError()
}
val params = createMediaViewerEntryPointParams()
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(MediaViewerNode::class.java)
assertThat(result.plugins).contains(params)
assertThat(result.plugins).contains(callback)
}
@Test
fun `test node builder avatar`() = runTest {
val entryPoint = DefaultMediaViewerEntryPoint()
val mockMediaUri: Uri = mockk("localMediaUri")
val localMediaFactory = FakeLocalMediaFactory(mockMediaUri)
val parentNode = TestParentNode.create { buildContext, plugins ->
MediaViewerNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { _, _, _ ->
createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
)
},
timelineMediaGalleryDataSource = createTimelineMediaGalleryDataSource(),
focusedTimelineMediaGalleryDataSourceFactory = { _, _, _ ->
lambdaError()
},
mediaLoader = FakeMatrixMediaLoader(),
localMediaFactory = FakeLocalMediaFactory(mockMediaUri),
coroutineDispatchers = testCoroutineDispatchers(),
systemClock = FakeSystemClock(),
pagerKeysHandler = PagerKeysHandler(),
textFileViewer = { _, _ -> lambdaError() },
audioFocus = FakeAudioFocus(),
)
}
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun onViewInTimeline(eventId: EventId) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.avatar(
filename = "fn",
avatarUrl = "avatarUrl",
)
.callback(callback)
.build()
assertThat(result).isInstanceOf(MediaViewerNode::class.java)
assertThat(result.plugins).contains(
MediaViewerEntryPoint.Params(
mode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia,
eventId = null,
mediaInfo = MediaInfo(
filename = "fn",
fileSize = null,
caption = null,
mimeType = MimeTypes.Images,
formattedFileSize = "",
fileExtension = "",
senderId = UserId("@dummy:server.org"),
senderName = null,
senderAvatar = null,
dateSent = null,
dateSentFull = null,
waveform = null,
duration = null,
),
mediaSource = MediaSource(url = "avatarUrl"),
thumbnailSource = null,
canShowInfo = false,
)
)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -255,19 +255,19 @@ class TimelineMediaGalleryDataSourceTest {
)
}
}
}
private fun TestScope.createTimelineMediaGalleryDataSource(
room: JoinedRoom = FakeJoinedRoom(
liveTimeline = FakeTimeline(),
),
): TimelineMediaGalleryDataSource {
return TimelineMediaGalleryDataSource(
room = room,
mediaTimeline = LiveMediaTimeline(room),
timelineMediaItemsFactory = createTimelineMediaItemsFactory(),
mediaItemsPostProcessor = MediaItemsPostProcessor(),
)
}
internal fun TestScope.createTimelineMediaGalleryDataSource(
room: JoinedRoom = FakeJoinedRoom(
liveTimeline = FakeTimeline(),
),
): TimelineMediaGalleryDataSource {
return TimelineMediaGalleryDataSource(
room = room,
mediaTimeline = LiveMediaTimeline(room),
timelineMediaItemsFactory = createTimelineMediaItemsFactory(),
mediaItemsPostProcessor = MediaItemsPostProcessor(),
)
}
fun TestScope.createTimelineMediaItemsFactory() = TimelineMediaItemsFactory(

View file

@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.mediaviewer.api.anApkMediaInfo
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
import io.element.android.libraries.mediaviewer.impl.R
import io.element.android.libraries.mediaviewer.impl.datasource.FakeMediaGalleryDataSource
import io.element.android.libraries.mediaviewer.impl.datasource.MediaGalleryDataSource
@ -78,12 +79,13 @@ class MediaViewerPresenterTest {
@Test
fun `present - initial state null Event`() = runTest {
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canRedactOwnResult = { Result.success(true) },
canRedactOwnResult = { Result.success(true) },
)
)
)
)
presenter.test {
val initialState = awaitFirstItem()
assertThat(initialState.listData).isEmpty()
@ -97,13 +99,14 @@ class MediaViewerPresenterTest {
@Test
fun `present - initial state cannot show info`() = runTest {
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
canShowInfo = false,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canRedactOwnResult = { Result.success(true) },
canRedactOwnResult = { Result.success(true) },
)
)
)
)
presenter.test {
val initialState = awaitFirstItem()
assertThat(initialState.listData).isEmpty()
@ -117,13 +120,14 @@ class MediaViewerPresenterTest {
@Test
fun `present - initial state Event`() = runTest {
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
eventId = AN_EVENT_ID,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canRedactOwnResult = { Result.success(true) },
canRedactOwnResult = { Result.success(true) },
)
)
)
)
presenter.test {
val initialState = awaitFirstItem()
assertThat(initialState.listData).isEmpty()
@ -137,14 +141,15 @@ class MediaViewerPresenterTest {
@Test
fun `present - initial state Event from other`() = runTest {
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
eventId = AN_EVENT_ID,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
sessionId = A_SESSION_ID_2,
canRedactOtherResult = { Result.success(false) },
sessionId = A_SESSION_ID_2,
canRedactOtherResult = { Result.success(false) },
)
)
)
)
presenter.test {
val initialState = awaitFirstItem()
assertThat(initialState.listData).isEmpty()
@ -161,6 +166,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage()
@ -192,6 +198,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -224,10 +231,13 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
room = FakeJoinedRoom(baseRoom = FakeBaseRoom(
canRedactOwnResult = { Result.success(true) },
))
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canRedactOwnResult = { Result.success(true) },
)
)
)
val anImage = aMediaItemImage(
mediaSourceUrl = aUrl,
@ -266,6 +276,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -298,6 +309,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -330,6 +342,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -362,6 +375,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -394,6 +408,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -441,6 +456,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
room = FakeJoinedRoom(
liveTimeline = timeline,
baseRoom = FakeBaseRoom(canRedactOwnResult = { Result.success(true) }),
@ -498,6 +514,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -549,6 +566,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mode = mode,
mediaGalleryDataSource = mediaGalleryDataSource,
)
@ -620,6 +638,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mode = mode,
mediaGalleryDataSource = mediaGalleryDataSource,
)
@ -674,6 +693,7 @@ class MediaViewerPresenterTest {
startLambda = { },
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
presenter.test {
@ -714,6 +734,7 @@ class MediaViewerPresenterTest {
loadMoreLambda = loadMoreLambda,
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaGalleryDataSource = mediaGalleryDataSource,
)
val anImage = aMediaItemImage(
@ -744,6 +765,7 @@ class MediaViewerPresenterTest {
onViewInTimelineClickLambda = onViewInTimelineClickLambda,
)
val presenter = createMediaViewerPresenter(
localMediaFactory = localMediaFactory,
mediaViewerNavigator = navigator,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(canRedactOwnResult = { Result.success(true) }),
@ -764,42 +786,53 @@ class MediaViewerPresenterTest {
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
return awaitItem()
}
private fun TestScope.createMediaViewerPresenter(
eventId: EventId? = null,
mode: MediaViewerEntryPoint.MediaViewerMode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia,
matrixMediaLoader: FakeMatrixMediaLoader = FakeMatrixMediaLoader(),
localMediaActions: FakeLocalMediaActions = FakeLocalMediaActions(),
mediaGalleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(
startLambda = { },
),
canShowInfo: Boolean = true,
mediaViewerNavigator: MediaViewerNavigator = FakeMediaViewerNavigator(),
room: JoinedRoom = FakeJoinedRoom(
liveTimeline = FakeTimeline(),
),
): MediaViewerPresenter {
return MediaViewerPresenter(
inputs = MediaViewerEntryPoint.Params(
mode = mode,
eventId = eventId,
mediaInfo = TESTED_MEDIA_INFO,
mediaSource = aMediaSource(),
thumbnailSource = null,
canShowInfo = canShowInfo,
),
navigator = mediaViewerNavigator,
dataSource = MediaViewerDataSource(
mode = mode,
dispatcher = testCoroutineDispatchers().computation,
galleryDataSource = mediaGalleryDataSource,
mediaLoader = matrixMediaLoader,
localMediaFactory = localMediaFactory,
systemClock = FakeSystemClock(),
pagerKeysHandler = PagerKeysHandler(),
),
room = room,
localMediaActions = localMediaActions,
)
}
}
internal fun TestScope.createMediaViewerPresenter(
localMediaFactory: LocalMediaFactory,
eventId: EventId? = null,
mode: MediaViewerEntryPoint.MediaViewerMode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia,
matrixMediaLoader: FakeMatrixMediaLoader = FakeMatrixMediaLoader(),
localMediaActions: FakeLocalMediaActions = FakeLocalMediaActions(),
mediaGalleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(
startLambda = { },
),
canShowInfo: Boolean = true,
mediaViewerNavigator: MediaViewerNavigator = FakeMediaViewerNavigator(),
room: JoinedRoom = FakeJoinedRoom(
liveTimeline = FakeTimeline(),
),
): MediaViewerPresenter {
return MediaViewerPresenter(
inputs = createMediaViewerEntryPointParams(
eventId = eventId,
mode = mode,
canShowInfo = canShowInfo,
),
navigator = mediaViewerNavigator,
dataSource = MediaViewerDataSource(
mode = mode,
dispatcher = testCoroutineDispatchers().computation,
galleryDataSource = mediaGalleryDataSource,
mediaLoader = matrixMediaLoader,
localMediaFactory = localMediaFactory,
systemClock = FakeSystemClock(),
pagerKeysHandler = PagerKeysHandler(),
),
room = room,
localMediaActions = localMediaActions,
)
}
internal fun createMediaViewerEntryPointParams(
eventId: EventId? = null,
mode: MediaViewerEntryPoint.MediaViewerMode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia,
canShowInfo: Boolean = true,
) = MediaViewerEntryPoint.Params(
mode = mode,
eventId = eventId,
mediaInfo = TESTED_MEDIA_INFO,
mediaSource = aMediaSource(),
thumbnailSource = null,
canShowInfo = canShowInfo,
)

View file

@ -168,7 +168,7 @@ class SingleMediaGalleryDataSourceTest {
assertThat(resultData.fileItems).isEmpty()
}
private fun aMediaViewerEntryPointParams(
internal fun aMediaViewerEntryPointParams(
mediaInfo: MediaInfo,
) = MediaViewerEntryPoint.Params(
mode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia,

View file

@ -33,7 +33,7 @@ class RoomSelectPresenter(
private val dataSource: RoomSelectSearchDataSource,
) : Presenter<RoomSelectState> {
@AssistedFactory
interface Factory {
fun interface Factory {
fun create(mode: RoomSelectMode): RoomSelectPresenter
}

View file

@ -0,0 +1,51 @@
/*
* 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.libraries.roomselect.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint
import io.element.android.libraries.roomselect.api.RoomSelectMode
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultRoomSelectEntryPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultRoomSelectEntryPoint()
val testMode = RoomSelectMode.Share
val parentNode = TestParentNode.create { buildContext, plugins ->
RoomSelectNode(
buildContext = buildContext,
plugins = plugins,
presenterFactory = { mode ->
assertThat(mode).isEqualTo(testMode)
createRoomSelectPresenter(mode)
},
)
}
val callback = object : RoomSelectEntryPoint.Callback {
override fun onRoomSelected(roomIds: List<RoomId>) = lambdaError()
override fun onCancel() = lambdaError()
}
val params = RoomSelectEntryPoint.Params(testMode)
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.params(params)
.callback(callback)
.build()
assertThat(result).isInstanceOf(RoomSelectNode::class.java)
assertThat(result.plugins).contains(RoomSelectNode.Inputs(params.mode))
assertThat(result.plugins).contains(callback)
}
}

View file

@ -111,15 +111,15 @@ class RoomSelectPresenterTest {
cancel()
}
}
private fun TestScope.createRoomSelectPresenter(
mode: RoomSelectMode = RoomSelectMode.Forward,
roomListService: RoomListService = FakeRoomListService(),
) = RoomSelectPresenter(
mode = mode,
dataSource = RoomSelectSearchDataSource(
roomListService = roomListService,
coroutineDispatchers = testCoroutineDispatchers(),
),
)
}
internal fun TestScope.createRoomSelectPresenter(
mode: RoomSelectMode = RoomSelectMode.Forward,
roomListService: RoomListService = FakeRoomListService(),
) = RoomSelectPresenter(
mode = mode,
dataSource = RoomSelectSearchDataSource(
roomListService = roomListService,
coroutineDispatchers = testCoroutineDispatchers(),
),
)

View file

@ -0,0 +1,42 @@
/*
* 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.libraries.troubleshoot.impl
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
import io.element.android.services.analytics.test.FakeScreenTracker
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultNotificationTroubleShootEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultNotificationTroubleShootEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
TroubleshootNotificationsNode(
buildContext = buildContext,
plugins = plugins,
presenter = createTroubleshootNotificationsPresenter(),
screenTracker = FakeScreenTracker(),
)
}
val callback = object : NotificationTroubleShootEntryPoint.Callback {
override fun onDone() = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(TroubleshootNotificationsNode::class.java)
assertThat(result.plugins).contains(callback)
}
}

View file

@ -97,23 +97,23 @@ class TroubleshootNotificationsPresenterTest {
assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading)
}
}
private fun createTroubleshootTestSuite(
tests: Set<NotificationTroubleshootTest> = emptySet(),
currentPushProvider: String? = null,
): TroubleshootTestSuite {
return TroubleshootTestSuite(
notificationTroubleshootTests = tests,
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider),
analyticsService = FakeAnalyticsService(),
)
}
private fun createTroubleshootNotificationsPresenter(
troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(),
): TroubleshootNotificationsPresenter {
return TroubleshootNotificationsPresenter(
troubleshootTestSuite = troubleshootTestSuite,
)
}
}
private fun createTroubleshootTestSuite(
tests: Set<NotificationTroubleshootTest> = emptySet(),
currentPushProvider: String? = null,
): TroubleshootTestSuite {
return TroubleshootTestSuite(
notificationTroubleshootTests = tests,
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider),
analyticsService = FakeAnalyticsService(),
)
}
internal fun createTroubleshootNotificationsPresenter(
troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(),
): TroubleshootNotificationsPresenter {
return TroubleshootNotificationsPresenter(
troubleshootTestSuite = troubleshootTestSuite,
)
}

View file

@ -0,0 +1,49 @@
/*
* 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.libraries.troubleshoot.impl.history
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.test.FakePushService
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
import io.element.android.services.analytics.test.FakeScreenTracker
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.node.TestParentNode
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultPushHistoryEntryPointTest {
@Test
fun `test node builder`() {
val entryPoint = DefaultPushHistoryEntryPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
PushHistoryNode(
buildContext = buildContext,
plugins = plugins,
presenter = PushHistoryPresenter(
pushService = FakePushService(),
),
screenTracker = FakeScreenTracker(),
)
}
val callback = object : PushHistoryEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun onItemClick(sessionId: SessionId, roomId: RoomId, eventId: EventId) = lambdaError()
}
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.callback(callback)
.build()
assertThat(result).isInstanceOf(PushHistoryNode::class.java)
assertThat(result.plugins).contains(callback)
}
}