diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d4a8945d5d..89181cf9cf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,6 +10,7 @@ import com.android.build.api.variant.FilterConfiguration.FilterType.ABI import com.android.build.gradle.internal.tasks.factory.dependsOn import com.android.build.gradle.tasks.GenerateBuildConfig +import config.BuildTimeConfig import extension.AssetCopyTask import extension.ComponentMergingStrategy import extension.GitBranchNameValueSource @@ -43,11 +44,7 @@ android { namespace = "io.element.android.x" defaultConfig { - applicationId = if (isEnterpriseBuild) { - "io.element.enterprise" - } else { - "io.element.android.x" - } + applicationId = BuildTimeConfig.APPLICATION_ID targetSdk = Versions.TARGET_SDK versionCode = Versions.VERSION_CODE versionName = Versions.VERSION_NAME @@ -97,11 +94,7 @@ android { } } - val baseAppName = if (isEnterpriseBuild) { - "Element Enterprise" - } else { - "Element X" - } + val baseAppName = BuildTimeConfig.APPLICATION_NAME logger.warnInBox("Building $baseAppName") buildTypes { diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt index d682cb7524..53d84f473b 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt @@ -73,23 +73,26 @@ object AppModule { @ApplicationContext context: Context, buildType: BuildType, enterpriseService: EnterpriseService, - ) = BuildMeta( - isDebuggable = BuildConfig.DEBUG, - buildType = buildType, - applicationName = ApplicationConfig.APPLICATION_NAME.takeIf { it.isNotEmpty() } ?: context.getString(R.string.app_name), - productionApplicationName = ApplicationConfig.PRODUCTION_APPLICATION_NAME, - desktopApplicationName = ApplicationConfig.DESKTOP_APPLICATION_NAME, - applicationId = BuildConfig.APPLICATION_ID, - isEnterpriseBuild = enterpriseService.isEnterpriseBuild, - // TODO EAx Config.LOW_PRIVACY_LOG_ENABLE, - lowPrivacyLoggingEnabled = false, - versionName = BuildConfig.VERSION_NAME, - versionCode = context.getVersionCodeFromManifest(), - gitRevision = BuildConfig.GIT_REVISION, - gitBranchName = BuildConfig.GIT_BRANCH_NAME, - flavorDescription = BuildConfig.FLAVOR_DESCRIPTION, - flavorShortDescription = BuildConfig.SHORT_FLAVOR_DESCRIPTION, - ) + ): BuildMeta { + val applicationName = ApplicationConfig.APPLICATION_NAME.takeIf { it.isNotEmpty() } ?: context.getString(R.string.app_name) + return BuildMeta( + isDebuggable = BuildConfig.DEBUG, + buildType = buildType, + applicationName = applicationName, + productionApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.PRODUCTION_APPLICATION_NAME, + desktopApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.DESKTOP_APPLICATION_NAME, + applicationId = BuildConfig.APPLICATION_ID, + isEnterpriseBuild = enterpriseService.isEnterpriseBuild, + // TODO EAx Config.LOW_PRIVACY_LOG_ENABLE, + lowPrivacyLoggingEnabled = false, + versionName = BuildConfig.VERSION_NAME, + versionCode = context.getVersionCodeFromManifest(), + gitRevision = BuildConfig.GIT_REVISION, + gitBranchName = BuildConfig.GIT_BRANCH_NAME, + flavorDescription = BuildConfig.FLAVOR_DESCRIPTION, + flavorShortDescription = BuildConfig.SHORT_FLAVOR_DESCRIPTION, + ) + } @Provides @SingleIn(AppScope::class) diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt index 2e0e46fa0b..5061290948 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt @@ -10,11 +10,6 @@ package io.element.android.appconfig object AuthenticationConfig { const val MATRIX_ORG_URL = "https://matrix.org" - /** - * Default homeserver url to sign in with, unless the user selects a different one. - */ - const val DEFAULT_HOMESERVER_URL = MATRIX_ORG_URL - /** * URL with some docs that explain what's sliding sync and how to add it to your home server. */ diff --git a/enterprise b/enterprise index 0c028db8a4..6d96bf58ae 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 0c028db8a48118433c7e11737080a6a01fb90f69 +Subproject commit 6d96bf58aec2ecc77b408858272cd64ec26e10d0 diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 305aae2243..4fddfe283c 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.SessionId interface EnterpriseService { val isEnterpriseBuild: Boolean suspend fun isEnterpriseUser(sessionId: SessionId): Boolean + fun defaultHomeserver(): String? fun semanticColorsLight(): SemanticColors fun semanticColorsDark(): SemanticColors diff --git a/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 1a18ddbee3..898f59969c 100644 --- a/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -22,6 +22,8 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService { override suspend fun isEnterpriseUser(sessionId: SessionId) = false + override fun defaultHomeserver() = null + override fun semanticColorsLight(): SemanticColors = compoundColorsLight override fun semanticColorsDark(): SemanticColors = compoundColorsDark diff --git a/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt b/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt index e2a6a11a9a..0d5593dab9 100644 --- a/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt +++ b/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt @@ -19,6 +19,12 @@ class DefaultEnterpriseServiceTest { assertThat(defaultEnterpriseService.isEnterpriseBuild).isFalse() } + @Test + fun `defaultHomeserver should return null`() { + val defaultEnterpriseService = DefaultEnterpriseService() + assertThat(defaultEnterpriseService.defaultHomeserver()).isNull() + } + @Test fun `isEnterpriseUser always return false`() = runTest { val defaultEnterpriseService = DefaultEnterpriseService() diff --git a/features/enterprise/test/build.gradle.kts b/features/enterprise/test/build.gradle.kts new file mode 100644 index 0000000000..91b76f4fa7 --- /dev/null +++ b/features/enterprise/test/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * 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. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.enterprise.test" +} + +dependencies { + api(projects.features.enterprise.api) + implementation(libs.compound) + implementation(projects.libraries.matrix.api) + implementation(projects.tests.testutils) +} diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt new file mode 100644 index 0000000000..fbb826ec73 --- /dev/null +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -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.enterprise.test + +import io.element.android.compound.tokens.generated.SemanticColors +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask + +class FakeEnterpriseService( + override val isEnterpriseBuild: Boolean = false, + private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() }, + private val defaultHomeserverResult: () -> String? = { A_FAKE_HOMESERVER }, + private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() }, + private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() }, +) : EnterpriseService { + override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask { + isEnterpriseUserResult(sessionId) + } + + override fun defaultHomeserver(): String? { + return defaultHomeserverResult() + } + + override fun semanticColorsLight(): SemanticColors { + return semanticColorsLightResult() + } + + override fun semanticColorsDark(): SemanticColors { + return semanticColorsDarkResult() + } + + companion object { + const val A_FAKE_HOMESERVER = "a_fake_homeserver" + } +} diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 327eaa5416..02c38e2a57 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -28,6 +28,7 @@ setupAnvil(componentMergingStrategy = ComponentMergingStrategy.KSP) dependencies { implementation(projects.appconfig) + implementation(projects.features.enterprise.api) implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) @@ -55,6 +56,7 @@ dependencies { testImplementation(libs.test.robolectric) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oidc.impl) testImplementation(projects.libraries.permissions.test) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt index 913cfb6f0c..3f9c368a1d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSource.kt @@ -7,7 +7,8 @@ package io.element.android.features.login.impl.accountprovider -import io.element.android.features.login.impl.util.defaultAccountProvider +import io.element.android.appconfig.AuthenticationConfig +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn import kotlinx.coroutines.flow.MutableStateFlow @@ -16,7 +17,18 @@ import kotlinx.coroutines.flow.asStateFlow import javax.inject.Inject @SingleIn(AppScope::class) -class AccountProviderDataSource @Inject constructor() { +class AccountProviderDataSource @Inject constructor( + enterpriseService: EnterpriseService, +) { + private val defaultAccountProvider = (enterpriseService.defaultHomeserver() ?: AuthenticationConfig.MATRIX_ORG_URL).let { url -> + AccountProvider( + url = url, + subtitle = null, + isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, + isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, + ) + } + private val accountProvider: MutableStateFlow = MutableStateFlow( defaultAccountProvider ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt deleted file mode 100644 index 6d06ad8c3e..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2023, 2024 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.util - -import io.element.android.appconfig.AuthenticationConfig -import io.element.android.features.login.impl.accountprovider.AccountProvider - -val defaultAccountProvider = AccountProvider( - url = AuthenticationConfig.DEFAULT_HOMESERVER_URL, - subtitle = null, - isPublic = AuthenticationConfig.DEFAULT_HOMESERVER_URL == AuthenticationConfig.MATRIX_ORG_URL, - isMatrixOrg = AuthenticationConfig.DEFAULT_HOMESERVER_URL == AuthenticationConfig.MATRIX_ORG_URL, -) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt new file mode 100644 index 0000000000..b0570cee93 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt @@ -0,0 +1,84 @@ +/* + * 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.accountprovider + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.appconfig.AuthenticationConfig +import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class AccountProviderDataSourceTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val sut = AccountProviderDataSource(FakeEnterpriseService()) + sut.flow().test { + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + AccountProvider( + url = FakeEnterpriseService.A_FAKE_HOMESERVER, + title = FakeEnterpriseService.A_FAKE_HOMESERVER, + subtitle = null, + isPublic = false, + isMatrixOrg = false, + isValid = false, + ) + ) + } + } + + @Test + fun `present - initial state - matrix org`() = runTest { + val sut = AccountProviderDataSource(FakeEnterpriseService( + defaultHomeserverResult = { AuthenticationConfig.MATRIX_ORG_URL } + )) + sut.flow().test { + val initialState = awaitItem() + assertThat(initialState).isEqualTo( + AccountProvider( + url = AuthenticationConfig.MATRIX_ORG_URL, + title = "matrix.org", + subtitle = null, + isPublic = true, + isMatrixOrg = true, + isValid = false, + ) + ) + } + } + + @Test + fun `present - user change and reset`() = runTest { + val sut = AccountProviderDataSource(FakeEnterpriseService()) + sut.flow().test { + val initialState = awaitItem() + assertThat(initialState.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER) + sut.userSelection(AccountProvider(url = "https://example.com")) + val changedState = awaitItem() + assertThat(changedState).isEqualTo( + AccountProvider( + url = "https://example.com", + title = "example.com", + subtitle = null, + isPublic = false, + isMatrixOrg = false, + isValid = false, + ) + ) + sut.reset() + val resetState = awaitItem() + assertThat(resetState.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER) + } + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 2dbde8633e..15b8856969 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -7,10 +7,8 @@ package io.element.android.features.login.impl.changeserver -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.libraries.architecture.AsyncData @@ -18,6 +16,7 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -28,13 +27,7 @@ class ChangeServerPresenterTest { @Test fun `present - initial state`() = runTest { - val presenter = ChangeServerPresenter( - FakeMatrixAuthenticationService(), - AccountProviderDataSource() - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createPresenter().test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) } @@ -43,13 +36,9 @@ class ChangeServerPresenterTest { @Test fun `present - change server ok`() = runTest { val authenticationService = FakeMatrixAuthenticationService() - val presenter = ChangeServerPresenter( - authenticationService, - AccountProviderDataSource() - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createPresenter( + authenticationService = authenticationService, + ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) authenticationService.givenHomeserver(A_HOMESERVER) @@ -63,14 +52,7 @@ class ChangeServerPresenterTest { @Test fun `present - change server error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - val presenter = ChangeServerPresenter( - authenticationService, - AccountProviderDataSource() - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createPresenter().test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) @@ -84,4 +66,12 @@ class ChangeServerPresenterTest { assertThat(finalState.changeServerAction).isEqualTo(AsyncData.Uninitialized) } } + + private fun createPresenter( + authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), + accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + ) = ChangeServerPresenter( + authenticationService = authenticationService, + accountProviderDataSource = accountProviderDataSource + ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 3ff604a1c5..89065f95ea 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -11,10 +11,10 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.DefaultLoginUserStory import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported -import io.element.android.features.login.impl.util.defaultAccountProvider import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.libraries.architecture.AsyncData @@ -44,7 +44,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() assertThat(initialState.isAccountCreation).isFalse() assertThat(initialState.submitEnabled).isTrue() - assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider) + assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER) assertThat(initialState.loginFlow).isEqualTo(AsyncData.Uninitialized) } } @@ -350,7 +350,7 @@ class ConfirmAccountProviderPresenterTest { private fun createConfirmAccountProviderPresenter( params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false), - accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(), + accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), defaultOidcActionFlow: DefaultOidcActionFlow = DefaultOidcActionFlow(), defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt index e118570f4e..8c39685316 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt @@ -8,8 +8,8 @@ package io.element.android.features.login.impl.screens.createaccount import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource -import io.element.android.features.login.impl.util.defaultAccountProvider import io.element.android.libraries.matrix.api.auth.external.ExternalSession import kotlinx.serialization.SerializationException import org.junit.Assert.assertThrows @@ -27,9 +27,7 @@ class DefaultMessageParserTest { @Test fun `DefaultMessageParser is able to parse correct message`() { - val sut = DefaultMessageParser( - AccountProviderDataSource() - ) + val sut = createDefaultMessageParser() assertThat(sut.parse(validMessage)).isEqualTo( anExternalSession( homeserverUrl = "home_server", @@ -39,9 +37,7 @@ class DefaultMessageParserTest { @Test fun `DefaultMessageParser should throw Exception in case of error`() { - val sut = DefaultMessageParser( - AccountProviderDataSource() - ) + val sut = createDefaultMessageParser() // kotlinx.serialization.json.internal.JsonDecodingException assertThrows(SerializationException::class.java) { sut.parse("invalid json") } // missing userId @@ -60,16 +56,20 @@ class DefaultMessageParserTest { @Test fun `DefaultMessageParser should be successful even is homeserver url is missing`() { - val sut = DefaultMessageParser( - AccountProviderDataSource() - ) + val sut = createDefaultMessageParser() // missing homeServer assertThat(sut.parse(validMessage.replace(""""home_server": "home_server",""", ""))).isEqualTo( anExternalSession( - homeserverUrl = defaultAccountProvider.url, + homeserverUrl = FakeEnterpriseService.A_FAKE_HOMESERVER, ) ) } + + private fun createDefaultMessageParser(): DefaultMessageParser { + return DefaultMessageParser( + AccountProviderDataSource(FakeEnterpriseService()) + ) + } } internal fun anExternalSession( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index cced3fece5..bb5b226536 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -7,13 +7,10 @@ package io.element.android.features.login.impl.screens.loginpassword -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.DefaultLoginUserStory import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource -import io.element.android.features.login.impl.util.defaultAccountProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_HOMESERVER @@ -23,6 +20,7 @@ import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -33,19 +31,9 @@ class LoginPasswordPresenterTest { @Test fun `present - initial state`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - val accountProviderDataSource = AccountProviderDataSource() - val loginUserStory = DefaultLoginUserStory() - val presenter = LoginPasswordPresenter( - authenticationService, - accountProviderDataSource, - loginUserStory, - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createLoginPasswordPresenter().test { val initialState = awaitItem() - assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider) + assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER) assertThat(initialState.formState).isEqualTo(LoginFormState.Default) assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.submitEnabled).isFalse() @@ -55,17 +43,10 @@ class LoginPasswordPresenterTest { @Test fun `present - enter login and password`() = runTest { val authenticationService = FakeMatrixAuthenticationService() - val accountProviderDataSource = AccountProviderDataSource() - val loginUserStory = DefaultLoginUserStory() - val presenter = LoginPasswordPresenter( - authenticationService, - accountProviderDataSource, - loginUserStory, - ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createLoginPasswordPresenter( + authenticationService = authenticationService, + ).test { val initialState = awaitItem() initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) val loginState = awaitItem() @@ -81,17 +62,12 @@ class LoginPasswordPresenterTest { @Test fun `present - submit`() = runTest { val authenticationService = FakeMatrixAuthenticationService() - val accountProviderDataSource = AccountProviderDataSource() - val loginUserStory = DefaultLoginUserStory().apply { setLoginFlowIsDone(false) } - val presenter = LoginPasswordPresenter( - authenticationService, - accountProviderDataSource, - loginUserStory, - ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val loginUserStory = DefaultLoginUserStory().apply { setLoginFlowIsDone(false) } + createLoginPasswordPresenter( + authenticationService = authenticationService, + defaultLoginUserStory = loginUserStory, + ).test { assertThat(loginUserStory.loginFlowIsDone.value).isFalse() val initialState = awaitItem() initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) @@ -110,17 +86,10 @@ class LoginPasswordPresenterTest { @Test fun `present - submit with error`() = runTest { val authenticationService = FakeMatrixAuthenticationService() - val accountProviderDataSource = AccountProviderDataSource() - val loginUserStory = DefaultLoginUserStory() - val presenter = LoginPasswordPresenter( - authenticationService, - accountProviderDataSource, - loginUserStory, - ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createLoginPasswordPresenter( + authenticationService = authenticationService, + ).test { val initialState = awaitItem() initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) @@ -138,17 +107,10 @@ class LoginPasswordPresenterTest { @Test fun `present - clear error`() = runTest { val authenticationService = FakeMatrixAuthenticationService() - val accountProviderDataSource = AccountProviderDataSource() - val loginUserStory = DefaultLoginUserStory() - val presenter = LoginPasswordPresenter( - authenticationService, - accountProviderDataSource, - loginUserStory, - ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + createLoginPasswordPresenter( + authenticationService = authenticationService, + ).test { val initialState = awaitItem() initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME)) initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) @@ -167,4 +129,14 @@ class LoginPasswordPresenterTest { assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized) } } + + private fun createLoginPasswordPresenter( + authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(), + accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), + defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory() + ): LoginPasswordPresenter = LoginPasswordPresenter( + authenticationService = authenticationService, + accountProviderDataSource = accountProviderDataSource, + defaultLoginUserStory = defaultLoginUserStory, + ) } diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index 619db35282..044b930572 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -7,6 +7,7 @@ @file:Suppress("UnstableApiUsage") +import config.BuildTimeConfig import extension.setupAnvil plugins { @@ -22,22 +23,14 @@ android { resValue( type = "string", name = "google_app_id", - value = if (isEnterpriseBuild) { - "1:912726360885:android:d273c2077ec3291500427c" - } else { - "1:912726360885:android:d097de99a4c23d2700427c" - } + value = BuildTimeConfig.GOOGLE_APP_ID_RELEASE, ) } getByName("debug") { resValue( type = "string", name = "google_app_id", - value = if (isEnterpriseBuild) { - "1:912726360885:android:f8de9126a94143d300427c" - } else { - "1:912726360885:android:def0a4e454042e9b00427c" - } + value = BuildTimeConfig.GOOGLE_APP_ID_DEBUG, ) } register("nightly") { @@ -46,11 +39,7 @@ android { resValue( type = "string", name = "google_app_id", - value = if (isEnterpriseBuild) { - "1:912726360885:android:3f7e1fe644d99d5a00427c" - } else { - "1:912726360885:android:e17435e0beb0303000427c" - } + value = BuildTimeConfig.GOOGLE_APP_ID_NIGHTLY, ) } } diff --git a/plugins/src/main/kotlin/config/BuildTimeConfig.kt b/plugins/src/main/kotlin/config/BuildTimeConfig.kt new file mode 100644 index 0000000000..f8512ab956 --- /dev/null +++ b/plugins/src/main/kotlin/config/BuildTimeConfig.kt @@ -0,0 +1,16 @@ +/* + * 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 config + +object BuildTimeConfig { + const val APPLICATION_ID = "io.element.android.x" + const val APPLICATION_NAME = "Element X" + const val GOOGLE_APP_ID_RELEASE = "1:912726360885:android:d097de99a4c23d2700427c" + const val GOOGLE_APP_ID_DEBUG = "1:912726360885:android:def0a4e454042e9b00427c" + const val GOOGLE_APP_ID_NIGHTLY = "1:912726360885:android:e17435e0beb0303000427c" +} diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 3e8f8f57b0..d7fbdfae2c 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -25,7 +25,7 @@ internal fun DependencyHandler.implementation(dependency: Any) = add("implementa private fun DependencyHandlerScope.implementation( dependency: Any, config: Action -) = dependencies.add("implementation", dependency, closureOf { config.execute(this) }) +) = dependencies.add("implementation", dependency, closureOf { config.execute(this) }) private fun DependencyHandlerScope.androidTestImplementation(dependency: Any) = dependencies.add("androidTestImplementation", dependency) @@ -114,14 +114,30 @@ fun DependencyHandlerScope.allServicesImpl() { implementation(project(":services:toolbox:impl")) } -fun DependencyHandlerScope.allEnterpriseImpl(project: Project) = addAll(project, "enterprise", "impl") +fun DependencyHandlerScope.allEnterpriseImpl(project: Project) = addAll( + project = project, + modulePrefix = ":enterprise:features", + moduleSuffix = ":impl", +) -fun DependencyHandlerScope.allFeaturesImpl(project: Project) = addAll(project, "features", "impl") +fun DependencyHandlerScope.allFeaturesImpl(project: Project) = addAll( + project = project, + modulePrefix = ":features", + moduleSuffix = ":impl", +) -fun DependencyHandlerScope.allFeaturesApi(project: Project) = addAll(project, "features", "api") +fun DependencyHandlerScope.allFeaturesApi(project: Project) = addAll( + project = project, + modulePrefix = ":features", + moduleSuffix = ":api", +) -private fun DependencyHandlerScope.addAll(project: Project, prefix: String, suffix: String) { - val subProjects = project.rootProject.subprojects.filter { it.path.startsWith(":$prefix") && it.path.endsWith(":$suffix") } +private fun DependencyHandlerScope.addAll( + project: Project, + modulePrefix: String, + moduleSuffix: String, +) { + val subProjects = project.rootProject.subprojects.filter { it.path.startsWith(modulePrefix) && it.path.endsWith(moduleSuffix) } for (p in subProjects) { add("implementation", p) } diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt index 24b13feb62..19fbc5b854 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt @@ -7,6 +7,7 @@ package io.element.android.tests.konsist +import com.google.common.truth.Truth.assertThat import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.Test @@ -43,10 +44,13 @@ class KonsistLicenseTest { .scopeFromProject() .files .filter { - it.path.contains("/enterprise/features").not() && + it.moduleName.startsWith("enterprise").not() && it.nameWithExtension != "locales.kt" && it.name.startsWith("Template ").not() } + .also { + assertThat(it).isNotEmpty() + } .assertTrue { publicLicense.containsMatchIn(it.text) } @@ -58,7 +62,10 @@ class KonsistLicenseTest { .scopeFromProject() .files .filter { - it.path.contains("/enterprise/features") + it.moduleName.startsWith("enterprise") + } + .also { + assertThat(it).isNotEmpty() } .assertTrue { enterpriseLicense.containsMatchIn(it.text)