From f0e575e25efb45aa1d2eb48dd7a7259d11787a7e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Aug 2026 15:11:00 +0200 Subject: [PATCH] Move the fake entry points of RootFlowNodeTest to the test modules The fakes were declared in RootFlowNodeTest, so other tests could not use them. Move each of them next to the API it fakes, following the existing convention of a createNodeResult lambda defaulting to lambdaError(): - FakeLoginEntryPoint in features/login/test - FakeBugReportEntryPoint in features/rageshake/test - FakeSignedOutEntryPoint in features/signedout/test (new module) - FakeAccountSelectEntryPoint in libraries/accountselect/test (new module) The login and rageshake test modules did not have Appyx on their classpath, so add the architecture module to their dependencies. Co-Authored-By: Claude Opus 5 (1M context) --- appnav/build.gradle.kts | 2 + .../android/appnav/RootFlowNodeTest.kt | 58 +++++++------------ features/login/test/build.gradle.kts | 1 + .../login/test/FakeLoginEntryPoint.kt | 24 ++++++++ features/rageshake/test/build.gradle.kts | 1 + .../rageshake/test/FakeBugReportEntryPoint.kt | 23 ++++++++ features/signedout/test/build.gradle.kts | 19 ++++++ .../signedout/test/FakeSignedOutEntryPoint.kt | 23 ++++++++ libraries/accountselect/test/build.gradle.kts | 19 ++++++ .../test/FakeAccountSelectEntryPoint.kt | 23 ++++++++ 10 files changed, 156 insertions(+), 37 deletions(-) create mode 100644 features/login/test/src/main/kotlin/io/element/android/features/login/test/FakeLoginEntryPoint.kt create mode 100644 features/rageshake/test/src/main/kotlin/io/element/android/features/rageshake/test/FakeBugReportEntryPoint.kt create mode 100644 features/signedout/test/build.gradle.kts create mode 100644 features/signedout/test/src/main/kotlin/io/element/android/features/signedout/test/FakeSignedOutEntryPoint.kt create mode 100644 libraries/accountselect/test/build.gradle.kts create mode 100644 libraries/accountselect/test/src/main/kotlin/io/element/android/libraries/accountselect/test/FakeAccountSelectEntryPoint.kt diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 64aa2a6fc5..8e21f247ee 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { testCommonDependencies(libs) testImplementation(projects.features.login.test) testImplementation(projects.features.share.test) + testImplementation(projects.libraries.accountselect.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrixmedia.test) @@ -75,6 +76,7 @@ dependencies { testImplementation(projects.features.messages.test) testImplementation(projects.features.networkmonitor.test) testImplementation(projects.features.rageshake.test) + testImplementation(projects.features.signedout.test) testImplementation(projects.services.apperror.test) testImplementation(projects.services.appnavstate.test) testImplementation(projects.services.analytics.test) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt index 9710b72df7..1661782871 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt @@ -33,13 +33,14 @@ import io.element.android.appnav.root.RootPresenter import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.api.LoginParams import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl +import io.element.android.features.login.test.FakeLoginEntryPoint import io.element.android.features.login.test.FakeLoginIntentResolver import io.element.android.features.preferences.api.CacheService -import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint +import io.element.android.features.rageshake.test.FakeBugReportEntryPoint import io.element.android.features.rageshake.test.logs.FakeAnnouncementService import io.element.android.features.share.test.FakeShareIntentHandler -import io.element.android.features.signedout.api.SignedOutEntryPoint -import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint +import io.element.android.features.signedout.test.FakeSignedOutEntryPoint +import io.element.android.libraries.accountselect.test.FakeAccountSelectEntryPoint import io.element.android.libraries.architecture.AssistedNodeFactory import io.element.android.libraries.architecture.NodeFactoriesBindings import io.element.android.libraries.architecture.Presenter @@ -81,8 +82,13 @@ class RootFlowNodeTest : RobolectricTest() { @Test fun `given no session, when a login link is handled before the nav state is observed, then the login params are kept`() = runTest { - val loginEntryPoint = FakeLoginEntryPoint() - val rootFlowNode = createRootFlowNode(loginEntryPoint = loginEntryPoint) + var loginEntryPointParams: LoginEntryPoint.Params? = null + val rootFlowNode = createRootFlowNode( + loginEntryPoint = FakeLoginEntryPoint { buildContext, params -> + loginEntryPointParams = params + node(buildContext) {} + }, + ) rootFlowNode.parentNodeTestHelper() // The intent is handled first, this is what happens when the deep link cold starts the app. rootFlowNode.handleIntent(aLoginIntent()) @@ -90,13 +96,18 @@ class RootFlowNodeTest : RobolectricTest() { // Then the first nav state emission lands, it must not override the login params. runCurrent() assertThat(rootFlowNode.backstack.activeElement).isEqualTo(RootFlowNode.NavTarget.NotLoggedInFlow(A_LOGIN_PARAMS)) - assertThat(loginEntryPoint.params).isEqualTo(A_LOGIN_ENTRY_POINT_PARAMS) + assertThat(loginEntryPointParams).isEqualTo(A_LOGIN_ENTRY_POINT_PARAMS) } @Test fun `given no session, when a login link is handled after the nav state is observed, then the login params are applied`() = runTest { - val loginEntryPoint = FakeLoginEntryPoint() - val rootFlowNode = createRootFlowNode(loginEntryPoint = loginEntryPoint) + var loginEntryPointParams: LoginEntryPoint.Params? = null + val rootFlowNode = createRootFlowNode( + loginEntryPoint = FakeLoginEntryPoint { buildContext, params -> + loginEntryPointParams = params + node(buildContext) {} + }, + ) rootFlowNode.parentNodeTestHelper() // The nav state is observed first, this is what happens when the app is already running. runCurrent() @@ -104,13 +115,13 @@ class RootFlowNodeTest : RobolectricTest() { rootFlowNode.handleIntent(aLoginIntent()) runCurrent() assertThat(rootFlowNode.backstack.activeElement).isEqualTo(RootFlowNode.NavTarget.NotLoggedInFlow(A_LOGIN_PARAMS)) - assertThat(loginEntryPoint.params).isEqualTo(A_LOGIN_ENTRY_POINT_PARAMS) + assertThat(loginEntryPointParams).isEqualTo(A_LOGIN_ENTRY_POINT_PARAMS) } private fun aLoginIntent() = Intent(Intent.ACTION_VIEW, Uri.parse(A_LOGIN_LINK)) private fun TestScope.createRootFlowNode( - loginEntryPoint: LoginEntryPoint = FakeLoginEntryPoint(), + loginEntryPoint: LoginEntryPoint, sessionStore: SessionStore = InMemorySessionStore(), ): RootFlowNode { val matrixSessionCache = MatrixSessionCache( @@ -215,33 +226,6 @@ private class FakeAccountProviderAccessControl : AccountProviderAccessControl { override suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String) = true } -private class FakeBugReportEntryPoint : BugReportEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, callback: BugReportEntryPoint.Callback): Node = node(buildContext) {} -} - -private class FakeSignedOutEntryPoint : SignedOutEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, params: SignedOutEntryPoint.Params): Node = node(buildContext) {} -} - -private class FakeAccountSelectEntryPoint : AccountSelectEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, callback: AccountSelectEntryPoint.Callback): Node = node(buildContext) {} -} - -private class FakeLoginEntryPoint : LoginEntryPoint { - var params: LoginEntryPoint.Params? = null - private set - - override fun createNode( - parentNode: Node, - buildContext: BuildContext, - params: LoginEntryPoint.Params, - callback: LoginEntryPoint.Callback, - ): Node { - this.params = params - return node(buildContext) {} - } -} - private class FakeNodeFactoriesBindings( private val nodeFactories: Map, AssistedNodeFactory<*>>, ) : NodeFactoriesBindings { diff --git a/features/login/test/build.gradle.kts b/features/login/test/build.gradle.kts index 7073648dd2..4cbf0910db 100644 --- a/features/login/test/build.gradle.kts +++ b/features/login/test/build.gradle.kts @@ -15,6 +15,7 @@ android { } dependencies { + implementation(projects.libraries.architecture) implementation(projects.features.login.api) implementation(projects.tests.testutils) } diff --git a/features/login/test/src/main/kotlin/io/element/android/features/login/test/FakeLoginEntryPoint.kt b/features/login/test/src/main/kotlin/io/element/android/features/login/test/FakeLoginEntryPoint.kt new file mode 100644 index 0000000000..2c94144890 --- /dev/null +++ b/features/login/test/src/main/kotlin/io/element/android/features/login/test/FakeLoginEntryPoint.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2026 Element Creations 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.login.api.LoginEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeLoginEntryPoint( + private val createNodeResult: (BuildContext, LoginEntryPoint.Params) -> Node = { _, _ -> lambdaError() }, +) : LoginEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: LoginEntryPoint.Params, + callback: LoginEntryPoint.Callback, + ): Node = createNodeResult(buildContext, params) +} diff --git a/features/rageshake/test/build.gradle.kts b/features/rageshake/test/build.gradle.kts index afc5e51499..faad812d73 100644 --- a/features/rageshake/test/build.gradle.kts +++ b/features/rageshake/test/build.gradle.kts @@ -14,6 +14,7 @@ android { } dependencies { + implementation(projects.libraries.architecture) implementation(projects.features.rageshake.api) implementation(libs.coroutines.core) implementation(projects.tests.testutils) diff --git a/features/rageshake/test/src/main/kotlin/io/element/android/features/rageshake/test/FakeBugReportEntryPoint.kt b/features/rageshake/test/src/main/kotlin/io/element/android/features/rageshake/test/FakeBugReportEntryPoint.kt new file mode 100644 index 0000000000..d6e1a47368 --- /dev/null +++ b/features/rageshake/test/src/main/kotlin/io/element/android/features/rageshake/test/FakeBugReportEntryPoint.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeBugReportEntryPoint( + private val createNodeResult: (BuildContext, BugReportEntryPoint.Callback) -> Node = { _, _ -> lambdaError() }, +) : BugReportEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: BugReportEntryPoint.Callback, + ): Node = createNodeResult(buildContext, callback) +} diff --git a/features/signedout/test/build.gradle.kts b/features/signedout/test/build.gradle.kts new file mode 100644 index 0000000000..5bbfb33c1a --- /dev/null +++ b/features/signedout/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.signedout.test" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.features.signedout.api) + implementation(projects.tests.testutils) +} diff --git a/features/signedout/test/src/main/kotlin/io/element/android/features/signedout/test/FakeSignedOutEntryPoint.kt b/features/signedout/test/src/main/kotlin/io/element/android/features/signedout/test/FakeSignedOutEntryPoint.kt new file mode 100644 index 0000000000..863b946871 --- /dev/null +++ b/features/signedout/test/src/main/kotlin/io/element/android/features/signedout/test/FakeSignedOutEntryPoint.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.signedout.api.SignedOutEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeSignedOutEntryPoint( + private val createNodeResult: (BuildContext, SignedOutEntryPoint.Params) -> Node = { _, _ -> lambdaError() }, +) : SignedOutEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: SignedOutEntryPoint.Params, + ): Node = createNodeResult(buildContext, params) +} diff --git a/libraries/accountselect/test/build.gradle.kts b/libraries/accountselect/test/build.gradle.kts new file mode 100644 index 0000000000..c38cd8ac69 --- /dev/null +++ b/libraries/accountselect/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.accountselect.test" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.accountselect.api) + implementation(projects.tests.testutils) +} diff --git a/libraries/accountselect/test/src/main/kotlin/io/element/android/libraries/accountselect/test/FakeAccountSelectEntryPoint.kt b/libraries/accountselect/test/src/main/kotlin/io/element/android/libraries/accountselect/test/FakeAccountSelectEntryPoint.kt new file mode 100644 index 0000000000..acc5ba10df --- /dev/null +++ b/libraries/accountselect/test/src/main/kotlin/io/element/android/libraries/accountselect/test/FakeAccountSelectEntryPoint.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations 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.accountselect.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeAccountSelectEntryPoint( + private val createNodeResult: (BuildContext, AccountSelectEntryPoint.Callback) -> Node = { _, _ -> lambdaError() }, +) : AccountSelectEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: AccountSelectEntryPoint.Callback, + ): Node = createNodeResult(buildContext, callback) +}