diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bbf8fdf811..4bb11596fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -78,6 +78,9 @@ jobs: - name: ⚙️ Run screenshot tests run: ./gradlew :tests:uitests:verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES + - name: ⚙️ Run Compound screenshot tests + run: ./gradlew :libraries:compound:verifyRoborazziDebug $CI_GRADLE_ARG_PROPERTIES + - name: ⚙️ Generate kover coverage report run: ./gradlew :koverXmlReportMerged :koverHtmlReportMerged :koverVerifyAll -x testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES 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 7432a6904f..a04abe1417 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/AuthenticationConfig.kt @@ -9,15 +9,8 @@ package io.element.android.appconfig object AuthenticationConfig { - const val MATRIX_ORG_URL = "https://matrix.org" - /** * URL with some docs that explain what's sliding sync and how to add it to your home server. */ const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md" - - /** - * Force a sliding sync proxy url, if not null, the proxy url in the .well-known file will be ignored. - */ - val SLIDING_SYNC_PROXY_URL: String? = null } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index c9deaeb3b3..5f2dc837ad 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -52,11 +52,11 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.rememberDelegateTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached -import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.deeplink.api.DeeplinkData import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId @@ -410,7 +410,8 @@ class RootFlowNode( } private suspend fun onLoginLink(params: LoginParams) { - if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(params.accountProvider.ensureProtocol())) { + val accountProvider = AccountProvider.Generic(params.accountProvider) + if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(accountProvider)) { // Is there a session already? val sessions = sessionStore.getAllSessions() if (sessions.isNotEmpty()) { diff --git a/build.gradle.kts b/build.gradle.kts index 876383e325..725033a6ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -153,7 +153,10 @@ allprojects { maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) val isScreenshotTest = project.gradle.startParameter.taskNames.any { it.contains("paparazzi", ignoreCase = true) } + val isRoborazziTest = project.gradle.startParameter.taskNames.any { it.contains("roborazzi", ignoreCase = true) } if (isScreenshotTest) { + // Paparazzi tests benefit from parallelisation, so we can use half the available cores to run them in parallel. + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) // Increase heap size for screenshot tests maxHeapSize = "2g" // Record all the languages? @@ -165,9 +168,22 @@ allprojects { exclude("translations/*.class") } } else { + // Robolectric and Compose pay a 5 to 30 seconds bootstrap cost per test JVM (instrumenting + // android-all, then warming up the Compose runtime). That cost is paid once per JVM and then + // amortised over every test class the JVM runs, so splitting a module across several forks + // re-pays it for each fork instead of saving time. Keep a single fork per module and let + // Gradle parallelise by running many modules' test tasks concurrently instead. + maxParallelForks = 1 + // Disable screenshot tests by default exclude("ui/*.class") exclude("translations/*.class") + if (isRoborazziTest.not()) { + // Roborazzi screenshot tests (:libraries:compound) live in a `screenshot` package, which + // the two patterns above do not match, so they used to run on every plain unit test run. + // They are verified by the dedicated `verifyRoborazziDebug` task instead. + exclude("**/screenshot/**") + } } } } diff --git a/enterprise b/enterprise index f463003f68..6b422f4c8e 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit f463003f6813ad33fc00f80786bc83b6a7f0d8e2 +Subproject commit 6b422f4c8e9e9e5557056e8a861fe3bfd9338f24 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 2cb4a356be..5483d1e60e 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 @@ -11,6 +11,7 @@ package io.element.android.features.enterprise.api import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.libraries.matrix.api.ClientUrlContentFetcher +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.flow.Flow @@ -36,18 +37,21 @@ interface EnterpriseService { suspend fun tweakMasUrl(url: String, urlContentFetcher: ClientUrlContentFetcher): String /** - * Returns the list of homeservers the user is allowed to sign in to. - * - * If the list is empty or contains the special value [ANY_ACCOUNT_PROVIDER], the user is allowed to sign in to any homeserver. + * Returns the list of account provider the user is allowed to sign in to. */ - fun homeserverAllowList(): List + fun accountProviderAllowList(): List /** - * Whether the user is allowed to sign in to a given homeserver, according to [homeserverAllowList]. - * - * @param homeserverUrl the server the user is trying to use. + * Whether the user is allowed to sign in to any account provider. */ - suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean + fun canConnectToAnyAccountProvider(): Boolean + + /** + * Whether the user is allowed to sign in to a given homeserver, according to [accountProviderAllowList]. + * + * @param accountProvider the account provider the user is trying to use. + */ + suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider): Boolean /** * Whether the given homeserver enforces the use of Element Pro or a derived app. @@ -97,14 +101,4 @@ interface EnterpriseService { * @param sessionId the session whose channel is requested. */ fun getNoisyNotificationChannelId(sessionId: SessionId): String? - - companion object { - const val ANY_ACCOUNT_PROVIDER = "*" - } -} - -fun EnterpriseService.canConnectToAnyHomeserver(): Boolean { - return homeserverAllowList().let { - it.isEmpty() || it.contains(EnterpriseService.ANY_ACCOUNT_PROVIDER) - } } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 6ccf70cd02..02fa9b282c 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.api.ClientUrlContentFetcher import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -33,8 +34,9 @@ class DefaultEnterpriseService( ) : EnterpriseService { override suspend fun isEnterpriseUser(sessionId: SessionId) = false override suspend fun tweakMasUrl(url: String, urlContentFetcher: ClientUrlContentFetcher) = url - override fun homeserverAllowList(): List = emptyList() - override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true + override fun accountProviderAllowList(): List = emptyList() + override fun canConnectToAnyAccountProvider(): Boolean = true + override suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider) = true override suspend fun isElementProEnforced(serverName: String): Boolean { val temporaryMatrixClient = temporaryMatrixClientFactory.create(serverName).getOrElse { return false } return temporaryMatrixClient.use { client -> diff --git a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt index 026c9caf9e..f43284e0bd 100644 --- a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt +++ b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClient import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderGeneric import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.test.runTest @@ -26,15 +27,21 @@ import org.junit.Test class DefaultEnterpriseServiceTest { @Test - fun homeserverWhitelist() { + fun accountProviderAllowList() { val defaultEnterpriseService = createDefaultEnterpriseService() - assertThat(defaultEnterpriseService.homeserverAllowList()).isEmpty() + assertThat(defaultEnterpriseService.accountProviderAllowList()).isEmpty() } @Test - fun `isAllowedToConnectToHomeserver is true for all homeserver urls`() = runTest { + fun `canConnectToAnyAccountProvider is always true`() { val defaultEnterpriseService = createDefaultEnterpriseService() - assertThat(defaultEnterpriseService.isAllowedToConnectToHomeserver(A_HOMESERVER_URL)).isTrue() + assertThat(defaultEnterpriseService.canConnectToAnyAccountProvider()).isTrue() + } + + @Test + fun `isAllowedToConnectToAccountProvider is true for all account providers`() = runTest { + val defaultEnterpriseService = createDefaultEnterpriseService() + assertThat(defaultEnterpriseService.isAllowedToConnectToAccountProvider(anAccountProviderGeneric())).isTrue() } @Test 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 index a8efcb5873..3ca30d410d 100644 --- 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 @@ -13,6 +13,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.ClientUrlContentFetcher +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask @@ -22,8 +23,9 @@ import kotlinx.coroutines.flow.asStateFlow class FakeEnterpriseService( private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() }, - private val defaultHomeserverListResult: () -> List = { emptyList() }, - private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() }, + private val accountProviderAllowListResult: () -> List = { emptyList() }, + private val canConnectToAnyAccountProviderResult: () -> Boolean = { true }, + private val isAllowedToConnectToAccountProviderResult: (AccountProvider) -> Boolean = { lambdaError() }, initialSemanticColors: SemanticColorsLightDark = SemanticColorsLightDark.default, initialBrandColor: Color? = null, private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() }, @@ -44,12 +46,16 @@ class FakeEnterpriseService( tweakMasUrlResult(url, urlContentFetcher) } - override fun homeserverAllowList(): List { - return defaultHomeserverListResult() + override fun accountProviderAllowList(): List { + return accountProviderAllowListResult() } - override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean = simulateLongTask { - isAllowedToConnectToHomeserverResult(homeserverUrl) + override fun canConnectToAnyAccountProvider(): Boolean { + return canConnectToAnyAccountProviderResult() + } + + override suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider): Boolean = simulateLongTask { + isAllowedToConnectToAccountProviderResult(accountProvider) } override suspend fun isElementProEnforced(serverName: String): Boolean = simulateLongTask { diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt index 9bc8719a8b..5245a164d1 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt @@ -91,7 +91,6 @@ import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.seconds -@Suppress("LargeClass") class RoomListPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index 39cfc3afba..30d6eaf844 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -59,7 +59,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -@Suppress("LargeClass") internal class DefaultInvitePeoplePresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 3c104e47ae..3edaf649e4 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -70,7 +70,6 @@ import org.junit.Rule import org.junit.Test import java.util.Optional -@Suppress("LargeClass") class JoinRoomPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/login/api/build.gradle.kts b/features/login/api/build.gradle.kts index 2a4e3ecb7c..a0683c814e 100644 --- a/features/login/api/build.gradle.kts +++ b/features/login/api/build.gradle.kts @@ -16,4 +16,5 @@ android { dependencies { implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) } diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt index 0c35c1655f..55cfa4b1ba 100644 --- a/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/accesscontrol/AccountProviderAccessControl.kt @@ -8,6 +8,8 @@ package io.element.android.features.login.api.accesscontrol +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider + /** * Enforces the restriction an enterprise deployment can put on which account providers the user may sign in to. */ @@ -15,7 +17,7 @@ interface AccountProviderAccessControl { /** * Whether sign-in to this provider is permitted; `true` on a build with no such restriction. * - * @param accountProviderUrl the server the user is trying to use. + * @param accountProvider the account provider the user is trying to use. */ - suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String): Boolean + suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider): Boolean } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt index a3a475df2f..58fbeed4bc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControl.kt @@ -14,16 +14,16 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.IsEnterpriseBuild import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl import io.element.android.features.login.impl.changeserver.AccountProviderAccessException +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider @ContributesBinding(AppScope::class) class DefaultAccountProviderAccessControl( private val isEnterpriseBuild: IsEnterpriseBuild, private val enterpriseService: EnterpriseService, ) : AccountProviderAccessControl { - override suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String) = try { + override suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider) = try { assertIsAllowedToConnectToAccountProvider( - title = accountProviderUrl, - accountProviderUrl = accountProviderUrl, + accountProvider = accountProvider, ) true } catch (_: AccountProviderAccessException) { @@ -32,22 +32,22 @@ class DefaultAccountProviderAccessControl( @Throws(AccountProviderAccessException::class) suspend fun assertIsAllowedToConnectToAccountProvider( - title: String, - accountProviderUrl: String, + accountProvider: AccountProvider, ) { if (isEnterpriseBuild().not()) { // Ensure that Element Pro is not required for this account provider - if (enterpriseService.isElementProEnforced(accountProviderUrl)) { + if (enterpriseService.isElementProEnforced(accountProvider.serverNameOrBaseUrl())) { throw AccountProviderAccessException.NeedElementProException( - unauthorisedAccountProviderTitle = title, + unauthorisedAccountProviderTitle = accountProvider.friendlyServerName(), applicationId = ELEMENT_PRO_APPLICATION_ID, ) } } - if (enterpriseService.isAllowedToConnectToHomeserver(accountProviderUrl).not()) { + if (enterpriseService.isAllowedToConnectToAccountProvider(accountProvider).not()) { throw AccountProviderAccessException.UnauthorizedAccountProviderException( - unauthorisedAccountProviderTitle = title, - authorisedAccountProviderTitles = enterpriseService.homeserverAllowList(), + unauthorisedAccountProviderTitle = accountProvider.friendlyServerName(), + authorisedAccountProviderTitles = enterpriseService.accountProviderAllowList() + .map { it.friendlyServerName() }, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt deleted file mode 100644 index 7ca467da7d..0000000000 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-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 - -data class AccountProvider( - val url: String, - val title: String = url.removePrefix("https://").removePrefix("http://"), - val subtitle: String? = null, - val isPublic: Boolean = false, - val isMatrixOrg: Boolean = false, -) 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 6d85f2c5ff..3e8e7657a4 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 @@ -11,10 +11,10 @@ package io.element.android.features.login.impl.accountprovider import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn -import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.enterprise.api.canConnectToAnyHomeserver import io.element.android.libraries.di.annotations.AppCoroutineScope +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.matrixOrgAccountProvider import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -33,11 +33,9 @@ class AccountProviderDataSource( ) { // The provider used when the user has not selected one: an enterprise/MDM-configured provider, // else matrix.org. The most recently used provider (from history) can override it, see init. - private val configuredAccountProvider = createAccountProvider( - url = enterpriseService.homeserverAllowList() - .firstOrNull { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } - ?: AuthenticationConfig.MATRIX_ORG_URL - ) + private val configuredAccountProvider = enterpriseService.accountProviderAllowList() + .firstOrNull() + ?: matrixOrgAccountProvider private val accountProvider: MutableStateFlow = MutableStateFlow(configuredAccountProvider) @@ -46,7 +44,7 @@ class AccountProviderDataSource( // The account provider the user last explicitly selected (via [setAccountProvider] / [setUrl]). // Unlike [flow], this is not recomputed by [reset], so it survives to be persisted to history on a // successful sign-in even when the login flow is torn down in between (e.g. across an OAuth round-trip). - var lastSelectedAccountProviderUrl: String? = null + var lastSelectedAccountProvider: AccountProvider? = null private set init { @@ -66,28 +64,19 @@ class AccountProviderDataSource( * connect to any provider, otherwise the enterprise/MDM-configured provider. */ private suspend fun defaultAccountProvider(): AccountProvider { - if (!enterpriseService.canConnectToAnyHomeserver()) { + if (!enterpriseService.canConnectToAnyAccountProvider()) { return configuredAccountProvider } val lastUsedProvider = appPreferencesStore.getHomeserverHistoryFlow().first().firstOrNull() - return lastUsedProvider?.let { createAccountProvider(it) } ?: configuredAccountProvider + return lastUsedProvider?.let { AccountProvider.Generic(it) } ?: configuredAccountProvider } suspend fun setUrl(url: String) { - setAccountProvider(createAccountProvider(url)) + setAccountProvider(AccountProvider.Generic(url)) } suspend fun setAccountProvider(data: AccountProvider) { - lastSelectedAccountProviderUrl = data.url + lastSelectedAccountProvider = data accountProvider.emit(data) } - - private fun createAccountProvider(url: String): AccountProvider { - return AccountProvider( - url = url, - subtitle = null, - isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, - isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, - ) - } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt index c14827d1a6..aca64b9db7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderPreviewParam.kt @@ -9,27 +9,27 @@ package io.element.android.features.login.impl.accountprovider import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.appconfig.AuthenticationConfig +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider open class AccountProviderPreviewParam : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - anAccountProvider(), - anAccountProvider().copy(subtitle = null), - anAccountProvider().copy(subtitle = null, title = "invalid"), - anAccountProvider().copy(subtitle = null, title = "Other", isPublic = false, isMatrixOrg = false), - // Add other state here + anAccountProviderManaged(), ) } -fun anAccountProvider( - url: String = AuthenticationConfig.MATRIX_ORG_URL, - subtitle: String? = "Matrix.org is an open network for secure, decentralized communication.", - isPublic: Boolean = true, - isMatrixOrg: Boolean = true, -) = AccountProvider( - url = url, - subtitle = subtitle, - isPublic = isPublic, - isMatrixOrg = isMatrixOrg, +fun anAccountProviderManaged( + serverName: String = "matrix.org", + baseUrl: String = "https://matrix-client.matrix.org", + canUseServerName: Boolean = true, +) = AccountProvider.Managed( + serverName = serverName, + baseUrl = baseUrl, + canUseServerName = canUseServerName, +) + +fun anAccountProviderGeneric( + serverName: String = "matrix.org", +) = AccountProvider.Generic( + serverName = serverName, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index 09b423f713..8365e9286c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -14,17 +14,14 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.login.impl.R import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.preview.ElementPreview @@ -32,6 +29,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider /** * https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817 @@ -60,37 +58,19 @@ fun AccountProviderView( .heightIn(min = 44.dp), verticalAlignment = Alignment.CenterVertically ) { - if (item.isMatrixOrg) { - RoundedIconAtom( - size = RoundedIconAtomSize.Medium, - resourceId = R.drawable.ic_matrix, - tint = Color.Unspecified, - ) - } else { - RoundedIconAtom( - size = RoundedIconAtomSize.Medium, - imageVector = CompoundIcons.Host(), - tint = ElementTheme.colors.iconPrimary, - ) - } + RoundedIconAtom( + size = RoundedIconAtomSize.Medium, + imageVector = CompoundIcons.Host(), + tint = ElementTheme.colors.iconPrimary, + ) Text( modifier = Modifier .padding(start = 16.dp) .weight(1f), - text = item.title, + text = item.friendlyServerName(), style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary, ) - if (item.isPublic) { - Icon( - modifier = Modifier - .padding(start = 10.dp) - .size(16.dp), - imageVector = CompoundIcons.Public(), - contentDescription = null, - tint = ElementTheme.colors.iconSecondary, - ) - } if (selected) { Icon( modifier = Modifier @@ -101,15 +81,6 @@ fun AccountProviderView( ) } } - if (item.subtitle != null) { - Text( - modifier = Modifier - .padding(start = 46.dp, bottom = 12.dp, end = 26.dp), - text = item.subtitle, - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - ) - } } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt index b3b99115cc..14c1b0346b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistory.kt @@ -25,8 +25,11 @@ class SaveAccountProviderToHistory( suspend operator fun invoke() { // Persist the provider the user actually selected, not the currently-exposed default: the latter can be // reset back to the history default while the login flow is torn down across an OAuth round-trip. - val url = accountProviderDataSource.lastSelectedAccountProviderUrl - ?: accountProviderDataSource.flow.value.url - appPreferencesStore.addHomeserverToHistory(url) + val accountProvider = accountProviderDataSource.lastSelectedAccountProvider + ?: accountProviderDataSource.flow.value + // Persist the sanitized name the user knows the provider by rather than its base url, which can be an + // internal client API url (e.g. matrix.org resolves to matrix-client.matrix.org) the user would not + // recognise, and so that the same provider is not stored twice with and without its scheme. + appPreferencesStore.addHomeserverToHistory(accountProvider.serverNameOrBaseUrl()) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt index f3fd02713f..d1b68190c6 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.login.impl.changeserver -import io.element.android.features.login.impl.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider sealed interface ChangeServerEvent { data class ChangeServer(val accountProvider: AccountProvider) : ChangeServerEvent diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index 9106d6e1c4..2e3a4c73b8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -14,13 +14,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails @@ -38,7 +38,7 @@ class ChangeServerPresenter( } val gateState = localNetworkPermissionGate.present( - urlOf = { provider -> provider.url }, + urlOf = { provider -> provider.serverNameOrBaseUrl() }, onProceed = { provider -> changeServer(provider, changeServerAction) }, ) @@ -63,11 +63,8 @@ class ChangeServerPresenter( changeServerAction: MutableState>, ) { suspend { - defaultAccountProviderAccessControl.assertIsAllowedToConnectToAccountProvider( - title = data.title, - accountProviderUrl = data.url, - ) - val details = authenticationService.setHomeserver(data.url).getOrThrow() + defaultAccountProviderAccessControl.assertIsAllowedToConnectToAccountProvider(data) + val details = authenticationService.setHomeserver(data.serverNameOrBaseUrl()).getOrThrow() if (!details.isSupported) { throw ChangeServerError.UnsupportedServer } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt index bb50f123cb..d3fddeeb1f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider -import io.element.android.features.login.impl.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider sealed interface ChooseAccountProviderEvent { data class SelectAccountProvider(val accountProvider: AccountProvider) : ChooseAccountProviderEvent diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt index e2863a295a..8984216ac8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt @@ -14,14 +14,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject -import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.uri.ensureProtocol +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import kotlinx.collections.immutable.toImmutableList @Inject @@ -41,7 +39,7 @@ class ChooseAccountProviderPresenter( loginModeState.eventSink( LoginModeEvent.Submit( isAccountCreation = false, - homeserverUrl = provider.url, + homeserverUrl = provider.serverNameOrBaseUrl(), resolvedHomeserverUrl = null, loginHint = null, ) @@ -59,18 +57,8 @@ class ChooseAccountProviderPresenter( } val staticAccountProviderList = remember { - // The list cannot contains ANY_ACCOUNT_PROVIDER ("*") and cannot be empty at this point - enterpriseService.homeserverAllowList() - .map { it.ensureProtocol() } - .map { url -> - AccountProvider( - url = url, - subtitle = null, - isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, - isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, - ) - } - .toImmutableList() + // The list cannot be empty at this point + enterpriseService.accountProviderAllowList().toImmutableList() } return ChooseAccountProviderState( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt index 398e32d687..b1e84fe8ec 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderState.kt @@ -8,9 +8,9 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import kotlinx.collections.immutable.ImmutableList data class ChooseAccountProviderState( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt index ceb1cfd6ea..9dc99b466c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStatePreviewParam.kt @@ -9,31 +9,22 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.accountprovider.anAccountProviderManaged import io.element.android.features.login.impl.login.LoginModeState import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import kotlinx.collections.immutable.toImmutableList open class ChooseAccountProviderStatePreviewParam : PreviewParameterProvider { - private val server1 = anAccountProvider( - url = "https://server1.io", - subtitle = null, - isPublic = false, - isMatrixOrg = false, + private val server1 = anAccountProviderManaged( + baseUrl = "https://server1.io", ) - private val server2 = anAccountProvider( - url = "https://server2.io", - subtitle = null, - isPublic = false, - isMatrixOrg = false, + private val server2 = anAccountProviderManaged( + baseUrl = "https://server2.io", ) - private val server3 = anAccountProvider( - url = "https://server3.io", - subtitle = null, - isPublic = false, - isMatrixOrg = false, + private val server3 = anAccountProviderManaged( + baseUrl = "https://server3.io", ) override val values: Sequence get() = sequenceOf( @@ -61,13 +52,12 @@ open class ChooseAccountProviderStatePreviewParam : PreviewParameterProvider = listOf( - anAccountProvider() + anAccountProviderManaged() ), selectedAccountProvider: AccountProvider? = null, loginModeState: LoginModeState = aLoginModeState(), diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index 552165a7b2..c2d0bd05b2 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -96,16 +96,8 @@ fun ChooseAccountProviderView( ) state.accountProviders.forEach { item -> - val alteredItem = if (item.isMatrixOrg) { - // Set the subtitle from the resource - item.copy( - subtitle = stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle), - ) - } else { - item - } AccountProviderView( - item = alteredItem, + item = item, selected = item == state.selectedAccountProvider, onClick = { state.eventSink(ChooseAccountProviderEvent.SelectAccountProvider(item)) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index 627620ff31..201571a8ff 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -20,9 +20,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject -import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.changeserver.ChangeServerEvent import io.element.android.features.login.impl.changeserver.ChangeServerState @@ -30,8 +28,11 @@ import io.element.android.features.login.impl.login.LoginModeEvent import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.uri.ensureProtocol +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.matrixOrgAccountProvider import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import kotlinx.coroutines.flow.map @AssistedInject class ConfirmAccountProviderPresenter( @@ -56,23 +57,25 @@ class ConfirmAccountProviderPresenter( val accountProvider by accountProviderDataSource.flow.collectAsState() val loginModeState = loginModePresenter.present() val changeServerState = changeServerPresenter.present() - val homeserverHistory by appPreferencesStore.getHomeserverHistoryFlow().collectAsState(emptyList()) + val homeserverHistory by remember { + appPreferencesStore.getHomeserverHistoryFlow().map { history -> + history.map { AccountProvider.Generic(it) } + } + }.collectAsState(emptyList()) // The account providers offered for autocomplete: previously-used ones first, then the // enterprise/MDM-configured allow-list, then matrix.org (always available, even before any sign-in). - // The "*" wildcard is a routing marker, not a real provider, so it is filtered out. Everything is - // rendered without the https:// scheme (added back at connection time). + // Everything is rendered without the https:// scheme (added back at connection time). val autocompleteCandidates = remember(homeserverHistory) { - (homeserverHistory + enterpriseService.homeserverAllowList() + AuthenticationConfig.MATRIX_ORG_URL) - .filter { it != EnterpriseService.ANY_ACCOUNT_PROVIDER } - .map { it.withoutScheme() } + (homeserverHistory + enterpriseService.accountProviderAllowList() + matrixOrgAccountProvider) + .map { it.friendlyServerName() } .distinct() } // Editable input, seeded from the current (history-defaulted) account provider until the user edits it. // Displayed without the scheme, so the field shows e.g. "matrix.org" rather than "https://matrix.org". var userInput by rememberSaveable { mutableStateOf(null) } - val accountProviderInput = userInput ?: accountProvider.url.withoutScheme() + val accountProviderInput = userInput ?: accountProvider.friendlyServerName() // Offer the first candidate that the current input is a (case-insensitive) prefix of. val accountProviderSuggestion = remember(accountProviderInput, autocompleteCandidates) { @@ -134,7 +137,7 @@ class ConfirmAccountProviderPresenter( val accountProviderUrl = host.ensureProtocol() submittedAccountProvider = accountProviderUrl changeServerState.eventSink( - ChangeServerEvent.ChangeServer(AccountProvider(url = accountProviderUrl)) + ChangeServerEvent.ChangeServer(AccountProvider.Generic(host)) ) } ConfirmAccountProviderEvent.ClearError -> { @@ -154,9 +157,3 @@ class ConfirmAccountProviderPresenter( ) } } - -/** - * The host part of an account provider URL, i.e. the value with any `https://` / `http://` scheme removed. - * Returns the input unchanged when it has no scheme (e.g. `matrix.org`). - */ -private fun String.withoutScheme(): String = substringAfter("://") diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt index db04e7b7ba..13d189af6f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt @@ -9,8 +9,8 @@ package io.element.android.features.login.impl.screens.loginpassword import android.os.Parcelable -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt index ba48e57b5d..bae4ed4df3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordStatePreviewParam.kt @@ -9,9 +9,9 @@ package io.element.android.features.login.impl.screens.loginpassword import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.accountprovider.AccountProvider -import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.accountprovider.anAccountProviderManaged import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.core.SessionId open class LoginPasswordStatePreviewParam : PreviewParameterProvider { @@ -26,7 +26,7 @@ open class LoginPasswordStatePreviewParam : PreviewParameterProvider = AsyncData.Uninitialized, eventSink: (LoginPasswordEvent) -> Unit = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 9a2dc3097b..c2a983a692 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -131,7 +131,7 @@ fun LoginPasswordView( iconStyle = BigIcon.Style.Default(CompoundIcons.UserProfileSolid()), title = stringResource( id = R.string.screen_account_provider_signin_title, - state.accountProvider.title + state.accountProvider.friendlyServerName() ), subTitle = stringResource(id = R.string.screen_login_subtitle) ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt index 4b3993e2d1..6d5979946d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingEvent.kt @@ -8,9 +8,11 @@ package io.element.android.features.login.impl.screens.onboarding +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider + sealed interface OnBoardingEvent { data class OnSignIn( - val defaultAccountProvider: String + val defaultAccountProvider: AccountProvider ) : OnBoardingEvent data object OnVersionClick : OnBoardingEvent diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt index 7e48ae22e8..84db4283b4 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt @@ -22,7 +22,6 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.appconfig.OnBoardingConfig import io.element.android.features.enterprise.api.EnterpriseService -import io.element.android.features.enterprise.api.canConnectToAnyHomeserver import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.login.LoginModeEvent @@ -31,9 +30,11 @@ import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildType +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.ui.utils.MultipleTapToUnlock import kotlinx.coroutines.launch +import timber.log.Timber @AssistedInject class OnBoardingPresenter( @@ -59,27 +60,35 @@ class OnBoardingPresenter( @Composable override fun present(): OnBoardingState { val localCoroutineScope = rememberCoroutineScope() - val forcedAccountProvider = remember { - // If homeserverAllowList() returns a singleton list, this is the default account provider. - // In this case, the user can sign in using this homeserver, or use QrCode login - enterpriseService.homeserverAllowList().singleOrNull() + val canConnectToAnyAccountProvider = remember { + enterpriseService.canConnectToAnyAccountProvider() } - val canConnectToAnyHomeserver = remember { - enterpriseService.canConnectToAnyHomeserver() + val forcedAccountProvider = remember { + // If accountProviderAllowList() returns a singleton list, and the user is not free to use another + // account provider, this is the default account provider. + // In this case, the user can sign in using this homeserver, or use QrCode login + if (canConnectToAnyAccountProvider) { + null + } else { + enterpriseService.accountProviderAllowList().singleOrNull() + } } val mustChooseAccountProvider = remember { - !canConnectToAnyHomeserver && enterpriseService.homeserverAllowList().size > 1 + !canConnectToAnyAccountProvider && enterpriseService.accountProviderAllowList().size > 1 } - val linkAccountProvider by produceState(initialValue = null) { + val linkAccountProvider by produceState(initialValue = null) { // Account provider from the link, if allowed by the enterprise service - value = params.accountProvider?.takeIf { - try { - defaultAccountProviderAccessControl.assertIsAllowedToConnectToAccountProvider(it, it) - true - } catch (_: Exception) { - false + value = params.accountProvider + ?.let { AccountProvider.Generic(it) } + ?.takeIf { + try { + defaultAccountProviderAccessControl.assertIsAllowedToConnectToAccountProvider(it) + true + } catch (e: Exception) { + Timber.e(e, "Failed to check account provider from link, assuming not allowed") + false + } } - } } val defaultAccountProvider = remember(linkAccountProvider) { // If there is a forced account provider, this is the default account provider @@ -105,11 +114,11 @@ class OnBoardingPresenter( when (event) { is OnBoardingEvent.OnSignIn -> localCoroutineScope.launch { // Ensure that the current account provider is set - accountProviderDataSource.setUrl(event.defaultAccountProvider) + accountProviderDataSource.setAccountProvider(event.defaultAccountProvider) loginModeState.eventSink( LoginModeEvent.Submit( isAccountCreation = false, - homeserverUrl = event.defaultAccountProvider, + homeserverUrl = event.defaultAccountProvider.serverNameOrBaseUrl(), resolvedHomeserverUrl = null, loginHint = params.loginHint?.takeIf { forcedAccountProvider == null }, ) @@ -134,7 +143,7 @@ class OnBoardingPresenter( defaultAccountProvider = defaultAccountProvider, mustChooseAccountProvider = mustChooseAccountProvider, canLoginWithQrCode = canLoginWithQrCode, - canCreateAccount = defaultAccountProvider == null && canConnectToAnyHomeserver && OnBoardingConfig.CAN_CREATE_ACCOUNT, + canCreateAccount = defaultAccountProvider == null && canConnectToAnyAccountProvider && OnBoardingConfig.CAN_CREATE_ACCOUNT, canReportBug = canReportBug && showReportBug, loginModeState = loginModeState, version = buildMeta.versionName, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt index 97ebed67f6..87d4605ceb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingState.kt @@ -11,13 +11,14 @@ package io.element.android.features.login.impl.screens.onboarding import androidx.annotation.DrawableRes import io.element.android.features.login.impl.login.LoginModeState import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider data class OnBoardingState( val isAddingAccount: Boolean, val showBackButton: Boolean, val showDeveloperSettings: Boolean, val productionApplicationName: String, - val defaultAccountProvider: String?, + val defaultAccountProvider: AccountProvider?, val mustChooseAccountProvider: Boolean, val canLoginWithQrCode: Boolean, val canCreateAccount: Boolean, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt index c7d89b21d5..09a2c8a245 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingStatePreviewParam.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.login.impl.login.LoginModeState import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.designsystem.R +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider open class OnBoardingStatePreviewParam : PreviewParameterProvider { override val values: Sequence @@ -22,7 +23,11 @@ open class OnBoardingStatePreviewParam : PreviewParameterProvider - - - diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt index fbfae984fc..86bbd0eadb 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accesscontrol/DefaultAccountProviderAccessControlTest.kt @@ -11,19 +11,30 @@ package io.element.android.features.login.impl.accesscontrol import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.changeserver.AccountProviderAccessException +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_URL +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Test class DefaultAccountProviderAccessControlTest { + /** + * The account provider the assertions are run against: its server name is the title reported by the exceptions, + * its base url is what the enterprise service is queried with. + */ + private fun anAccountProviderUnderTest() = anAccountProviderManaged( + serverName = AN_ACCOUNT_PROVIDER, + baseUrl = AN_ACCOUNT_PROVIDER_URL, + ) + @Test fun `foss build should not allow using account provider that enforce enterprise build`() { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, enterpriseService = FakeEnterpriseService(isElementProEnforcedResult = { true }), ) accessControl.expectNeedElementProException() @@ -34,7 +45,7 @@ class DefaultAccountProviderAccessControlTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, // false here. - isAllowedToConnectToHomeserver = false, + isAllowedToConnectToAccountProvider = false, enterpriseService = FakeEnterpriseService(isElementProEnforcedResult = { true }), ) accessControl.expectNeedElementProException() @@ -44,9 +55,9 @@ class DefaultAccountProviderAccessControlTest { fun `foss build should allow using account provider that does not enforce enterprise build`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false } ), ) @@ -57,7 +68,7 @@ class DefaultAccountProviderAccessControlTest { fun `foss build should allow using account provider twith missing key in wellknown`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, ) accessControl.expectAllowed() } @@ -66,7 +77,7 @@ class DefaultAccountProviderAccessControlTest { fun `foss build should allow using account provider with missing wellknown`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, ) accessControl.expectAllowed() } @@ -75,12 +86,12 @@ class DefaultAccountProviderAccessControlTest { fun `foss build should not allow using account provider that do not enforce enterprise build but is not allowed`() { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = false, - isAllowedToConnectToHomeserver = false, - allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), + isAllowedToConnectToAccountProvider = false, + allowedAccountProviders = listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)), enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { false }, + isAllowedToConnectToAccountProviderResult = { false }, isElementProEnforcedResult = { false }, - defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -90,9 +101,9 @@ class DefaultAccountProviderAccessControlTest { fun `enterprise build should allow using account provider that enforce enterprise build`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { true }, ), ) @@ -103,9 +114,9 @@ class DefaultAccountProviderAccessControlTest { fun `enterprise build should allow using account provider that do not enforce enterprise build`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, - isAllowedToConnectToHomeserver = true, + isAllowedToConnectToAccountProvider = true, enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), ) @@ -116,12 +127,12 @@ class DefaultAccountProviderAccessControlTest { fun `enterprise build should not allow using account provider that enforce enterprise build but is not allowed`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, - isAllowedToConnectToHomeserver = false, - allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), + isAllowedToConnectToAccountProvider = false, + allowedAccountProviders = listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)), enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { false }, + isAllowedToConnectToAccountProviderResult = { false }, isElementProEnforcedResult = { true }, - defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -131,12 +142,12 @@ class DefaultAccountProviderAccessControlTest { fun `enterprise build should not allow using account provider that do not enforce enterprise build but is not allowed`() = runTest { val accessControl = createDefaultAccountProviderAccessControl( isEnterpriseBuild = true, - isAllowedToConnectToHomeserver = false, - allowedAccountProviders = listOf(AN_ACCOUNT_PROVIDER_2), + isAllowedToConnectToAccountProvider = false, + allowedAccountProviders = listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)), enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { false }, + isAllowedToConnectToAccountProviderResult = { false }, isElementProEnforcedResult = { false }, - defaultHomeserverListResult = { listOf(AN_ACCOUNT_PROVIDER_2) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = AN_ACCOUNT_PROVIDER_2)) }, ), ) accessControl.expectUnauthorizedAccountProviderException() @@ -144,11 +155,11 @@ class DefaultAccountProviderAccessControlTest { private fun createDefaultAccountProviderAccessControl( isEnterpriseBuild: Boolean = false, - isAllowedToConnectToHomeserver: Boolean = false, - allowedAccountProviders: List = emptyList(), + isAllowedToConnectToAccountProvider: Boolean = false, + allowedAccountProviders: List = emptyList(), enterpriseService: FakeEnterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { isAllowedToConnectToHomeserver }, - defaultHomeserverListResult = { allowedAccountProviders }, + isAllowedToConnectToAccountProviderResult = { isAllowedToConnectToAccountProvider }, + accountProviderAllowListResult = { allowedAccountProviders }, isElementProEnforcedResult = { false }, ) ) = DefaultAccountProviderAccessControl( @@ -159,19 +170,14 @@ class DefaultAccountProviderAccessControlTest { private fun DefaultAccountProviderAccessControl.expectNeedElementProException() { val exception = assertThrows(AccountProviderAccessException.NeedElementProException::class.java) { runTest { - assertIsAllowedToConnectToAccountProvider( - title = AN_ACCOUNT_PROVIDER, - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + assertIsAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) } } assertThat(exception.unauthorisedAccountProviderTitle).isEqualTo(AN_ACCOUNT_PROVIDER) assertThat(exception.applicationId).isEqualTo("io.element.enterprise") runTest { assertThat( - isAllowedToConnectToAccountProvider( - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + isAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) ).isFalse() } } @@ -179,34 +185,24 @@ class DefaultAccountProviderAccessControlTest { private fun DefaultAccountProviderAccessControl.expectUnauthorizedAccountProviderException() { val exception = assertThrows(AccountProviderAccessException.UnauthorizedAccountProviderException::class.java) { runTest { - assertIsAllowedToConnectToAccountProvider( - title = AN_ACCOUNT_PROVIDER, - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + assertIsAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) } } assertThat(exception.unauthorisedAccountProviderTitle).isEqualTo(AN_ACCOUNT_PROVIDER) assertThat(exception.authorisedAccountProviderTitles).containsExactly(AN_ACCOUNT_PROVIDER_2) runTest { assertThat( - isAllowedToConnectToAccountProvider( - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + isAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) ).isFalse() } } private suspend fun DefaultAccountProviderAccessControl.expectAllowed() { // If no exception is thrown, the test passes - assertIsAllowedToConnectToAccountProvider( - title = AN_ACCOUNT_PROVIDER, - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + assertIsAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) runTest { assertThat( - isAllowedToConnectToAccountProvider( - accountProviderUrl = AN_ACCOUNT_PROVIDER_URL, - ) + isAllowedToConnectToAccountProvider(anAccountProviderUnderTest()) ).isTrue() } } 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 index 30e25c8c3e..ff03d79989 100644 --- 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 @@ -10,9 +10,10 @@ 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.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.matrixOrgAccountProvider +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest @@ -24,61 +25,24 @@ class AccountProviderDataSourceTest { val warmUpRule = WarmUpRule() @Test - fun `present - initial state`() = runTest { + fun `present - initial state - matrix org is the default when nothing is configured`() = runTest { val sut = anAccountProviderDataSource() sut.flow.test { - val initialState = awaitItem() - assertThat(initialState).isEqualTo( - AccountProvider( - url = AuthenticationConfig.MATRIX_ORG_URL, - title = "matrix.org", - subtitle = null, - isPublic = true, - isMatrixOrg = true, - ) - ) + assertThat(awaitItem()).isEqualTo(matrixOrgAccountProvider) } } @Test - fun `present - initial state - matrix org`() = runTest { + fun `present - initial state - the first configured account provider is the default`() = runTest { val sut = anAccountProviderDataSource( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(AuthenticationConfig.MATRIX_ORG_URL) } + accountProviderAllowListResult = { + listOf(anAccountProviderManaged(serverName = "first.org"), anAccountProviderManaged(serverName = "second.org")) + } ), ) sut.flow.test { - val initialState = awaitItem() - assertThat(initialState).isEqualTo( - AccountProvider( - url = AuthenticationConfig.MATRIX_ORG_URL, - title = "matrix.org", - subtitle = null, - isPublic = true, - isMatrixOrg = true, - ) - ) - } - } - - @Test - fun `present - ensure that default homeserver is not star char`() = runTest { - val sut = anAccountProviderDataSource( - enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(EnterpriseService.ANY_ACCOUNT_PROVIDER, 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, - ) - ) + assertThat(awaitItem()).isEqualTo(anAccountProviderManaged(serverName = "first.org")) } } @@ -86,22 +50,11 @@ class AccountProviderDataSourceTest { fun `present - user change and reset`() = runTest { val sut = anAccountProviderDataSource() sut.flow.test { - val initialState = awaitItem() - assertThat(initialState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) - sut.setAccountProvider(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, - ) - ) + assertThat(awaitItem()).isEqualTo(matrixOrgAccountProvider) + sut.setAccountProvider(AccountProvider.Generic("https://example.com")) + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://example.com")) sut.reset() - val resetState = awaitItem() - assertThat(resetState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(awaitItem()).isEqualTo(matrixOrgAccountProvider) } } @@ -109,22 +62,23 @@ class AccountProviderDataSourceTest { fun `present - set url and reset`() = runTest { val sut = anAccountProviderDataSource() sut.flow.test { - val initialState = awaitItem() - assertThat(initialState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(awaitItem()).isEqualTo(matrixOrgAccountProvider) sut.setUrl(url = "https://example.com") - val changedState = awaitItem() - assertThat(changedState).isEqualTo( - AccountProvider( - url = "https://example.com", - title = "example.com", - subtitle = null, - isPublic = false, - isMatrixOrg = false, - ) + assertThat(awaitItem()).isEqualTo( + AccountProvider.Generic("https://example.com") ) sut.reset() - val resetState = awaitItem() - assertThat(resetState.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(awaitItem()).isEqualTo(matrixOrgAccountProvider) + } + } + + @Test + fun `present - a provider set by the user is stored as they input it`() = runTest { + val sut = anAccountProviderDataSource() + sut.flow.test { + skipItems(1) + sut.setUrl(url = "example.com") + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("example.com")) } } @@ -136,7 +90,7 @@ class AccountProviderDataSourceTest { ), ) sut.flow.test { - assertThat(awaitItem().url).isEqualTo("https://example.com") + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://example.com")) } } @@ -144,14 +98,31 @@ class AccountProviderDataSourceTest { fun `present - history is ignored when the account provider is enforced`() = runTest { val sut = anAccountProviderDataSource( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf("https://enforced.org") } + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = "enforced.org")) }, + canConnectToAnyAccountProviderResult = { false }, ), appPreferencesStore = InMemoryAppPreferencesStore( homeserverHistory = listOf("https://example.com"), ), ) sut.flow.test { - assertThat(awaitItem().url).isEqualTo("https://enforced.org") + assertThat(awaitItem()).isEqualTo(anAccountProviderManaged(serverName = "enforced.org")) + } + } + + @Test + fun `present - history wins over the configured provider when other providers are allowed`() = runTest { + val sut = anAccountProviderDataSource( + enterpriseService = FakeEnterpriseService( + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = "preferred.org")) }, + canConnectToAnyAccountProviderResult = { true }, + ), + appPreferencesStore = InMemoryAppPreferencesStore( + homeserverHistory = listOf("https://example.com"), + ), + ) + sut.flow.test { + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://example.com")) } } @@ -163,11 +134,11 @@ class AccountProviderDataSourceTest { ), ) sut.flow.test { - assertThat(awaitItem().url).isEqualTo("https://example.com") + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://example.com")) sut.setUrl("https://other.com") - assertThat(awaitItem().url).isEqualTo("https://other.com") + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://other.com")) sut.reset() - assertThat(awaitItem().url).isEqualTo("https://example.com") + assertThat(awaitItem()).isEqualTo(AccountProvider.Generic("https://example.com")) } } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt index 0af8188f3f..604e01acfe 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/SaveAccountProviderToHistoryTest.kt @@ -20,7 +20,7 @@ class SaveAccountProviderToHistoryTest { val warmUpRule = WarmUpRule() @Test - fun `invoke saves the current account provider url to history`() = runTest { + fun `invoke saves the current account provider server name to history`() = runTest { val appPreferencesStore = InMemoryAppPreferencesStore() val accountProviderDataSource = anAccountProviderDataSource() accountProviderDataSource.setUrl("https://example.com") @@ -29,7 +29,7 @@ class SaveAccountProviderToHistoryTest { sut() assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) - .containsExactly("https://example.com") + .containsExactly("example.com") } @Test @@ -45,6 +45,6 @@ class SaveAccountProviderToHistoryTest { sut() assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) - .containsExactly("https://example.com") + .containsExactly("example.com") } } 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 44a3bbd5cb..f43a575d37 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 @@ -12,14 +12,15 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissionGate import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialog @@ -57,13 +58,13 @@ class ChangeServerPresenterTest { createPresenter( authenticationService = authenticationService, enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val successState = awaitItem() @@ -80,14 +81,14 @@ class ChangeServerPresenterTest { ) createPresenter( enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), authenticationService = authenticationService, ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() @@ -108,14 +109,14 @@ class ChangeServerPresenterTest { ) createPresenter( enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), authenticationService = authenticationService, ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() @@ -128,30 +129,30 @@ class ChangeServerPresenterTest { @Test fun `present - change server not allowed error`() = runTest { - val isAllowedToConnectToHomeserverResult = lambdaRecorder { false } + val isAllowedToConnectToAccountProviderResult = lambdaRecorder { false } createPresenter( enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = isAllowedToConnectToHomeserverResult, - defaultHomeserverListResult = { listOf("element.io") }, + isAllowedToConnectToAccountProviderResult = isAllowedToConnectToAccountProviderResult, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = "element.io")) }, isElementProEnforcedResult = { false }, ), ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) + val anAccountProvider = AccountProvider.Generic(A_HOMESERVER_URL) initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(anAccountProvider)) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) val failureState = awaitItem() assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.UnauthorizedAccountProvider).unauthorisedAccountProviderTitle - ).isEqualTo(anAccountProvider.title) + ).isEqualTo(anAccountProvider.serverNameOrBaseUrl()) assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.UnauthorizedAccountProvider).authorisedAccountProviderTitles ).containsExactly("element.io") - isAllowedToConnectToHomeserverResult.assertions() + isAllowedToConnectToAccountProviderResult.assertions() .isCalledOnce() - .with(value(A_HOMESERVER_URL)) + .with(value(AccountProvider.Generic(A_HOMESERVER_URL))) } } @@ -166,14 +167,14 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - val anAccountProvider = AccountProvider(url = A_HOMESERVER_URL) + val anAccountProvider = AccountProvider.Generic(A_HOMESERVER_URL) initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(anAccountProvider)) // Skip loading state skipItems(1) val failureState = awaitItem() assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).unauthorisedAccountProviderTitle - ).isEqualTo(anAccountProvider.title) + ).isEqualTo(anAccountProvider.serverNameOrBaseUrl()) assertThat( (failureState.changeServerAction.errorOrNull() as ChangeServerError.NeedElementPro).applicationId ).isEqualTo("io.element.enterprise") @@ -188,13 +189,13 @@ class ChangeServerPresenterTest { ) createPresenter( authenticationService = authenticationService, - enterpriseService = FakeEnterpriseService(isAllowedToConnectToHomeserverResult = { true }), + enterpriseService = FakeEnterpriseService(isAllowedToConnectToAccountProviderResult = { true }), localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(shouldPrompt = true), permissionsPresenter = FakePermissionsPresenter(), ).test { val initialState = awaitItem() assertThat(initialState.localNetworkPermissionDialog).isEqualTo(LocalNetworkPermissionDialog.None) - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val promptState = expectMostRecentItem() // Dialog is shown, permission has not been requested yet, setHomeserver has not been called. assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) @@ -208,12 +209,12 @@ class ChangeServerPresenterTest { authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ), - enterpriseService = FakeEnterpriseService(isAllowedToConnectToHomeserverResult = { true }), + enterpriseService = FakeEnterpriseService(isAllowedToConnectToAccountProviderResult = { true }), localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(shouldPrompt = true), permissionsPresenter = FakePermissionsPresenter(), ).test { val initialState = awaitItem() - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val promptState = expectMostRecentItem() assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) promptState.eventSink.invoke(ChangeServerEvent.DismissLocalNetworkPermission) @@ -231,12 +232,12 @@ class ChangeServerPresenterTest { val permissionsPresenter = FakePermissionsPresenter() createPresenter( authenticationService = authenticationService, - enterpriseService = FakeEnterpriseService(isAllowedToConnectToHomeserverResult = { true }, isElementProEnforcedResult = { false }), + enterpriseService = FakeEnterpriseService(isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }), localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(shouldPrompt = true), permissionsPresenter = permissionsPresenter, ).test { val initialState = awaitItem() - initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + initialState.eventSink.invoke(ChangeServerEvent.ChangeServer(AccountProvider.Generic(A_HOMESERVER_URL))) val promptState = expectMostRecentItem() assertThat(promptState.localNetworkPermissionDialog).isNotEqualTo(LocalNetworkPermissionDialog.None) permissionsPresenter.setPermissionGranted() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt index 78f4293489..17711cc469 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt @@ -11,14 +11,13 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.login.LoginModePresenter import io.element.android.features.login.impl.screens.onboarding.createLoginModePresenter import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_3 import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test @@ -33,18 +32,8 @@ class ChooseAccountProviderPresenterTest { companion object { private const val ACCOUNT_PROVIDER_FROM_CONFIG_1 = AN_ACCOUNT_PROVIDER_2 private const val ACCOUNT_PROVIDER_FROM_CONFIG_2 = AN_ACCOUNT_PROVIDER_3 - val accountProvider1 = AccountProvider( - url = ACCOUNT_PROVIDER_FROM_CONFIG_1.ensureProtocol(), - subtitle = null, - isPublic = false, - isMatrixOrg = false, - ) - val accountProvider2 = AccountProvider( - url = ACCOUNT_PROVIDER_FROM_CONFIG_2.ensureProtocol(), - subtitle = null, - isPublic = false, - isMatrixOrg = false, - ) + val accountProvider1 = anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG_1) + val accountProvider2 = anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG_2) } @Test @@ -61,7 +50,7 @@ class ChooseAccountProviderPresenterTest { fun `present - initial state`() = runTest { val presenter = createPresenter( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG_1, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, + accountProviderAllowListResult = { listOf(accountProvider1, accountProvider2) }, ), ) presenter.test { @@ -79,7 +68,7 @@ class ChooseAccountProviderPresenterTest { val authenticationService = FakeMatrixAuthenticationService() val presenter = createPresenter( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG_1, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, + accountProviderAllowListResult = { listOf(accountProvider1, accountProvider2) }, ), loginModePresenter = createLoginModePresenter( authenticationService = authenticationService, @@ -103,7 +92,7 @@ class ChooseAccountProviderPresenterTest { ) val presenter = createPresenter( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG_1, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, + accountProviderAllowListResult = { listOf(accountProvider1, accountProvider2) }, ), loginModePresenter = createLoginModePresenter( authenticationService = authenticationService, @@ -136,7 +125,7 @@ class ChooseAccountProviderPresenterTest { val authenticationService = FakeMatrixAuthenticationService() val presenter = createPresenter( enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG_1, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, + accountProviderAllowListResult = { listOf(accountProvider1, accountProvider2) }, ), loginModePresenter = createLoginModePresenter( authenticationService = authenticationService, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateTest.kt index a5cb54e7e1..c4af8f9544 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderStateTest.kt @@ -9,7 +9,7 @@ package io.element.android.features.login.impl.screens.chooseaccountprovider import com.google.common.truth.Truth.assertThat -import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.accountprovider.anAccountProviderManaged import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.architecture.AsyncData @@ -27,7 +27,7 @@ class ChooseAccountProviderStateTest { @Test fun `submitEnabled returns true when there is a selectedAccountProvider`() { val sut = aChooseAccountProviderState( - selectedAccountProvider = anAccountProvider(), + selectedAccountProvider = anAccountProviderManaged(), ) assertThat(sut.submitEnabled).isTrue() } @@ -35,7 +35,7 @@ class ChooseAccountProviderStateTest { @Test fun `submitEnabled returns false when there is a selectedAccountProvider but there is an error`() { val sut = aChooseAccountProviderState( - selectedAccountProvider = anAccountProvider(), + selectedAccountProvider = anAccountProviderManaged(), loginModeState = aLoginModeState(loginMode = AsyncData.Failure(Throwable("Error"))), ) assertThat(sut.submitEnabled).isFalse() @@ -44,7 +44,7 @@ class ChooseAccountProviderStateTest { @Test fun `submitEnabled returns false when there is a selectedAccountProvider but the result is successful`() { val sut = aChooseAccountProviderState( - selectedAccountProvider = anAccountProvider(), + selectedAccountProvider = anAccountProviderManaged(), loginModeState = aLoginModeState(loginMode = AsyncData.Success(LoginMode.PasswordLogin)), ) assertThat(sut.submitEnabled).isFalse() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt index 4ef7a71966..9bb8da717e 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import io.element.android.features.login.impl.accountprovider.anAccountProvider +import io.element.android.features.login.impl.accountprovider.anAccountProviderManaged import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.OAuthDetails @@ -57,11 +57,11 @@ class ChooseAccountProviderViewTest : RobolectricTest() { ChooseAccountProviderPresenterTest.accountProvider1, ChooseAccountProviderPresenterTest.accountProvider2, ), - selectedAccountProvider = anAccountProvider(), + selectedAccountProvider = anAccountProviderManaged(), eventSink = eventSink, ), ) - onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick() + onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.friendlyServerName()).performClick() eventSink.assertSingle(ChooseAccountProviderEvent.SelectAccountProvider(ChooseAccountProviderPresenterTest.accountProvider1)) } 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 19afbe0f28..9bccb2c261 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 @@ -10,7 +10,6 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat -import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl @@ -24,6 +23,7 @@ import io.element.android.features.login.impl.screens.onboarding.createLoginMode import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.oauth.api.OAuthAction @@ -44,6 +44,11 @@ class ConfirmAccountProviderPresenterTest { @get:Rule val warmUpRule = WarmUpRule() + companion object { + private const val MATRIX_ORG_SERVER_NAME = "matrix.org" + private const val MATRIX_ORG_URL = "https://matrix.org" + } + @Test fun `present - initial test`() = runTest { val presenter = createConfirmAccountProviderPresenter() @@ -68,7 +73,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isEqualTo(LoginMode.PasswordLogin) cancelAndIgnoreRemainingEvents() @@ -87,7 +92,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) cancelAndIgnoreRemainingEvents() @@ -108,7 +113,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenOAuthCancelError(AN_EXCEPTION) @@ -133,7 +138,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOAuthActionFlow.post(OAuthAction.GoBack()) @@ -157,7 +162,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) awaitLoginMode { it is AsyncData.Loading } defaultOAuthActionFlow.post(OAuthAction.GoBack(toUnblock = true)) val cancelFinalState = awaitLoginMode { it is AsyncData.Uninitialized } @@ -180,7 +185,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenLoginError(AN_EXCEPTION) @@ -205,7 +210,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val successState = awaitLoginMode { it is AsyncData.Success } assertThat(successState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) defaultOidcActionFlow.post(OAuthAction.Success("aUrl")) @@ -227,7 +232,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink.invoke(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) // The account provider validation fails, so the login is never attempted. val failureState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } assertThat(failureState.loginModeState.loginMode).isEqualTo(AsyncData.Uninitialized) @@ -249,7 +254,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() // Submit will return an error while validating the account provider - initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) // Check an error was returned val submittedState = awaitState { it.changeServerState.changeServerAction is AsyncData.Failure } @@ -274,7 +279,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) // Check an error was returned val submittedState = awaitLoginMode { it is AsyncData.Failure } assertThat(submittedState.loginModeState.loginMode.errorOrNull()).isInstanceOf(AccountCreationNotSupported::class.java) @@ -299,7 +304,7 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) val submittedState = awaitLoginMode { it is AsyncData.Success } assertThat(submittedState.loginModeState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) cancelAndIgnoreRemainingEvents() @@ -344,7 +349,7 @@ class ConfirmAccountProviderPresenterTest { val suggestionState = awaitState { it.accountProviderSuggestion != null } suggestionState.eventSink(ConfirmAccountProviderEvent.Continue("randomcommunity.org")) awaitLoginMode { it is AsyncData.Success } - assertThat(submittedUrls.first()).isEqualTo("https://randomcommunity.org") + assertThat(submittedUrls.first()).isEqualTo("randomcommunity.org") cancelAndIgnoreRemainingEvents() } } @@ -375,8 +380,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( appPreferencesStore = InMemoryAppPreferencesStore(homeserverHistory = emptyList()), enterpriseService = FakeEnterpriseService( - // Preferred servers plus the "*" (any) wildcard: the wildcard is filtered out, the rest complete. - defaultHomeserverListResult = { listOf("https://element.io", EnterpriseService.ANY_ACCOUNT_PROVIDER) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = "element.io")) }, ), ) presenter.test { @@ -387,6 +391,19 @@ class ConfirmAccountProviderPresenterTest { } } + @Test + fun `present - offers the previously used servers without the scheme as autocomplete suggestions`() = runTest { + val presenter = createConfirmAccountProviderPresenter( + appPreferencesStore = InMemoryAppPreferencesStore(homeserverHistory = listOf("https://previous.org")), + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink(ConfirmAccountProviderEvent.UserInputChanged("prev")) + val suggestionState = awaitState { it.accountProviderInput == "prev" } + assertThat(suggestionState.accountProviderSuggestion).isEqualTo("previous.org") + } + } + @Test fun `present - offers matrix_org without the scheme as an autocomplete suggestion`() = runTest { val presenter = createConfirmAccountProviderPresenter( @@ -432,7 +449,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvent.Continue("matrix.org")) awaitLoginMode { it is AsyncData.Success } - assertThat(submittedUrls.first()).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(submittedUrls.first()).isEqualTo(MATRIX_ORG_SERVER_NAME) cancelAndIgnoreRemainingEvents() } } @@ -453,9 +470,9 @@ class ConfirmAccountProviderPresenterTest { ) presenter.test { val initialState = awaitItem() - initialState.eventSink(ConfirmAccountProviderEvent.Continue(AuthenticationConfig.MATRIX_ORG_URL)) + initialState.eventSink(ConfirmAccountProviderEvent.Continue(MATRIX_ORG_URL)) awaitLoginMode { it is AsyncData.Success } - assertThat(submittedUrls).containsExactly(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(submittedUrls).containsExactly(MATRIX_ORG_SERVER_NAME) cancelAndIgnoreRemainingEvents() } } @@ -474,7 +491,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvent.Continue("@alice:example.org")) awaitLoginMode { it is AsyncData.Success } - assertThat(submittedUrls.first()).isEqualTo("https://example.org") + assertThat(submittedUrls.first()).isEqualTo("example.org") assertThat(authenticationService.getOAuthUrlLoginHint).isEqualTo("mxid:@alice:example.org") cancelAndIgnoreRemainingEvents() } @@ -494,7 +511,7 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvent.Continue("@alice:example.org:8448")) awaitLoginMode { it is AsyncData.Success } - assertThat(submittedUrls.first()).isEqualTo("https://example.org:8448") + assertThat(submittedUrls.first()).isEqualTo("example.org:8448") cancelAndIgnoreRemainingEvents() } } @@ -570,7 +587,7 @@ class ConfirmAccountProviderPresenterTest { accountProviderDataSource = accountProviderDataSource, defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl( enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), isEnterpriseBuild = { false }, 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 b12c9ec667..015ece9e23 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 @@ -9,11 +9,11 @@ package io.element.android.features.login.impl.screens.loginpassword import com.google.common.truth.Truth.assertThat -import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.accountprovider.SaveAccountProviderToHistory import io.element.android.features.login.impl.accountprovider.anAccountProviderDataSource import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.matrixOrgAccountProvider import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_PASSWORD @@ -39,7 +39,7 @@ class LoginPasswordPresenterTest { fun `present - initial state`() = runTest { createLoginPasswordPresenter().test { val initialState = awaitItem() - assertThat(initialState.accountProvider.url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) + assertThat(initialState.accountProvider).isEqualTo(matrixOrgAccountProvider) assertThat(initialState.formState).isEqualTo(LoginFormState.Default) assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.submitEnabled).isFalse() @@ -127,7 +127,7 @@ class LoginPasswordPresenterTest { val loggedInState = awaitItem() assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Success(A_SESSION_ID)) assertThat(appPreferencesStore.getHomeserverHistoryFlow().first()) - .containsExactly(AuthenticationConfig.MATRIX_ORG_URL) + .containsExactly(matrixOrgAccountProvider.serverName) } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 1088a87538..4fecc3ada8 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -9,7 +9,6 @@ package io.element.android.features.login.impl.screens.onboarding import com.google.common.truth.Truth.assertThat -import io.element.android.appconfig.AuthenticationConfig import io.element.android.appconfig.OnBoardingConfig import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.IsEnterpriseBuild @@ -22,6 +21,8 @@ import io.element.android.features.login.impl.localnetwork.LocalNetworkPermissio import io.element.android.features.login.impl.login.LoginModePresenter import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider +import io.element.android.libraries.matrix.api.accountprovider.matrixOrgAccountProvider import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 @@ -30,6 +31,7 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_LOGIN_HINT +import io.element.android.libraries.matrix.test.accountprovider.anAccountProviderManaged import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.oauth.api.OAuthActionFlow @@ -82,7 +84,8 @@ class OnBoardingPresenterTest { val presenter = createPresenter( buildMeta = buildMeta, enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG)) }, + canConnectToAnyAccountProviderResult = { true }, ), ) presenter.test { @@ -184,15 +187,16 @@ class OnBoardingPresenterTest { showBackButton = false, ), enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) }, - isAllowedToConnectToHomeserverResult = { true }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG)) }, + canConnectToAnyAccountProviderResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), ) presenter.test { skipItems(3) awaitItem().also { - assertThat(it.defaultAccountProvider).isEqualTo(ACCOUNT_PROVIDER_FROM_LINK) + assertThat(it.defaultAccountProvider).isEqualTo(AccountProvider.Generic(ACCOUNT_PROVIDER_FROM_LINK)) assertThat(it.canLoginWithQrCode).isFalse() assertThat(it.canCreateAccount).isFalse() } @@ -208,8 +212,14 @@ class OnBoardingPresenterTest { showBackButton = false, ), enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, - isAllowedToConnectToHomeserverResult = { false }, + accountProviderAllowListResult = { + listOf( + anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG), + anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG_2) + ) + }, + canConnectToAnyAccountProviderResult = { false }, + isAllowedToConnectToAccountProviderResult = { false }, ), ) presenter.test { @@ -231,19 +241,60 @@ class OnBoardingPresenterTest { showBackButton = false, ), enterpriseService = FakeEnterpriseService( - defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG) }, + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG)) }, + canConnectToAnyAccountProviderResult = { false }, ) ) presenter.test { skipItems(1) awaitItem().also { - assertThat(it.defaultAccountProvider).isEqualTo(ACCOUNT_PROVIDER_FROM_CONFIG) + assertThat(it.defaultAccountProvider).isEqualTo(anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG)) assertThat(it.canLoginWithQrCode).isTrue() assertThat(it.canCreateAccount).isFalse() } } } + @Test + fun `present - a single configured account provider is not forced when the user may use another one`() = runTest { + // A singleton allow list only forces the account provider when the user is not free to use another one. + val presenter = createPresenter( + enterpriseService = FakeEnterpriseService( + accountProviderAllowListResult = { listOf(anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG)) }, + canConnectToAnyAccountProviderResult = { true }, + ), + ) + presenter.test { + awaitItem().also { + assertThat(it.defaultAccountProvider).isNull() + assertThat(it.mustChooseAccountProvider).isFalse() + } + skipItems(1) + } + } + + @Test + fun `present - the user must choose when several account providers are configured and enforced`() = runTest { + val presenter = createPresenter( + enterpriseService = FakeEnterpriseService( + accountProviderAllowListResult = { + listOf( + anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG), + anAccountProviderManaged(serverName = ACCOUNT_PROVIDER_FROM_CONFIG_2) + ) + }, + canConnectToAnyAccountProviderResult = { false }, + ), + ) + presenter.test { + awaitItem().also { + assertThat(it.defaultAccountProvider).isNull() + assertThat(it.mustChooseAccountProvider).isTrue() + } + skipItems(1) + } + } + @Test fun `present - default account provider - login and clear error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( @@ -259,7 +310,7 @@ class OnBoardingPresenterTest { showBackButton = false, ), enterpriseService = FakeEnterpriseService( - isAllowedToConnectToHomeserverResult = { true }, + isAllowedToConnectToAccountProviderResult = { true }, isElementProEnforcedResult = { false }, ), loginModePresenter = createLoginModePresenter( @@ -270,12 +321,12 @@ class OnBoardingPresenterTest { presenter.test { skipItems(3) awaitItem().also { - assertThat(it.defaultAccountProvider).isEqualTo(A_HOMESERVER_URL) - assertThat(accountProviderDataSource.flow.first().url).isEqualTo(AuthenticationConfig.MATRIX_ORG_URL) - it.eventSink(OnBoardingEvent.OnSignIn(A_HOMESERVER_URL_2)) + assertThat(it.defaultAccountProvider).isEqualTo(AccountProvider.Generic(A_HOMESERVER_URL)) + assertThat(accountProviderDataSource.flow.first()).isEqualTo(matrixOrgAccountProvider) + it.eventSink(OnBoardingEvent.OnSignIn(AccountProvider.Generic(A_HOMESERVER_URL_2))) skipItems(1) // Loading // Account data source has been updated - assertThat(accountProviderDataSource.flow.first().url).isEqualTo(A_HOMESERVER_URL_2) + assertThat(accountProviderDataSource.flow.first()).isEqualTo(AccountProvider.Generic(A_HOMESERVER_URL_2)) // Check an error was returned val submittedState = awaitItem() assertThat(submittedState.loginModeState.loginMode).isInstanceOf(AsyncData.Failure::class.java) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt index 88c9acf55f..c7cc2b9f03 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt @@ -23,6 +23,7 @@ import io.element.android.features.login.impl.R import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.login.aLoginModeState import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.ui.strings.CommonStrings @@ -134,13 +135,13 @@ class OnboardingViewTest : RobolectricTestParameter() { val eventSink = EventsRecorder() setOnboardingView( state = anOnBoardingState( - defaultAccountProvider = "element.io", + defaultAccountProvider = AccountProvider.Generic("element.io"), eventSink = eventSink, ), ) val buttonText = activity!!.getString(R.string.screen_onboarding_sign_in_to, "element.io") onNodeWithText(buttonText).performClick() - eventSink.assertSingle(OnBoardingEvent.OnSignIn("element.io")) + eventSink.assertSingle(OnBoardingEvent.OnSignIn(AccountProvider.Generic("element.io"))) } @Test @@ -148,7 +149,7 @@ class OnboardingViewTest : RobolectricTestParameter() { val eventSink = EventsRecorder() setOnboardingView( state = anOnBoardingState( - defaultAccountProvider = "element.io", + defaultAccountProvider = AccountProvider.Generic("element.io"), loginModeState = aLoginModeState(loginMode = AsyncData.Failure(AN_EXCEPTION)), eventSink = eventSink, ), diff --git a/features/login/test/build.gradle.kts b/features/login/test/build.gradle.kts index 4cbf0910db..41c231a594 100644 --- a/features/login/test/build.gradle.kts +++ b/features/login/test/build.gradle.kts @@ -16,6 +16,7 @@ android { dependencies { implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) implementation(projects.features.login.api) implementation(projects.tests.testutils) } diff --git a/features/login/test/src/main/kotlin/io/element/android/features/login/test/accesscontrol/FakeAccountProviderAccessControl.kt b/features/login/test/src/main/kotlin/io/element/android/features/login/test/accesscontrol/FakeAccountProviderAccessControl.kt index 9ebd3be7fc..2864669e7f 100644 --- a/features/login/test/src/main/kotlin/io/element/android/features/login/test/accesscontrol/FakeAccountProviderAccessControl.kt +++ b/features/login/test/src/main/kotlin/io/element/android/features/login/test/accesscontrol/FakeAccountProviderAccessControl.kt @@ -8,12 +8,13 @@ package io.element.android.features.login.test.accesscontrol import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider import io.element.android.tests.testutils.lambda.lambdaError class FakeAccountProviderAccessControl( - private val isAllowedToConnectToAccountProviderResult: (String) -> Boolean = { lambdaError() }, + private val isAllowedToConnectToAccountProviderResult: (AccountProvider) -> Boolean = { lambdaError() }, ) : AccountProviderAccessControl { - override suspend fun isAllowedToConnectToAccountProvider(accountProviderUrl: String): Boolean { - return isAllowedToConnectToAccountProviderResult(accountProviderUrl) + override suspend fun isAllowedToConnectToAccountProvider(accountProvider: AccountProvider): Boolean { + return isAllowedToConnectToAccountProviderResult(accountProvider) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 97d3465ce7..e0213f2fb5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -51,6 +51,7 @@ import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.ApplicationContext @@ -103,6 +104,7 @@ class MessagesNode( private val roomMemberModerationRenderer: RoomMemberModerationRenderer, private val eventContentValidationCache: EventContentValidationCache, private val emojiPickerRenderer: EmojiPickerRenderer, + private val dispatchers: CoroutineDispatchers, ) : Node(buildContext, plugins = plugins), MessagesNavigator { data class Inputs( val focusedEventId: EventId?, @@ -111,7 +113,12 @@ class MessagesNode( private val inputs = inputs() private val callback: Callback = callback() - private val timelineController = TimelineController(room, room.liveTimeline) + private val timelineController = TimelineController( + room = room, + liveTimeline = room.liveTimeline, + roomCoroutineScope = room.roomCoroutineScope, + dispatchers = dispatchers, + ) private val presenter = presenterFactory.create( navigator = this, composerPresenter = messageComposerPresenterFactory.create(timelineController, this, threadRoot = null), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index cf3c701015..b5542ec4be 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -54,6 +54,7 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.emoji.api.picker.EmojiPickerRenderer @@ -95,6 +96,7 @@ class ThreadedMessagesNode( private val appNavigationStateService: AppNavigationStateService, private val roomMemberModerationRenderer: RoomMemberModerationRenderer, private val emojiPickerRenderer: EmojiPickerRenderer, + private val dispatchers: CoroutineDispatchers, ) : Node(buildContext, plugins = plugins), MessagesNavigator { data class Inputs( val threadRootEventId: ThreadId, @@ -113,7 +115,12 @@ class ThreadedMessagesNode( */ private suspend fun createPresenter(): Presenter { val threadedTimeline = room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = inputs.threadRootEventId)).getOrThrow() - val timelineController = TimelineController(room, threadedTimeline) + val timelineController = TimelineController( + room, + liveTimeline = threadedTimeline, + roomCoroutineScope = room.roomCoroutineScope, + dispatchers = dispatchers, + ) this.timelineController = timelineController return presenterFactory.create( navigator = this, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt index e41ac7bee7..05f7a06054 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt @@ -12,7 +12,10 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn import dev.zacsweers.metro.binding import io.element.android.features.messages.impl.timeline.di.LiveTimeline +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.di.annotations.RoomCoroutineScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.room.CreateTimelineParams @@ -23,7 +26,6 @@ import io.element.android.libraries.matrix.api.timeline.TimelineProvider import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -47,8 +49,10 @@ import java.util.Optional class TimelineController( private val room: JoinedRoom, @LiveTimeline private val liveTimeline: Timeline, + @RoomCoroutineScope private val roomCoroutineScope: CoroutineScope, + dispatchers: CoroutineDispatchers, ) : Closeable, TimelineProvider { - private val coroutineScope = CoroutineScope(SupervisorJob()) + private val coroutineScope = roomCoroutineScope.childScope(dispatchers.computation, "TimelineController") private val liveTimelineFlow = flowOf(liveTimeline) private val detachedTimelineFlow = MutableStateFlow>(Optional.empty()) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 4c0abc0a34..78e9aba2d0 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -125,7 +125,6 @@ import org.junit.Test import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -@Suppress("LargeClass") class MessagesPresenterTest { @get:Rule val warmUpRule = WarmUpRule() @@ -1484,7 +1483,12 @@ class MessagesPresenterTest { clipboardHelper = clipboardHelper, htmlConverterProvider = FakeHtmlConverterProvider(), buildMeta = aBuildMeta(), - timelineController = TimelineController(joinedRoom, timeline), + timelineController = TimelineController( + room = joinedRoom, + liveTimeline = timeline, + roomCoroutineScope = backgroundScope, + dispatchers = coroutineDispatchers, + ), permalinkParser = permalinkParser, analyticsService = analyticsService, encryptionService = encryptionService, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index f662da0eb0..677b2ac10b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -90,7 +90,6 @@ import org.junit.Test import org.robolectric.annotation.Config import kotlin.time.Duration.Companion.milliseconds -@Suppress("LargeClass") class MessagesViewTest : RobolectricTest() { @Test fun `clicking on back invoke expected callback`() = runAndroidComposeUiTest { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index e987a32480..66e57da1fa 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -54,7 +54,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -@Suppress("LargeClass") class ActionListPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 8370ec516f..c9f6aa9631 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -82,7 +82,6 @@ import org.junit.Test import java.io.File import kotlin.io.path.createTempFile -@Suppress("LargeClass") class AttachmentsPreviewPresenterTest : RobolectricTest() { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt index faf0bbe6ed..a8f6c338f5 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt @@ -65,6 +65,7 @@ import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test +import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -307,7 +308,12 @@ class MessageComposerPresenterSlashCommandTest { permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionPresenter), permalinkParser = permalinkParser, permalinkBuilder = permalinkBuilder, - timelineController = TimelineController(room, timeline), + timelineController = TimelineController( + room = room, + liveTimeline = timeline, + roomCoroutineScope = backgroundScope, + dispatchers = testCoroutineDispatchers(), + ), draftService = draftService, mentionSpanProvider = mentionSpanProvider, pillificationHelper = textPillificationHelper, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index 4170ae41ab..3253e6088d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -113,6 +113,7 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.robolectric.RobolectricTest import io.element.android.tests.testutils.test +import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.waitForPredicate import io.mockk.mockk import kotlinx.collections.immutable.ImmutableList @@ -126,7 +127,6 @@ import org.junit.Test import uniffi.wysiwyg_composer.MentionsState import java.io.File -@Suppress("LargeClass") class MessageComposerPresenterTest : RobolectricTest() { @get:Rule val warmUpRule = WarmUpRule() @@ -1707,7 +1707,12 @@ class MessageComposerPresenterTest : RobolectricTest() { permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionPresenter), permalinkParser = permalinkParser, permalinkBuilder = permalinkBuilder, - timelineController = TimelineController(room, timeline), + timelineController = TimelineController( + room = room, + liveTimeline = timeline, + roomCoroutineScope = backgroundScope, + dispatchers = testCoroutineDispatchers(), + ), draftService = draftService, mentionSpanProvider = mentionSpanProvider, pillificationHelper = textPillificationHelper, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt index bc36766bac..d00062ba6b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt @@ -21,8 +21,10 @@ import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -35,7 +37,10 @@ class TimelineControllerTest { liveTimeline = liveTimeline, createTimelineResult = { Result.success(detachedTimeline) } ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { awaitItem().also { state -> @@ -74,7 +79,10 @@ class TimelineControllerTest { } } ) - val sut = TimelineController(joinedRoom, liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { awaitItem().also { state -> @@ -102,7 +110,10 @@ class TimelineControllerTest { val joinedRoom = FakeJoinedRoom( liveTimeline = liveTimeline ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) @@ -121,7 +132,10 @@ class TimelineControllerTest { liveTimeline = liveTimeline, createTimelineResult = { Result.success(detachedTimeline) } ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { awaitItem().also { state -> assertThat(state).isEqualTo(liveTimeline) @@ -149,7 +163,10 @@ class TimelineControllerTest { val joinedRoom = FakeJoinedRoom( liveTimeline = liveTimeline ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) assertThat(sut.timelineItems().first()).hasSize(1) } @@ -171,7 +188,10 @@ class TimelineControllerTest { liveTimeline = liveTimeline, createTimelineResult = { Result.success(detachedTimeline) } ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { sut.focusOnEvent(AN_EVENT_ID, null) awaitItem().also { state -> @@ -196,7 +216,10 @@ class TimelineControllerTest { liveTimeline = liveTimeline, createTimelineResult = { Result.success(detachedTimeline) } ) - val sut = TimelineController(room = joinedRoom, liveTimeline = liveTimeline) + val sut = createTimelineController( + room = joinedRoom, + liveTimeline = liveTimeline, + ) sut.activeTimelineFlow().test { awaitItem().also { state -> @@ -220,12 +243,14 @@ class TimelineControllerTest { } } -internal fun createTimelineController( +internal fun TestScope.createTimelineController( room: FakeJoinedRoom = FakeJoinedRoom(liveTimeline = FakeTimeline()), liveTimeline: Timeline = FakeTimeline(name = "live"), ): TimelineController { return TimelineController( room = room, - liveTimeline = liveTimeline + liveTimeline = liveTimeline, + roomCoroutineScope = backgroundScope, + dispatchers = testCoroutineDispatchers(), ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 290246ffb9..aa4198d156 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -103,7 +103,6 @@ import java.util.Date import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds -@Suppress("LargeClass") @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) class TimelinePresenterTest { @get:Rule @@ -1079,7 +1078,11 @@ class TimelinePresenterTest { val focusedState = consumeItemsUntilPredicate { it.focusRequestState == FocusRequestState.Success(AN_EVENT_ID) }.last() assertThat(focusedState.isLive).isFalse() initialState.eventSink.invoke(TimelineEvent.LoadMore(Timeline.PaginationDirection.FORWARDS)) - advanceUntilIdle() + + // Give some time for any potential pagination to be called, but it should not be called because the focused event is not rendered yet. + // Also, given enough time, a new state should be emitted with isLive = false, because we switched to a focused event timeline. + runCurrent() + assert(paginateLambda).isNeverCalled() assertThat(expectMostRecentItem().isLive).isFalse() } @@ -1906,7 +1909,12 @@ class TimelinePresenterTest { sendPollResponseAction = sendPollResponseAction, sessionPreferencesStore = sessionPreferencesStore, timelineItemIndexer = timelineItemIndexer, - timelineController = TimelineController(room, timeline), + timelineController = TimelineController( + room = room, + liveTimeline = timeline, + roomCoroutineScope = backgroundScope, + dispatchers = testCoroutineDispatchers(), + ), resolveVerifiedUserSendFailurePresenter = resolveVerifiedUserSendFailurePresenter, typingNotificationPresenter = { aTypingNotificationState() }, roomCallStatePresenter = { aStandByCallState() }, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index 0c6c81a513..15b624aeff 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -83,7 +83,6 @@ import org.junit.Test import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes -@Suppress("LargeClass") class TimelineItemContentMessageFactoryTest : RobolectricTest() { @Test fun `test create OtherMessageType`() = runTest { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt index 62e596bc21..cdce1f2483 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -@Suppress("LargeClass") class TypingNotificationPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt index fb44bb4e80..baedfa33b6 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt @@ -63,7 +63,6 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -@Suppress("LargeClass") class DefaultVoiceMessageComposerPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt index 6a201fe3fa..8b32ef993c 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt @@ -45,7 +45,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.time.Duration.Companion.milliseconds -@Suppress("LargeClass") class NotificationSettingsPresenterTest { @Test fun `present - ensures initial state is correct`() = runTest { diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt index 51a6f97ab0..8d6ef8d518 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt @@ -68,7 +68,6 @@ import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.milliseconds -@Suppress("LargeClass") @ExperimentalCoroutinesApi class RoomDetailsPresenterTest { @get:Rule diff --git a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt index 656cc92265..5d46f26230 100644 --- a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt @@ -50,7 +50,6 @@ import org.junit.Rule import org.junit.Test import java.io.File -@Suppress("LargeClass") @ExperimentalCoroutinesApi class RoomDetailsEditPresenterTest { @get:Rule diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt index 0b5133fe6a..8cdba871be 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt @@ -43,7 +43,6 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest import org.junit.Test -@Suppress("LargeClass") class SecurityAndPrivacyPresenterTest { @Test fun `present - initial states`() = runTest { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6602dbdf14..62794efded 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ android_gradle_plugin = "9.3.2" # When updating this, please also update the version in the file ./idea/kotlinc.xml kotlin = "2.4.10" -kotlinpoet = "2.3.0" +kotlinpoet = "2.4.0" ksp = "2.3.11" firebaseAppDistribution = "5.3.0" @@ -32,7 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.73.0" +roborazzi = "1.74.0" # Jetbrain datetime = "0.8.0" @@ -238,7 +238,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.25.0" +element_call_embedded = "io.element.android:element-call-embedded:0.26.0" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } @@ -272,7 +272,7 @@ paparazzi = "app.cash.paparazzi:2.0.0-alpha05" roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } -sonarqube = "org.sonarqube:7.4.0.8496" +sonarqube = "org.sonarqube:7.5.0.8588" licensee = "app.cash.licensee:1.14.1" compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } gms_google_services = { id = "com.google.gms.google-services", version = "4.5.0" } diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index 38517dab33..981f53ebfe 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -57,7 +57,6 @@ import org.junit.Test import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config -@Suppress("LargeClass") class DefaultPinnedMessagesBannerFormatterTest : RobolectricTest() { private lateinit var context: Context private lateinit var fakeMatrixClient: FakeMatrixClient diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index c293b67975..8b92d881a9 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -58,7 +58,6 @@ import org.junit.Test import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config -@Suppress("LargeClass") class DefaultRoomLatestEventFormatterTest : RobolectricTest() { private lateinit var context: Context private lateinit var fakeMatrixClient: FakeMatrixClient diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt index 9a79f78413..207de5bf6b 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt @@ -21,7 +21,6 @@ import org.junit.Test import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config -@Suppress("LargeClass") class RtcNotificationContentFormatterTest : RobolectricTest() { private lateinit var context: Context private lateinit var fakeMatrixClient: FakeMatrixClient diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProvider.kt new file mode 100644 index 0000000000..c2d103b3f2 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProvider.kt @@ -0,0 +1,73 @@ +/* + * 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.matrix.api.accountprovider + +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface AccountProvider { + /** + * An account provider that was input by the user. + */ + data class Generic( + val serverName: String, + ) : AccountProvider + + /** + * An account provider that is provided by the app. + */ + data class Managed( + val serverName: String, + val baseUrl: String, + val canUseServerName: Boolean, + ) : AccountProvider + + fun serverNameOrBaseUrl(): String { + return when (this) { + is Generic -> serverName + is Managed -> if (canUseServerName) serverName else baseUrl + }.sanitized() + } + + /** + * Return the server name, to be used in the UI. + */ + fun friendlyServerName(): String { + return when (this) { + is Generic -> serverName + is Managed -> serverName + }.sanitized() + } + + /** + * The user may reach an allowed account provider by either of its identifiers: the server name it is known by + * (what the user types, e.g. `element.io`) or the base url its client API lives at (e.g. `https://element.ems.host`). + */ + fun matches(other: AccountProvider): Boolean { + return serverNameOrBaseUrl() == other.serverNameOrBaseUrl() + } + + /** + * Sanitizes the string with the following rules: + * - Trim any whitespace. + * - Lowercase the string. + * - Removes a https scheme (treating it as implicit). + * - Remove any trailing slashes. + */ + private fun String.sanitized(): String { + return trim() + .lowercase() + // Intentionally continue to show http:// as an indicator (and for history). + .removePrefix("https://") + .removeSuffix("/") + } +} + +val matrixOrgAccountProvider = AccountProvider.Generic( + serverName = "matrix.org", +) diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProviderTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProviderTest.kt new file mode 100644 index 0000000000..0268eaed2b --- /dev/null +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/accountprovider/AccountProviderTest.kt @@ -0,0 +1,89 @@ +/* + * 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.matrix.api.accountprovider + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class AccountProviderTest { + @Test + fun `serverNameOrBaseUrl of a generic account provider is the server name the user input`() { + assertThat(AccountProvider.Generic("matrix.org").serverNameOrBaseUrl()).isEqualTo("matrix.org") + } + + @Test + fun `serverNameOrBaseUrl of a managed account provider is its server name when it can be used`() { + val accountProvider = AccountProvider.Managed( + serverName = "element.io", + baseUrl = "https://element.ems.host", + canUseServerName = true, + ) + assertThat(accountProvider.serverNameOrBaseUrl()).isEqualTo("element.io") + } + + @Test + fun `serverNameOrBaseUrl of a managed account provider is its base url when the server name cannot be used`() { + val accountProvider = AccountProvider.Managed( + serverName = "element.io", + baseUrl = "https://element.ems.host", + canUseServerName = false, + ) + assertThat(accountProvider.serverNameOrBaseUrl()).isEqualTo("element.ems.host") + } + + @Test + fun `matrixOrgAccountProvider is known by its server name`() { + assertThat(matrixOrgAccountProvider.serverNameOrBaseUrl()).isEqualTo("matrix.org") + } + + @Test + fun `serverNameOrBaseUrl trims, lowercases and drops the implicit https scheme and a trailing slash`() { + assertThat(AccountProvider.Generic(" MATRIX.org ").serverNameOrBaseUrl()).isEqualTo("matrix.org") + assertThat(AccountProvider.Generic("https://matrix.org").serverNameOrBaseUrl()).isEqualTo("matrix.org") + assertThat(AccountProvider.Generic("https://matrix.org/").serverNameOrBaseUrl()).isEqualTo("matrix.org") + } + + @Test + fun `serverNameOrBaseUrl keeps an explicit http scheme as an indicator`() { + assertThat(AccountProvider.Generic("http://localhost:8080").serverNameOrBaseUrl()).isEqualTo("http://localhost:8080") + } + + @Test + fun `matches ignores the scheme, the case and a trailing slash`() { + val typed = AccountProvider.Generic("Element.io/") + assertThat(typed.matches(AccountProvider.Generic("https://element.io"))).isTrue() + assertThat( + typed.matches( + AccountProvider.Managed(serverName = "element.io", baseUrl = "https://element.ems.host", canUseServerName = true) + ) + ).isTrue() + } + + @Test + fun `matches a managed provider that cannot use its server name only through its base url`() { + val managed = AccountProvider.Managed( + serverName = "element.io", + baseUrl = "https://element.ems.host", + canUseServerName = false, + ) + assertThat(AccountProvider.Generic("element.ems.host").matches(managed)).isTrue() + assertThat(AccountProvider.Generic("element.io").matches(managed)).isFalse() + } + + @Test + fun `matches is false for a different account provider`() { + val typed = AccountProvider.Generic("element.io") + assertThat(typed.matches(AccountProvider.Generic("matrix.org"))).isFalse() + // A managed provider that can use its server name is not reached through its base url. + assertThat( + typed.matches( + AccountProvider.Managed(serverName = "other.io", baseUrl = "https://element.io", canUseServerName = true) + ) + ).isFalse() + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/accountprovider/AccountProviderFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/accountprovider/AccountProviderFixture.kt new file mode 100644 index 0000000000..3c9cb7033d --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/accountprovider/AccountProviderFixture.kt @@ -0,0 +1,28 @@ +/* + * 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.matrix.test.accountprovider + +import io.element.android.libraries.matrix.api.accountprovider.AccountProvider +import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER +import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_URL + +fun anAccountProviderGeneric( + serverName: String = AN_ACCOUNT_PROVIDER, +) = AccountProvider.Generic( + serverName = serverName, +) + +fun anAccountProviderManaged( + baseUrl: String = AN_ACCOUNT_PROVIDER_URL, + serverName: String = AN_ACCOUNT_PROVIDER, + canUseServerName: Boolean = true, +) = AccountProvider.Managed( + serverName = serverName, + baseUrl = baseUrl, + canUseServerName = canUseServerName, +) diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index 64e647ab19..46a012baff 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -54,7 +54,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.time.Duration.Companion.minutes -@Suppress("LargeClass") class InReplyToMetadataKtTest : RobolectricTest() { @Test fun `any message content`() = runTest { diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt index 4f02637a62..3003e5d46e 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt @@ -62,7 +62,6 @@ import kotlinx.collections.immutable.toImmutableList import org.junit.Test import kotlin.time.Duration.Companion.seconds -@Suppress("LargeClass") class EventItemFactoryTest { @Test fun `create check all null cases`() { diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index be577362bd..ed710df50b 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -64,7 +64,6 @@ private val TESTED_MEDIA_INFO = anApkMediaInfo( senderId = A_USER_ID, ) -@Suppress("LargeClass") class MediaViewerPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index 5918329c40..d25dd23d9d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -69,7 +69,6 @@ import org.junit.Test import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config -@Suppress("LargeClass") class DefaultNotifiableEventResolverTest : RobolectricTest() { @Test fun `resolve event no session`() = runTest { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt index f0dee4446c..68fc55909b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -52,7 +52,6 @@ import kotlin.time.Duration.Companion.milliseconds private const val A_PUSHER_INFO = "info" -@Suppress("LargeClass") class DefaultPushHandlerTest { @Test fun `check handleInvalid behavior`() = runTest { diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png index 0b20380cd5..b91b3b97b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49371b85e60a5e0465c0ba1537293f6c071962c498579bd71b42763671c8dc01 -size 18148 +oid sha256:6200774d91495f1ef0b49440b8836148f60736c510ffab7d28c7f0b9b38634bf +size 6596 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png deleted file mode 100644 index cbe30336d4..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:78ef17d2f96d5544ecc8213fe1b7f78ddf263557855901f00f152a0b543c1a6f -size 7746 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png deleted file mode 100644 index 447de4de32..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ce777b1c6895caacc21adf4f6c10c71f75a2c6d31a617afe1c7f49d10c5141b3 -size 6929 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_3_en.png deleted file mode 100644 index ae99061ada..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f49c89cde78d8d8971d5866f3cef2fef51bc36d1844b0364c950dc2afb188c5b -size 5707 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png index 359e866105..82412586b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:759f68aae091a43f54d597e386a5503692f99ed9c3908450dfadf59999047d01 -size 17272 +oid sha256:8553d2b2b795ca403f340881b0c12a30bd7f0871cbb924a6987550a87cdad8fa +size 6437 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png deleted file mode 100644 index 3f58435f8a..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0591f839e4605cbc51746f9d479a0bfc37e078178e6b2d1395db07dc8a7e1261 -size 7382 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png deleted file mode 100644 index abcf602e31..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a57866d169c69ba68365ef8cfd4739ec95f8a8aecf4805ec68b2f247f3f698dd -size 6627 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_3_en.png deleted file mode 100644 index fe5123f403..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db8eda0daf8e2038c2c69ff945e686a9e5ff271d8dc4f30987f14f7acb22ae85 -size 5625 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en.png index 7869ed3dc5..95fd48ffda 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8098b9ff6c4d08d8cc61bc51e391a5a17e9791d7236d2d7cf652a6b855c87b97 -size 22501 +oid sha256:30f64c99d3bae9f34fdc855293070933e3c96ff6ea635892a4dc9f29a6e8e62c +size 22387 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en.png index c1f55034ec..d2d67db632 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94878580a4ffff7c4d437eba1c62184aa61435420d6c85488fefc53e33e1e150 -size 23497 +oid sha256:99d101dae150cf33fa9a2c5782ae18811c6238d74b35c27e54227bff1e32b28b +size 23390 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en.png index a960bff528..8b7f161b7e 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31cee1e647c74c67740320180af54509e466ad162ed56894695b038ccbe35103 -size 24017 +oid sha256:867ca4eacdbd56ea23d2b3ac619159a46fcb1670ab9a63f433cb7e44c8ceac63 +size 23910 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en.png index fc8aeb2409..bc0bf3ca43 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bdca1cc8a99976a8c4a297192650a496a4347dc27c73d1950f693be1cf2cabf -size 22005 +oid sha256:c0bb15955a7bb7ce2fecfc8227ece62c31021a3af3311bb9234d4d468a3384d8 +size 21850 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en.png index aa2b293c3b..de02c4d9bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:604243eb5409e170a760cda08fca1662b7694c6920f7b18da8d4c4f9d60ffe9a -size 22915 +oid sha256:b1b70aeb18d6e3e6d8d61081135dd015fb8d6a88fa4129605541097b4274cdb0 +size 22747 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en.png index 1f6dce1201..8b2c3b9bfa 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d3bb32677abc81ffcb892839c5609f27637a9ea8081460d3f95751a03f125aa -size 23426 +oid sha256:403b8e7c3488f0edbfd53afba758b3cda0638f80167c1a84ea46b75100b8a4be +size 23264 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png index 8edee63bc1..3192cf2a5e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de82970da241172b94f822f8792e6fc721dcfc7f0f67e3523962e253e9750884 -size 255933 +oid sha256:1885bbe8b0ea497ac20d7bcdd0c6a93ba0533e898378d35e1cafdc8c9924fd56 +size 279984 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png index ccfdd7d9ed..f899ad5d43 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe6cb72de5079be2ac02ec39f74bac0d20492cd6f5509b71ad9cfc4cc52955d3 -size 326468 +oid sha256:1ca032575e4ebd75a2ebc5a4cb9f966e48cdfa56f4ff42d6d9309fa3577a1adb +size 310609 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png index 5f51b27f5e..693a36b7dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3274314ab9e39c23a6b9d66adf80ac8bb7b364ce60a300f96582374a18bc821f -size 54590 +oid sha256:3dbb8ef39b6a7a0c2f576d8bd101aa97c89da696f23e6d5836846494471c01a1 +size 55799 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png index e1f6ee6430..b1850a50a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e27c7c51fe07cca51b92a601aa9c58deab609bfa5e82f8f6f88eaab6ae59ec3 -size 64307 +oid sha256:f1dd0da147212e13a6c0ad5ad8f85ce55666c444076c238ea27eba5605cfab1f +size 56909 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png index 5289eaca63..4185f46b51 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65f9ee083a73669a0d3f9d88141a610ea53f3c39e89d8f706d414b04555b4764 -size 59321 +oid sha256:085963fcb425147d35a9ad947a7a3e4ba4712bd3d2f32e93099ff15cffe83b88 +size 64761 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png index d3f7da6320..2e70435fa5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a94a4cc176b5d52ef9b21339749324adeef9857b6cdaa2ae973903e2408662a -size 55904 +oid sha256:7a3667390d9d88894bbdd5d72b0b714b29c9ff160653e310e97b0da87b6ba8b0 +size 54724 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png index bd3b0196d6..0f6de1b844 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f7f7415abbe51858f938e16ad03c6aab983fb3d5e2a53a028bae3c031446646 -size 67848 +oid sha256:28512b0fda0b71b98eab03cee15c9d3163825b1a3545670cfa277ddd5434c87a +size 58417 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png index f4d51ccc61..6c774d4cec 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a8f5aaaa7ebb0e61b07867590d99a8791c5d04cad1ce0f334d23dce6f26c6e6 -size 67276 +oid sha256:6ed6a5f1a2055e19a6b8fb0af4738845bd2184f755c323022c52154cdfa07e05 +size 60909 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png index bd73e322ab..02d0fc8eeb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 -size 3657 +oid sha256:f77d8243c23d3f37dff86dec53671cbde9fafe1314f6e6b445abdfe47d751ad6 +size 56187 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png index 93622cedfe..bd73e322ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87193a7c9eb9d40234970e5f5be02e80f2b3ffe91e2c9ca6afd58b8bccdd5911 -size 55075 +oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 +size 3657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png index bd73e322ab..0cf6c10c4e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 -size 3657 +oid sha256:49f0bb16d3d57b59242e704e3dd4e8885c59721a42129837ff6a4f0d7d02de11 +size 54433 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png index bd73e322ab..adec213a8c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12020ba97720f2374f9ace172693f08be01522e4ca30d3970efa91d802581e81 -size 3657 +oid sha256:8ebee4aec3dbab6e1bb05fda7c5e423f0e931560743d0f0ccbac6b70fa9ad808 +size 52921 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png index 0ab00359b7..12bd190dd1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b5e6f4c5ec97e67bb20f03f25f18d9f32d5d1aed5ff30d161b331894ad03443 -size 647384 +oid sha256:ce15ca9f89274620728715e3448ce671e2253f4fed22a44035460e9a3709ca0b +size 647404 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png index 0ccdcbb603..160d58f9ec 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0424685437a433b35b589668b409d55f26ab41bcae01af0121a54033c56a8c2 -size 646194 +oid sha256:c4e7b28b78cfbcbb6abf00317eea945859ac6465cecf81a3ad15801b0de721e4 +size 646216 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png index ab6392a424..0435f55de6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94f89a37549a3f198442db467bc7d8539f0b5649d97927bfe731f090d6dfb03d -size 379505 +oid sha256:ff839731834d7d0c94fa9a2c412014ddbaeaa053c5af1c8cbd00a3f3a583f364 +size 379511 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png index f0b5506b89..9cbd5c1d95 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04ea6c2947a21d3d4a3d43e9c4750c4f2a49056536a4da3f34c95df8353e9de3 -size 377990 +oid sha256:d0e170b4bea262de612bf3374d475fcd25470ff91b7554d20809c51857a78cec +size 377963 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png index 3ac59caeea..1f0fdabe6d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a94d5198e27338675c0e15df5874ba3272ee315b8d77291dc7ccc7c754ed1f4 -size 365082 +oid sha256:b8bb5a6de6601da8d7fe59f110a054577e2e25dae4bb449f60c04e7d7a1b9551 +size 365073 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png index 195aa04fe2..86018764ac 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76e5ae44362792d570cd82f3910c8bd0acde509b8418c5892e0385e7d636f042 -size 370223 +oid sha256:8344427a0fe87cbf0883ff53a9ee4dcf90976a7011f6618d85961df48b2c53a9 +size 370228 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png index 29771235f0..058a3992c3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60b9f2b920cd2a2a414805d1f0f77925232d9ae60bd607fa18d4395cda2dd73d -size 363291 +oid sha256:00fe1e676086766a3eac3e4457eff52c989789fc5d8d0df2624c019ed25ce9af +size 363322 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png index db4ea8296a..c6d4541184 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f0d24b8011564ee7a8b2f8c2e6a24eda668ee42d451d862f884acb7926aa06c -size 368529 +oid sha256:c00e504593d8eb61b598f05f46ee4d591757975a80c0b4227b240847d2cd4868 +size 368510 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png index 94428abb56..84d9d4e5c3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36d7cf2510d546d2b330b1d1b1674ce58df0558c547e443794a1e7b2222add87 -size 347897 +oid sha256:7c609a633ab23eae5729ecb57a17a385bac65e81cbf0180c01e0847f1028039e +size 347890 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png index 5400a98a50..40fa5d563f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40a23ddff1fe46a486a5d34d94e0e67ec14d0b2b9c6bee2ddbadc17ce0948b7b -size 367131 +oid sha256:3ff97eb99daf65f74b469d51b93bd77d304d0c780b9406e1c4d7893b71e3c68d +size 367163 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png index 22ff4965d1..bac23aab98 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:787151f8f242a8794a804bf14831f6ca680814d617e85feb3652103e8a0f20cd -size 346565 +oid sha256:e30686c7c037ef102717e0316543faf4d3ed2671c21eca96a667150daab380d6 +size 346614 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png index c3b6a086fe..2348755afd 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e615c14cd23f3efd217ec20ce05ed32be76dc3317091e8221f1a72a5a594d92 -size 364175 +oid sha256:e907f67ef884edb61df187f0f229ed4ff4cb31f8c276f807698f861c0e7a4c27 +size 364124 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png index 67490ba700..01d4dca84b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4431e2d9f30123949ea5676ce60f642234adcf9b5a33f2acc3f80ebf6609a66 -size 369730 +oid sha256:ef2bf449671b652cb152b3baab92ab100eab77a54229ab49daeab156f2f484d0 +size 369724 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png index 1600afff71..f0dfbc3321 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:273351cc9c533b2424859a0de00a8a3786628abc3a88175705c4205dedf7e9aa -size 354737 +oid sha256:f7a6302320bd1e4258e03487539bf46b0b12781dfc8a22b3f9f267017a6146fa +size 354751 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png index c4943b892c..e38cf03ad6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c410e853089cc2c4a2c48a00dd9a8babc8095aed72bb4625057444710559c6c -size 368129 +oid sha256:41a0e404dea7f52359b24b3ec3926b1405429b165cfed44c63b3922ea6d3cb46 +size 368114 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png index b5dac8434c..68f8148879 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a92f8af94682facac66d4aacb6e49f345561c15be2f8b689041b78aeaaae831 -size 345062 +oid sha256:6ca8cf82aa8ab1bdad56a27584b27789402aeb7ad8fadd038280a376fb75a72c +size 345094 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png index 1b99468924..8aa2cca147 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a9509411777f342261cab58b92a022b3c9f8f0f7a9e4d0527120a91976575f0 -size 357733 +oid sha256:02ecb903124273e24151ea2088f6b99144f7dcadba00a582b6210dd23835b5c9 +size 357743 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png index cececc229a..b5032c29b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0c6dafaa41849af00d5097a85bd88e3a054a0a9e41c0466a08e6ff8dd6102b2 -size 356857 +oid sha256:dce23463c335d01eb67293cfcede5d85e96ab2ba035cc9a188d42e8e6866bc63 +size 356831 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png index 14a40a5676..a332744358 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a11044fe90227f1f316a1ade8587e545bb620e11cca76865fc2eb83cdb817fc5 -size 364655 +oid sha256:588967869cbd25ed8b82aff5618175660a8a9e45a5a861b1c7867cf1913f575b +size 364646 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png index d7b969fd5b..133fd3903f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2528df873713755f542f61cd794080929586c1bd5f316c0378ce9e837c21d50b -size 397145 +oid sha256:426d1c844a8c88281c5fa4c61916d93278fdfd38f8cdf2dc54bf1a821f2de3eb +size 397167 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png index 63bd9c3f6d..0041a870b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6cd94664f89bdad2a9761ea8b20acb83fed3ed7cb5be41af8fe4852643c0bfc -size 356175 +oid sha256:a2bd77c08be705c66225f16c29fd6826decd92b41922d0f8ffc0d1a63d8c59d2 +size 356193 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png index 9fcf389708..f67906a37c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9daeee1aaa04247da3c8bb5aaae9947aab1ed2d2057995e53d6ebdb63e5cd356 -size 355848 +oid sha256:ffa68c8398d789444151855496e5df4ffa03218a383db6ea46fd4f979829b823 +size 355831 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png index d235670def..6e83d6491d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a00bd766a00b7e7c6615fc53bf469b3ca5056a6c514c096431ae38d99d604306 -size 364845 +oid sha256:34152c742abc486a48234e22101833bc163cf11979890d06aac41e68e3c404d0 +size 364846 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png index 0021619c44..b10f9741b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b23d3a37774283067a8919b8c332b1bb5a79e90dceb7216b484b985491282e5 -size 355389 +oid sha256:3ec1b0244dab22414c6719b3309426e13128d0c70fb67dc66cd8a60d42521fd0 +size 355411 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png index 422c324695..77abe20982 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1df8d22766981fc76d6d767dc3f40d98cadc7c51be20417b067b91af604232b -size 367884 +oid sha256:5c3d87862c6d7fc88d0f23cd54c5b9f77028992fdd7c282a195b964dd17e85ac +size 367855 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png index 448bcec677..1d34761a9b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe0daf9161bc2da194992a2fc9505bf2009c5dbed78cdb35d2eb7ed1380a7df5 -size 353037 +oid sha256:f7d9b5fc0ff5d7828ad6f4dc0c9bd9520b4920553da97ed7b4e030ee844c0208 +size 353054 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png index c081fa16a5..5243218ae3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a92bcf5885322e11a706071969fb674b1f6e60e021af153aaa125baa4a602ae -size 366285 +oid sha256:9a85db2f5958db867578663533f204b2e803372d470da890fd8056ffed0cf46c +size 366254 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png index 47a9a6b630..b6d1f07279 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54372845e922d047408dd960d048b9b5eae2263d589117d8e7d197741aa34f32 -size 342827 +oid sha256:4bee12a6bdb18fea5ac6997671bfcb903296da7278cf69b2456330066ca31dfb +size 342870 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png index 5560c21967..ceca184bd7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af4a48ee80535f2c0a226c4ffa4344054fb63b7b0a88ea2c030a4eb8dd5e9644 -size 356078 +oid sha256:8e82011a72f36889c53ab61ed2b9947c7fb5f868c9b7010904fc10f4ca587082 +size 356082 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png index 5cc5cfe4fc..5cdad1df92 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6524cc87257cc9d3f135904f4e14594b56df695e71fe576418d2319c472d710b -size 355285 +oid sha256:9e2d8caf5aaae7d3406c703288e491f328552f0acd18e56afd5265b4ceaf1cd1 +size 355272 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png index e9d85990e8..a431a9429a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66d4b6ea4a697735bbb0fac936b89cde99c5077973ba828894bf50b5dae233b3 -size 362848 +oid sha256:93c2df04d65f6078ce3f7067fbae8a7317ec42ed0fc013dee962a3953269f3b7 +size 362813 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png index b51a89586b..0eb106f4a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e642229c58169775fe702769a306bbef22698516ae6f7ac75a761213c3f195fa -size 395446 +oid sha256:217ac95bf476c53d2dc6d3d0143b21312bf38b8ab86c8dfd4f9360582b584f9d +size 395476 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png index aa322fc2e4..ae8495618a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fae87094ea13b14e2dc7c16ca96e4f8096ff39e962799468fb4ead4c666620f -size 354381 +oid sha256:08057fdc1e1184720b438ddd2a8753f00791baf2456cc30b45de8e6bdeb57e8e +size 354404 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png index 40105c1498..9d617f6688 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ed24d190f10c079f8cf176404372d40eb25cc78ce595a22febc540315057b86 -size 354075 +oid sha256:dabad9dcd5b8f0d4bb964a0187ad7c94b023b04552dacd29a077daddfd48d550 +size 354052 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png index 0cfd1dc985..1a8ea3e4e9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:545c9e8933e420cc3f8fbba0fbd5162c946c65a20721faeb77c568596ca720cd -size 363045 +oid sha256:66aba7b4271a990fac85b0bbc7cbdc371fa1e38a4da5bb4e2f62a80b21cb3a8f +size 363012 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png index c9227ebf94..c8306263a0 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e614f4a8657637c584a283d69124236d00fe1251c2b8d739c5688a57a6fd152e -size 353693 +oid sha256:756f0cff90ddd61adce2f467c0384d74d430f6d51c091e4980e45bcc9ee6f0fd +size 353705 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png index 7a5e5e1512..144411969f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e3467a7606a5a7beaafb510faf008ed567b1e3a9915ede6e2ec09c491af0bb9 -size 56552 +oid sha256:84415250503d5e586af7b7504aaa915c1e24dec0a3d28446487c474c354e05c9 +size 55534 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png index 843adcdff0..52e023ec49 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:446ae29cbb211665f6d91129a7dee0f718b407fcb2a64d3f2c0e962e1cb47af7 -size 50168 +oid sha256:c8420417cf8ba717747dead34b9646515f35eb94e1f71aa952b77c3ff433043e +size 49684 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index ee23a7111d..adabb6dde5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38ae233a128e887efc48da73be3d6ce50a7095601315ed49a3c9cb209ffdda26 -size 58352 +oid sha256:9b071ba6e99b68dcc75f5aaed3a879f2e30614c954b88c6dc05e1dfec57f1079 +size 65771 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png index 2a5991cf59..6c2a7e8460 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:115c725873fb206d8f732a8297a6548ba4fa45d7328196b79a2d9e35545de7d5 -size 40347 +oid sha256:984536295516b542ec6aecf7d07baa3c6dfebbc66403b6d4258a6217616cec93 +size 39991 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png index 7ce91f4684..a1dc68a3d5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b749e1260d6ba2b7fef5ae9efd58612f6d67d1788f6c1e5cebc22d58d0fd7b7 -size 58563 +oid sha256:871d92a7565dd233ea87d53658be4e5202775def8e17fea8d386b8975c3b065e +size 53868 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png index f6456f0ec1..479d07ae5f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22153168f6f3e84ab789faea29c94fffbc1a5e4c1b9a98deccb0460192fdde23 -size 57213 +oid sha256:0d4f03ac10bbb676d272dba906e758b25d633a1da1e249fe23ba825f8d2f4901 +size 55298 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png index 6a3ffb84b6..c90dc8d08f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48b91e52306daa750b4b5792535e1f1865ae3b74a36215376a51dafdd0dcd506 -size 54837 +oid sha256:e48a364a28a67d7d090faa23ef88b27dbd47f0a2f552857490728cb6952ce08b +size 53454 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png index abf8eb7086..6c0a7b4edc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e79e1cc59ba62ca8eb99300eb520b05d7ac6d52c077e3010c03b474865b15f4 -size 58374 +oid sha256:f5aa22c3d79a392625ae231abc046f07f7cdabd91613d5e6f07aede5382aba9b +size 60410 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png index 5d4f5bdaeb..ac1d94a4dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08fb14008b08d86755c3473e7194ef0a39bb73c3f0d48122ca80c6a019ec98eb -size 48676 +oid sha256:be5b086ab6934bae88b39987bc51e709c5adb81bdd04fa028e97832c725fd68a +size 50219 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index b3af9d831a..2209fcb54f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce795c4e32a1be3d57f6b1c87588d8c7ed0442996ab9d8a96f1ec5a07627e598 -size 59663 +oid sha256:bd0de7412503e93c33cf92dde0cff38bb68555ef176080d913320af5e4942e7a +size 58642 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png index 2c88d322d4..82ef5e7d6a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:963fcec9647d0e5e57bd3fbe3edd389c17be7558c1da7e93fa44f34db34307ee -size 57726 +oid sha256:41c4bd5cd1bcbedd91de0a64967fb3f3e03b5f0937ae1ea1a93a7c4a1b24a4a0 +size 56262 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png index 281aad62a7..135d84dd90 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85f6f4c64f67faa99b9af65307a0cd3b46a68db52503c9c929375408597995d9 -size 62094 +oid sha256:4ff538a81d013fdc90dd7be466f710088eea93e081d23448b0731d301a4a3bb2 +size 60702 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png index cfa6e4dbc3..89fc6708d2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a3281f35461edb900ea434c68543dc684a5fad7dab7c9aabac510bc59d442b3 -size 56479 +oid sha256:89467c5b84d0a15210d4013013598d9994d3005a2299e44583d78355e6718ad8 +size 55538 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png index 5f3a619097..17df34a50a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19d14e3d779b163053c28e66d3381f0cabe3b540744606e955cd733f9e044089 -size 51713 +oid sha256:6548f5bf1906e1dddcbcc03bb8f76b42e604075751ea8efbc3ead826bf4cfad7 +size 51258 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index 6e8468054f..b3109663f2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:262e8d4b8daa2d25593834fd0ab5d7019b0a3dd13707aa96e0701bf9240c6525 -size 59809 +oid sha256:10bce6df962a5198f019f509b1cdb55e28195587de4b74742ddc29833f1333a4 +size 69216 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png index b32ee34f99..1ff5ace3e3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bde66c53d81f882cde9ed527e8ff82c7e9d6cb1199b440d4b721659f7189bb42 -size 38906 +oid sha256:8d875f83a3a1977e2a2cf151dcf9d731108a8590eb3643eb057727421eb1e955 +size 38812 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png index f4ab74029b..f425dd2c4c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51a5b682243b969bc2d7d609decb823b0509d240f3ff14ee78319260af76a47f -size 58740 +oid sha256:bd6dc088e16f722b36c2198d6a8611af87dec2451f94ca3c2b6880b97f709b98 +size 54095 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png index 3974618f39..b0cd5bc6d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:718d5c9f24c114f3bf88e000b637ea19598d7bb9b97001ba137fc12a6a1285e4 -size 52608 +oid sha256:5e900ff75d02c9db4200e2032444dd7e2f8635973cca3389dd96d7d2bb734061 +size 50757 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png index 0ad1e67deb..486d5da5c6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82e6e38576a507b19e58fce9dda0734ec00bcedcb0baf8f4c7919dbaaf7d2b85 -size 55046 +oid sha256:e482603e6d03fffb57d4eb135edb6209c748c51b1f07e638a4ccf133b1b87a79 +size 52920 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png index a009d0f584..5a877a59e1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:478851e9e2e8c80095aa552b98a5d574749200ff49899c9472c913b07459c4b5 -size 53758 +oid sha256:5a582219d3143acb08563193bf7106e1ee8f6612e359d0a192e9a2f67c6799e3 +size 55375 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png index f82a7a8219..0383bb8f95 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22ea7fe121a63b35a2c2bb47609e692f8bf2b913871a6baf3d420c04334c6c4a -size 44344 +oid sha256:7c88b3ed8cc69dee59d4d0a726a9e383d00f2815b6525315f7dcd96f9c25ec2a +size 45618 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index 7261c9b671..952751a962 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cf3d727da78b39ecf24d4df7db0884af9008db67cc92274849c1b0978f87726 -size 59252 +oid sha256:ed13a2778b8ced86ee41d5460d274b42b714a87f59749e3b26bf75114ef40dd9 +size 58265 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png index cfa62a6626..0f7fe2db38 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfe848e4c365cf4b4d663145f74e5f8b3f47a4c8994961c530603f0248400316 -size 57117 +oid sha256:a576d52b840730334a38046cf8b57993693fc760b4fbdc00376cb48b7268d76e +size 55890 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png index 89065e6bf0..4d6afdbbb1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a966c9a1fa4fdd933f4ef9239592e90458a2819ff4a45c857714d2e38c0df5b2 -size 64405 +oid sha256:35069d4fae829d9afe479308dd72ad025fa011290d23133a231f4e70f601ee75 +size 62987 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png index 7cca911c45..074820e157 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e14b77fa3c3aeaea58782309e6de8b9047e07945009322990ebf5f4c283309 -size 52770 +oid sha256:7caacb336793b8ae4b83e9a10a82325e25e8b3c036f881898ed28688250eb6e0 +size 55744 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png index 2af459c158..7cca911c45 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28a95d4b7ebca92c4e0467f3ecd9acdaf7f6257604d190f5d088a9a25e87d637 -size 55305 +oid sha256:26e14b77fa3c3aeaea58782309e6de8b9047e07945009322990ebf5f4c283309 +size 52770 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png index 616c06bf46..a68f886f73 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 -size 53966 +oid sha256:e5a2e306087a967849f61872b56a83f194d1a76df5723f0bbae6e6e2f4a98c63 +size 55725 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png index 616c06bf46..36d19c6b72 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ce2519906f1aaf34700ca9ba4bdeaa72030f1f5aff3f7e24352a1435ac67d98 -size 53966 +oid sha256:7dfa2767c55a81d3f121b8fa55443b7b05f9369e91639b80e0be349863799ad7 +size 55438 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png index 82f344f714..2c15fd6a37 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d96eefa01a5de803b0cc9d9109c20df86eaff01b73ff24a91945b43d53d5d01 -size 11960 +oid sha256:7260abe16fc2ab4f6ada97ea0f756dc5c9908ff3b61d6ec1723ffde302bb3dc5 +size 11955 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png index fdd8fcd5a5..7c422591ad 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22678a2a8505e1d9d5bfcb44459b6fac69c11a4bf72fae09c48ffa2e54f5a110 -size 12128 +oid sha256:2e16fc1daa0f284c442f66591e1d1f59161272a67998d26740b0b403bfe82495 +size 11866 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png index f6e27475da..acb8afa74a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ee89826c8aa2ac0371ed31959243f62bf9968806a7b27117cd7b4d849423a33 -size 11667 +oid sha256:bed7565420e11c43acc06896ef298683d2f2dd1dc16c430eab7018e9ead9937a +size 11652 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png index b1bb69efb8..e0a4cb1c97 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.userstatus_UserStatusView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e27cb9e501ccbac54926f0252b4098867643a2d2683c5b267f86ad99edf6f5d7 -size 11817 +oid sha256:87811df502c760816e74beb006341627857c39380ce715f1a747ac9fce77b226 +size 11516 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png index 73b8b8e50c..5c597300e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8a3ad99763929d2783147814e44fa9ec7e7efb7b99949e3580f23c38fb5d971 -size 9398 +oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 +size 3853 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png index 5c597300e4..f8a1f24b34 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21aee17ee9a292bb2f0d3d2ec2db565ebb8d5df753454a5a002f033ed5ed39c5 -size 3853 +oid sha256:1342b6427f5b2e3075ae3ccee60bf1d3b11afaccf3e152643d6ef2d65c21b54e +size 9108 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png index d47bf90664..4ae7279fe7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c -size 3664 +oid sha256:410a8596d4c70b9a3856d959d6d71ade8e831e8581ff410c9900957a93b3eb50 +size 8114 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png index a87d89c701..d47bf90664 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f3a0cb5d9bae6725a1c527f3d9575aabf85d3edf63a28d9faa23f4f0b65e2d0 -size 7624 +oid sha256:520cae2544153ba45f8fe4b021286c2d22da06aa1b60be7f1b13879723ff994c +size 3664 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png index ee5c582240..58546b8522 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75de341f705bb22955f9399180519b65ad59c2e1917317d56e50ea4db78f35bc -size 12102 +oid sha256:30357bf18792db5ab20085010251170a724ffe8c00313facd04ee704e806af64 +size 10982 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png index 52fd9b9693..fedead0fa8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58b9491aaa5554b89d86c62a5c9c44975367538c6baa5562f8dc5768f76d8264 -size 8705 +oid sha256:4811667dec0f32accc5e2c8a5dd5c0b34017fcff8d1e0376ee7a4708426bca0f +size 9838 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png index 38ad0e4b56..83302d6ba4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:326d59cf768b2dbd96eb7964d54c8eda509a5cd57c6e62087625e3314da0bb4d -size 13658 +oid sha256:16dee1dfe746cd40dac9fe8f0af3ecb42cd14bab5cc490917b589beed065ed1b +size 14686 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png index 687e0d436a..4da2a98aca 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d57df7110371c9224e232b707d6a1b10ce14afcb56c803a78220792f2a0d4cf2 -size 11354 +oid sha256:ef0dc4d53daeb986f378b6e4d85928b4b6729336c89cf21e642c88c53688a3ab +size 10292 diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 147eca01d0..9c9bcd248b 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -175,6 +175,8 @@ complexity: active: true LargeClass: active: true + # Do not report large test classes. + excludes: [ '**/test/**', '**/androidTest/**' ] naming: VariableNaming: diff --git a/tools/release/releaseV2.sh b/tools/release/releaseV2.sh index 23f3910311..85d96414e9 100755 --- a/tools/release/releaseV2.sh +++ b/tools/release/releaseV2.sh @@ -5,7 +5,7 @@ # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. # Please see LICENSE files in the repository root for full details. -# do not exit when any command fails (issue with git flow) +# do not exit when any command fails, failures are handled explicitly by the script set +e printf "\n================================================================================\n" @@ -63,6 +63,12 @@ if [ ${envError} == 1 ]; then exit 1 fi +# Abort the script with an error message. +fail() { + printf "\nFatal: %s\n" "$1" + exit 1 +} + # Handle the result of a version check. ${checkError} must be set to 1 when a check has failed. checkVersionResult() { if [[ ${checkError} -ne 0 ]]; then @@ -97,16 +103,6 @@ if [[ ${unmergedPrs} != "" ]]; then fi -# Check if git flow is enabled -gitFlowDevelop=$(git config gitflow.branch.develop) -if [[ ${gitFlowDevelop} != "" ]] -then - printf "Git flow is initialized\n" -else - printf "Git flow is not initialized. Initializing...\n" - ./tools/gitflow/gitflow-init.sh -fi - printf "OK\n" printf "\n================================================================================\n" @@ -149,7 +145,7 @@ versionReleaseNumber=$(echo "${version}" | cut -d "." -f3) printf "\n================================================================================\n" printf "Starting the release %s\n" "${version}" -git flow release start "${version}" +git checkout -b "release/${version}" develop # Note: in case the release is already started and the script is started again, checkout the release branch again. ret=$? @@ -160,7 +156,7 @@ if [[ $ret -ne 0 ]]; then read -r -p "Continue (yes/no) default to yes? " doContinue doContinue=${doContinue:-yes} if [ "${doContinue}" == "no" ]; then - printf "OK, exiting, you can start the release again with the command 'git flow release start %s'\n" "${version}" + printf "OK, exiting, you can start the release again with the command 'git checkout -b release/%s develop'\n" "${version}" exit 1 fi git checkout "release/${version}" @@ -197,10 +193,17 @@ git commit -a -m "Adding fastlane file for version ${version}" printf "\n================================================================================\n" printf "OK, finishing the release...\n" -# GIT_MERGE_AUTOEDIT avoids opening the editor for the 2 merge commits, whose default message is -# always used, and -m provides the message of the annotated tag. git flow appends the tag name to -# it, so the tag message ends up being "Release v${version}". -GIT_MERGE_AUTOEDIT=no git flow release finish -m "Release" "${version}" +# Merge the release branch into main, tag it, then merge the tag back into develop and delete the +# release branch. The merge messages are provided explicitly to keep the history consistent with +# the previous releases. +git checkout main || fail "unable to checkout main." +git merge --no-ff --no-edit -m "Merge branch 'release/${version}'" "release/${version}" \ + || fail "unable to merge the branch release/${version} into main. Please fix the conflicts and finish the release manually." +git tag -a "v${version}" -m "Release v${version}" || fail "unable to create the tag v${version}." +git checkout develop || fail "unable to checkout develop." +git merge --no-ff --no-edit -m "Merge tag 'v${version}' into develop" "v${version}" \ + || fail "unable to merge the tag v${version} into develop. Please fix the conflicts and finish the release manually." +git branch -d "release/${version}" || fail "unable to delete the branch release/${version}." printf "\n================================================================================\n" read -r -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush