diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/GetTokenResponseJson.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/GetTokenResponseJson.kt index 537483d1384..e8d688835dd 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/GetTokenResponseJson.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/GetTokenResponseJson.kt @@ -1,7 +1,9 @@ package com.x8bit.bitwarden.data.auth.datasource.network.model +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonNames import kotlinx.serialization.json.JsonObject /** @@ -92,20 +94,21 @@ sealed class GetTokenResponseJson { /** * Models json body of an invalid request. + * + * This model supports older versions of the error response model that used lower-case keys. */ + @OptIn(ExperimentalSerializationApi::class) @Serializable data class Invalid( + @JsonNames("errorModel") @SerialName("ErrorModel") - val errorModel: ErrorModel?, - @SerialName("errorModel") - val legacyErrorModel: LegacyErrorModel?, + private val errorModel: ErrorModel?, ) : GetTokenResponseJson() { /** * The error message returned from the server, or null. */ - val errorMessage: String? - get() = errorModel?.errorMessage ?: legacyErrorModel?.errorMessage + val errorMessage: String? get() = errorModel?.errorMessage /** * The type of invalid responses that can be received. @@ -131,24 +134,16 @@ sealed class GetTokenResponseJson { /** * The error body of an invalid request containing a message. + * + * This model supports older versions of the error response model that used lower-case + * keys. */ @Serializable data class ErrorModel( + @JsonNames("message") @SerialName("Message") val errorMessage: String, ) - - /** - * The legacy error body of an invalid request containing a message. - * - * This model is used to support older versions of the error response model that used - * lower-case keys. - */ - @Serializable - data class LegacyErrorModel( - @SerialName("message") - val errorMessage: String, - ) } /** diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/RegisterResponseJson.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/RegisterResponseJson.kt index bff33119442..73ad1ce4ec9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/RegisterResponseJson.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/model/RegisterResponseJson.kt @@ -1,7 +1,9 @@ package com.x8bit.bitwarden.data.auth.datasource.network.model +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonNames /** * Models response bodies for the register request. @@ -50,20 +52,24 @@ sealed class RegisterResponseJson { * The values in the array should be used for display to the user, since the keys tend to come * back as nonsense. (eg: empty string key) */ + @OptIn(ExperimentalSerializationApi::class) @Serializable data class Invalid( - @SerialName("message") - private val invalidMessage: String? = null, - + @JsonNames("message") @SerialName("Message") - private val errorMessage: String? = null, + private val invalidMessage: String? = null, @SerialName("validationErrors") - val validationErrors: Map>?, + private val validationErrors: Map>?, ) : RegisterResponseJson() { /** * A generic error message. */ - val message: String? get() = invalidMessage ?: errorMessage + val message: String? + get() = validationErrors + ?.values + ?.firstOrNull() + ?.firstOrNull() + ?: invalidMessage } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/AccountsServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/AccountsServiceImpl.kt index 35c44c52c1b..54702bb5e3f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/AccountsServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/AccountsServiceImpl.kt @@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequest import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyOtpRequestJson import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_BEARER_PREFIX +import com.x8bit.bitwarden.data.platform.datasource.network.util.NetworkErrorCode import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull import com.x8bit.bitwarden.data.platform.datasource.network.util.toResult import kotlinx.serialization.json.Json @@ -73,7 +74,7 @@ class AccountsServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -104,7 +105,7 @@ class AccountsServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 429, + code = NetworkErrorCode.TOO_MANY_REQUESTS, json = json, ) ?: throw throwable diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt index a4eae0f787b..fc701503592 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError +import com.x8bit.bitwarden.data.platform.datasource.network.util.NetworkErrorCode import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode import com.x8bit.bitwarden.data.platform.datasource.network.util.executeForNetworkResult import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull @@ -34,7 +35,6 @@ class IdentityServiceImpl( .preLogin(PreLoginRequestJson(email = email)) .toResult() - @Suppress("MagicNumber") override suspend fun register(body: RegisterRequestJson): Result = unauthenticatedIdentityApi .register(body) @@ -43,17 +43,19 @@ class IdentityServiceImpl( val bitwardenError = throwable.toBitwardenError() bitwardenError .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: bitwardenError.parseErrorBodyOrNull( - codes = listOf(400, 429), + codes = listOf( + NetworkErrorCode.BAD_REQUEST, + NetworkErrorCode.TOO_MANY_REQUESTS, + ), json = json, ) ?: throw throwable } - @Suppress("MagicNumber") override suspend fun getToken( uniqueAppId: String, email: String, @@ -87,21 +89,20 @@ class IdentityServiceImpl( val bitwardenError = throwable.toBitwardenError() bitwardenError .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: bitwardenError.parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: bitwardenError.parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable } - @Suppress("MagicNumber") override suspend fun prevalidateSso( organizationIdentifier: String, ): Result = unauthenticatedIdentityApi @@ -113,7 +114,7 @@ class IdentityServiceImpl( val bitwardenError = throwable.toBitwardenError() bitwardenError .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -130,7 +131,6 @@ class IdentityServiceImpl( .executeForNetworkResult() .toResult() - @Suppress("MagicNumber") override suspend fun registerFinish( body: RegisterFinishRequestJson, ): Result = @@ -141,13 +141,15 @@ class IdentityServiceImpl( val bitwardenError = throwable.toBitwardenError() bitwardenError .parseErrorBodyOrNull( - codes = listOf(400, 429), + codes = listOf( + NetworkErrorCode.BAD_REQUEST, + NetworkErrorCode.TOO_MANY_REQUESTS, + ), json = json, ) ?: throw throwable } - @Suppress("MagicNumber") override suspend fun sendVerificationEmail( body: SendVerificationEmailRequestJson, ): Result { @@ -159,14 +161,13 @@ class IdentityServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable } } - @Suppress("MagicNumber") override suspend fun verifyEmailRegistrationToken( body: VerifyEmailTokenRequestJson, ): Result = unauthenticatedIdentityApi @@ -179,7 +180,7 @@ class IdentityServiceImpl( val bitwardenError = throwable.toBitwardenError() bitwardenError .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?.checkForExpiredMessage() diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 7a4c4c46b19..70f92c4873d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -910,14 +910,7 @@ class AuthRepositoryImpl( } is RegisterResponseJson.Invalid -> { - RegisterResult.Error( - errorMessage = it - .validationErrors - ?.values - ?.firstOrNull() - ?.firstOrNull() - ?: it.message, - ) + RegisterResult.Error(errorMessage = it.message) } } }, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt index ced0336bf0d..86dbbf8d301 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt @@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType * @property name The name of the organization (if applicable). * @property shouldManageResetPassword Indicates that this user has the permission to manage their * own password. + * @property shouldManagePolicies Indicates that this user has the permission to manage policies. * @property shouldUseKeyConnector Indicates that the organization uses a key connector. * @property role The user's role in the organization. */ @@ -16,6 +17,7 @@ data class Organization( val id: String, val name: String?, val shouldManageResetPassword: Boolean, + val shouldManagePolicies: Boolean, val shouldUseKeyConnector: Boolean, val role: OrganizationType, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt index c124a136387..ac665edc955 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt @@ -22,6 +22,7 @@ fun SyncResponseJson.Profile.Organization.toOrganization(): Organization = shouldUseKeyConnector = this.shouldUseKeyConnector, role = this.type, shouldManageResetPassword = this.permissions.shouldManageResetPassword, + shouldManagePolicies = this.permissions.shouldManagePolicies, ) /** diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/ExceptionExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/ExceptionExtensions.kt index ce6b9a3e588..e0fa602946f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/ExceptionExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/ExceptionExtensions.kt @@ -17,9 +17,12 @@ import retrofit2.HttpException * will be attempted to be parsed. * @param json [Json] serializer to use. */ -inline fun BitwardenError.parseErrorBodyOrNull(codes: List, json: Json): T? = +inline fun BitwardenError.parseErrorBodyOrNull( + codes: List, + json: Json, +): T? = (this as? BitwardenError.Http) - ?.takeIf { codes.any { it == this.code } } + ?.takeIf { codes.any { it.code == this.code } } ?.responseBodyString ?.let { responseBody -> json.decodeFromStringOrNull(responseBody) @@ -28,5 +31,7 @@ inline fun BitwardenError.parseErrorBodyOrNull(codes: List, jso /** * Helper for calling [parseErrorBodyOrNull] with a single code. */ -inline fun BitwardenError.parseErrorBodyOrNull(code: Int, json: Json): T? = - parseErrorBodyOrNull(listOf(code), json) +inline fun BitwardenError.parseErrorBodyOrNull( + code: NetworkErrorCode, + json: Json, +): T? = parseErrorBodyOrNull(codes = listOf(code), json = json) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/NetworkErrorCode.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/NetworkErrorCode.kt new file mode 100644 index 00000000000..7f4e8bb2507 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/util/NetworkErrorCode.kt @@ -0,0 +1,11 @@ +package com.x8bit.bitwarden.data.platform.datasource.network.util + +/** + * An enum that represents HTTP error codes that we may need to parse for specific responses. + */ +enum class NetworkErrorCode( + val code: Int, +) { + BAD_REQUEST(code = 400), + TOO_MANY_REQUESTS(code = 429), +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt index 09c46cba892..7faec25b8fd 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt @@ -31,7 +31,6 @@ sealed class FlagKey { OnboardingFlow, OnboardingCarousel, ImportLoginsFlow, - SshKeyCipherItems, VerifiedSsoDomainEndpoint, CredentialExchangeProtocolImport, CredentialExchangeProtocolExport, @@ -94,15 +93,6 @@ sealed class FlagKey { override val isRemotelyConfigured: Boolean = true } - /** - * Data object holding the feature flag key for the SSH key cipher items feature. - */ - data object SshKeyCipherItems : FlagKey() { - override val keyName: String = "ssh-key-vault-item" - override val defaultValue: Boolean = false - override val isRemotelyConfigured: Boolean = true - } - /** * Data object holding the feature flag key for the new verified SSO domain endpoint feature. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/CiphersServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/CiphersServiceImpl.kt index 3e4fc2d836a..1170a2c6102 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/CiphersServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/CiphersServiceImpl.kt @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.datasource.network.service import androidx.core.net.toUri import com.bitwarden.vault.Attachment import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError +import com.x8bit.bitwarden.data.platform.datasource.network.util.NetworkErrorCode import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull import com.x8bit.bitwarden.data.platform.datasource.network.util.toResult import com.x8bit.bitwarden.data.platform.util.asFailure @@ -110,7 +111,7 @@ class CiphersServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -229,7 +230,7 @@ class CiphersServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/FolderServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/FolderServiceImpl.kt index c1d7090fc7e..d76941df947 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/FolderServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/FolderServiceImpl.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.vault.datasource.network.service import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError +import com.x8bit.bitwarden.data.platform.datasource.network.util.NetworkErrorCode import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull import com.x8bit.bitwarden.data.platform.datasource.network.util.toResult import com.x8bit.bitwarden.data.vault.datasource.network.api.FoldersApi @@ -33,7 +34,7 @@ class FolderServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SendsServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SendsServiceImpl.kt index 63647394c51..313867860b5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SendsServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SendsServiceImpl.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.datasource.network.service import androidx.core.net.toUri import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError +import com.x8bit.bitwarden.data.platform.datasource.network.util.NetworkErrorCode import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull import com.x8bit.bitwarden.data.platform.datasource.network.util.toResult import com.x8bit.bitwarden.data.vault.datasource.network.api.AzureApi @@ -42,7 +43,7 @@ class SendsServiceImpl( .recoverCatching { throwable -> throwable.toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -58,7 +59,7 @@ class SendsServiceImpl( .recoverCatching { throwable -> throwable.toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -79,7 +80,7 @@ class SendsServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable @@ -143,7 +144,7 @@ class SendsServiceImpl( throwable .toBitwardenError() .parseErrorBodyOrNull( - code = 400, + code = NetworkErrorCode.BAD_REQUEST, json = json, ) ?: throw throwable diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt index 3e44ce9c828..1e3ad263417 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt @@ -229,7 +229,7 @@ private fun TwoFactorLoginScreenContent( .imePadding() .verticalScroll(rememberScrollState()), ) { - if (state.authMethod == TwoFactorAuthMethod.EMAIL) { + if (state.authMethod != TwoFactorAuthMethod.YUBI_KEY) { state.imageRes?.let { Spacer(modifier = Modifier.height(12.dp)) Image( @@ -237,7 +237,7 @@ private fun TwoFactorLoginScreenContent( contentDescription = null, modifier = Modifier .standardHorizontalMargin() - .size(100.dp), + .size(124.dp), ) Spacer(modifier = Modifier.height(12.dp)) } @@ -261,7 +261,7 @@ private fun TwoFactorLoginScreenContent( Spacer(modifier = Modifier.height(12.dp)) - if (state.authMethod != TwoFactorAuthMethod.EMAIL) { + if (state.authMethod == TwoFactorAuthMethod.YUBI_KEY) { state.imageRes?.let { Spacer(modifier = Modifier.height(12.dp)) Image( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensions.kt index 8d3abba9a14..a5eeadc8e61 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensions.kt @@ -119,5 +119,6 @@ val TwoFactorAuthMethod.imageRes: Int? get() = when (this) { TwoFactorAuthMethod.YUBI_KEY -> R.drawable.yubi_key TwoFactorAuthMethod.EMAIL -> R.drawable.img_new_device_verification + TwoFactorAuthMethod.AUTHENTICATOR_APP -> R.drawable.img_authenticator else -> null } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt index 9b999012697..49dfebf0057 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt @@ -29,7 +29,6 @@ fun FlagKey.ListItemContent( FlagKey.OnboardingCarousel, FlagKey.OnboardingFlow, FlagKey.ImportLoginsFlow, - FlagKey.SshKeyCipherItems, FlagKey.VerifiedSsoDomainEndpoint, FlagKey.CredentialExchangeProtocolImport, FlagKey.CredentialExchangeProtocolExport, @@ -87,7 +86,6 @@ private fun FlagKey.getDisplayLabel(): String = when (this) { FlagKey.OnboardingCarousel -> stringResource(R.string.onboarding_carousel) FlagKey.OnboardingFlow -> stringResource(R.string.onboarding_flow) FlagKey.ImportLoginsFlow -> stringResource(R.string.import_logins_flow) - FlagKey.SshKeyCipherItems -> stringResource(R.string.ssh_key_cipher_item_types) FlagKey.VerifiedSsoDomainEndpoint -> stringResource(R.string.verified_sso_domain_verified) FlagKey.CredentialExchangeProtocolImport -> stringResource(R.string.cxp_import) FlagKey.CredentialExchangeProtocolExport -> stringResource(R.string.cxp_export) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt index 8deaf6be257..d189a23076c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult import com.x8bit.bitwarden.data.auth.repository.util.policyInformation @@ -21,7 +22,9 @@ import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow +import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson +import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.util.Text @@ -113,10 +116,10 @@ class AccountSecurityViewModel @Inject constructor( .launchIn(viewModelScope) policyManager - .getActivePoliciesFlow(type = PolicyTypeJson.REMOVE_UNLOCK_WITH_PIN) + .getActivePoliciesFlow(type = (PolicyTypeJson.REMOVE_UNLOCK_WITH_PIN)) .map { policies -> AccountSecurityAction.Internal.RemovePinPolicyUpdateReceive( - removeUnlockWithPinPolicyEnabled = policies.isNotEmpty(), + removeUnlockWithPinPolicyEnabled = arePoliciesValid(policies), ) } .onEach(::sendAction) @@ -166,6 +169,20 @@ class AccountSecurityViewModel @Inject constructor( } } + private fun arePoliciesValid(policies: List) = + policies.any { policy -> + authRepository.userStateFlow.value?.activeAccount?.organizations?.any { org -> + (policy.organizationId == org.id) && !isExemptFromPolicy(org) + } == true + } + + private fun isExemptFromPolicy( + userOrg: Organization, + ) = userOrg.role == OrganizationType.OWNER || + userOrg.role == OrganizationType.ADMIN || + (userOrg.role == OrganizationType.CUSTOM && + userOrg.shouldManagePolicies) + override fun handleAction(action: AccountSecurityAction): Unit = when (action) { AccountSecurityAction.AccountFingerprintPhraseClick -> handleAccountFingerprintPhraseClick() is AccountSecurityAction.AuthenticatorSyncToggle -> handleAuthenticatorSyncToggle(action) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreen.kt index fb2d61c7c8c..13b5b729da5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreen.kt @@ -47,6 +47,7 @@ import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager +import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.ChromeAutofillSettingsCard import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.displayLabel import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager import kotlinx.collections.immutable.toImmutableList @@ -95,6 +96,11 @@ fun AutoFillScreen( } AutoFillEvent.NavigateToSetupAutofill -> onNavigateToSetupAutofill() + is AutoFillEvent.NavigateToChromeAutofillSettings -> { + intentManager.startChromeAutofillSettingsActivity( + releaseChannel = event.releaseChannel, + ) + } } } @@ -139,7 +145,7 @@ fun AutoFillScreen( actionText = stringResource(R.string.get_started), onActionClick = remember(viewModel) { { - viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick) + viewModel.trySendAction(AutoFillAction.AutofillActionCardCtaClick) } }, onDismissClick = remember(viewModel) { @@ -196,6 +202,20 @@ fun AutoFillScreen( ) Spacer(modifier = Modifier.height(height = 8.dp)) } + + if (state.chromeAutofillSettingsOptions.isNotEmpty()) { + ChromeAutofillSettingsCard( + options = state.chromeAutofillSettingsOptions, + onOptionClicked = remember(viewModel) { + { + viewModel.trySendAction(AutoFillAction.ChromeAutofillSelected(it)) + } + }, + enabled = state.isAutoFillServicesEnabled, + ) + Spacer(modifier = Modifier.height(8.dp)) + } + if (state.showPasskeyManagementRow) { BitwardenExternalLinkRow( text = stringResource(id = R.string.passkey_management), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModel.kt index 918c8a3d9d3..164b682597f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModel.kt @@ -5,13 +5,20 @@ import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.autofill.manager.chrome.ChromeThirdPartyAutofillEnabledManager +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeThirdPartyAutofillStatus import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.util.Text +import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.model.ChromeAutofillSettingsOption +import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -28,6 +35,7 @@ private const val KEY_STATE = "state" @HiltViewModel class AutoFillViewModel @Inject constructor( authRepository: AuthRepository, + chromeThirdPartyAutofillEnabledManager: ChromeThirdPartyAutofillEnabledManager, private val savedStateHandle: SavedStateHandle, private val settingsRepository: SettingsRepository, private val firstTimeActionManager: FirstTimeActionManager, @@ -50,6 +58,7 @@ class AutoFillViewModel @Inject constructor( defaultUriMatchType = settingsRepository.defaultUriMatchType, showAutofillActionCard = false, activeUserId = userId, + chromeAutofillSettingsOptions = persistentListOf(), ) }, ) { @@ -81,6 +90,12 @@ class AutoFillViewModel @Inject constructor( .map { AutoFillAction.Internal.UpdateShowAutofillActionCard(it.showSetupAutofillCard) } .onEach(::sendAction) .launchIn(viewModelScope) + + chromeThirdPartyAutofillEnabledManager + .chromeThirdPartyAutofillStatusFlow + .map { AutoFillAction.Internal.ChromeAutofillStatusReceive(status = it) } + .onEach(::sendAction) + .launchIn(viewModelScope) } override fun handleAction(action: AutoFillAction) = when (action) { @@ -94,8 +109,9 @@ class AutoFillViewModel @Inject constructor( is AutoFillAction.UseInlineAutofillClick -> handleUseInlineAutofillClick(action) AutoFillAction.PasskeyManagementClick -> handlePasskeyManagementClick() is AutoFillAction.Internal -> handleInternalAction(action) - AutoFillAction.AutoFillActionCardCtaClick -> handleAutoFillActionCardCtClick() + AutoFillAction.AutofillActionCardCtaClick -> handleAutofillActionCardCtaClick() AutoFillAction.DismissShowAutofillActionCard -> handleDismissShowAutofillActionCard() + is AutoFillAction.ChromeAutofillSelected -> handleChromeAutofillSelected(action) } private fun handleInternalAction(action: AutoFillAction.Internal) { @@ -111,14 +127,34 @@ class AutoFillViewModel @Inject constructor( is AutoFillAction.Internal.UpdateShowAutofillActionCard -> { handleUpdateShowAutofillActionCard(action) } + + is AutoFillAction.Internal.ChromeAutofillStatusReceive -> { + handleChromeAutofillStatusReceive(action) + } + } + } + + private fun handleChromeAutofillStatusReceive( + action: AutoFillAction.Internal.ChromeAutofillStatusReceive, + ) { + mutableStateFlow.update { + it.copy( + chromeAutofillSettingsOptions = action + .status + .toChromeAutoFillSettingsOptions(), + ) } } + private fun handleChromeAutofillSelected(action: AutoFillAction.ChromeAutofillSelected) { + sendEvent(AutoFillEvent.NavigateToChromeAutofillSettings(action.releaseChannel)) + } + private fun handleDismissShowAutofillActionCard() { dismissShowAutofillActionCard() } - private fun handleAutoFillActionCardCtClick() { + private fun handleAutofillActionCardCtaClick() { sendEvent(AutoFillEvent.NavigateToSetupAutofill) } @@ -216,6 +252,7 @@ data class AutoFillState( val defaultUriMatchType: UriMatchType, val showAutofillActionCard: Boolean, val activeUserId: String, + val chromeAutofillSettingsOptions: ImmutableList, ) : Parcelable { /** @@ -226,6 +263,19 @@ data class AutoFillState( get() = isAutoFillServicesEnabled } +@Suppress("MaxLineLength") +private fun ChromeThirdPartyAutofillStatus.toChromeAutoFillSettingsOptions(): ImmutableList = + persistentListOfNotNull( + ChromeAutofillSettingsOption.Stable( + enabled = this.stableStatusData.isThirdPartyEnabled, + ) + .takeIf { this.stableStatusData.isAvailable }, + ChromeAutofillSettingsOption.Beta( + enabled = this.betaChannelStatusData.isThirdPartyEnabled, + ) + .takeIf { this.betaChannelStatusData.isAvailable }, + ) + /** * Models events for the auto-fill screen. */ @@ -262,6 +312,13 @@ sealed class AutoFillEvent { val text: Text, ) : AutoFillEvent() + /** + * Navigate to the Autofill settings of the specified [releaseChannel]. + */ + data class NavigateToChromeAutofillSettings( + val releaseChannel: ChromeReleaseChannel, + ) : AutoFillEvent() + /** * Navigates to the setup autofill screen. */ @@ -335,7 +392,12 @@ sealed class AutoFillAction { /** * User has clicked the CTA on the autofill action card. */ - data object AutoFillActionCardCtaClick : AutoFillAction() + data object AutofillActionCardCtaClick : AutoFillAction() + + /** + * User has clicked one of the chrome autofill options. + */ + data class ChromeAutofillSelected(val releaseChannel: ChromeReleaseChannel) : AutoFillAction() /** * Internal actions. @@ -359,5 +421,12 @@ sealed class AutoFillAction { * An update for changes in the [showAutofillActionCard] value from the settings repository. */ data class UpdateShowAutofillActionCard(val showAutofillActionCard: Boolean) : Internal() + + /** + * Received updated [ChromeThirdPartyAutofillStatus] data. + */ + data class ChromeAutofillStatusReceive( + val status: ChromeThirdPartyAutofillStatus, + ) : Internal() } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/ChromeAutofillSettingsCard.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/ChromeAutofillSettingsCard.kt new file mode 100644 index 00000000000..e072dd9b00b --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/ChromeAutofillSettingsCard.kt @@ -0,0 +1,94 @@ +package com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel +import com.x8bit.bitwarden.ui.platform.base.util.cardStyle +import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin +import com.x8bit.bitwarden.ui.platform.components.model.CardStyle +import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch +import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.model.ChromeAutofillSettingsOption +import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +/** + * Card for displaying a list of [ChromeAutofillSettingsOption]s and whether they are + * currently enabled. + * + * @param options List of data to display in the card, if the list is empty nothing will be drawn. + * @param onOptionClicked Lambda that is invoked when an option row is clicked and passes back the + * [ChromeReleaseChannel] for that option. + * @param enabled Whether to show the switches for each option as enabled. + */ +@Composable +fun ChromeAutofillSettingsCard( + options: ImmutableList, + onOptionClicked: (ChromeReleaseChannel) -> Unit, + enabled: Boolean, + modifier: Modifier = Modifier, +) { + if (options.isEmpty()) return + Column(modifier = modifier) { + options.forEachIndexed { index, option -> + BitwardenSwitch( + label = option.optionText(), + isChecked = option.isEnabled, + onCheckedChange = { + onOptionClicked(option.chromeReleaseChannel) + }, + cardStyle = if (index == 0) { + CardStyle.Top( + dividerPadding = 16.dp, + ) + } else { + CardStyle.Middle( + dividerPadding = 16.dp, + ) + }, + enabled = enabled, + modifier = Modifier + .fillMaxWidth() + .standardHorizontalMargin(), + ) + } + Text( + text = stringResource( + R.string.improves_login_filling_for_supported_websites_on_chrome, + ), + style = BitwardenTheme.typography.bodyMedium, + color = BitwardenTheme.colorScheme.text.secondary, + modifier = Modifier + .fillMaxWidth() + .standardHorizontalMargin() + .cardStyle( + cardStyle = CardStyle.Bottom, + paddingHorizontal = 16.dp, + ) + .defaultMinSize(minHeight = 48.dp), + ) + } +} + +@Preview +@Composable +private fun ChromeAutofillSettingsCard_preview() { + BitwardenTheme { + ChromeAutofillSettingsCard( + options = persistentListOf( + ChromeAutofillSettingsOption.Stable(enabled = false), + ChromeAutofillSettingsOption.Beta(enabled = true), + ), + enabled = true, + onOptionClicked = {}, + ) + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/model/ChromeAutofillSettingsOption.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/model/ChromeAutofillSettingsOption.kt new file mode 100644 index 00000000000..e3c463beeb3 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/chrome/model/ChromeAutofillSettingsOption.kt @@ -0,0 +1,42 @@ +package com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.model + +import android.os.Parcelable +import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel +import com.x8bit.bitwarden.ui.platform.base.util.Text +import com.x8bit.bitwarden.ui.platform.base.util.asText +import kotlinx.parcelize.Parcelize + +/** + * Models an option for an option for each type of supported version of Chrome to enable + * third party autofill. Each [ChromeAutofillSettingsOption] contains the associated + * [ChromeReleaseChannel], the [optionText] to display in any UI component, and + * whether or not the third party autofill [isEnabled]. + */ +@Parcelize +sealed class ChromeAutofillSettingsOption(val isEnabled: Boolean) : Parcelable { + abstract val chromeReleaseChannel: ChromeReleaseChannel + abstract val optionText: Text + + /** + * Represents the stable Chrome release channel. + */ + @Parcelize + data class Stable(val enabled: Boolean) : ChromeAutofillSettingsOption(isEnabled = enabled) { + override val chromeReleaseChannel: ChromeReleaseChannel + get() = ChromeReleaseChannel.STABLE + override val optionText: Text + get() = R.string.use_chrome_autofill_integration.asText() + } + + /** + * Represents the beta Chrome release channel. + */ + @Parcelize + data class Beta(val enabled: Boolean) : ChromeAutofillSettingsOption(isEnabled = enabled) { + override val chromeReleaseChannel: ChromeReleaseChannel + get() = ChromeReleaseChannel.BETA + override val optionText: Text + get() = R.string.use_chrome_beta_autofill_integration.asText() + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt index ff00823ca77..b43893b0bc4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt @@ -9,6 +9,7 @@ import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel import kotlinx.parcelize.Parcelize /** @@ -48,6 +49,11 @@ interface IntentManager { */ fun startCredentialManagerSettings(context: Context) + /** + * Starts the Chrome autofill settings activity for the provided [ChromeReleaseChannel]. + */ + fun startChromeAutofillSettingsActivity(releaseChannel: ChromeReleaseChannel): Boolean + /** * Start an activity to view the given [uri] in an external browser. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt index 3902676e627..c3a0bb92cfe 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt @@ -26,6 +26,7 @@ import androidx.credentials.CredentialManager import com.x8bit.bitwarden.BuildConfig import com.x8bit.bitwarden.MainActivity import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow @@ -133,6 +134,22 @@ class IntentManagerImpl( } } + override fun startChromeAutofillSettingsActivity( + releaseChannel: ChromeReleaseChannel, + ): Boolean = try { + val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES) + .apply { + addCategory(Intent.CATEGORY_DEFAULT) + addCategory(Intent.CATEGORY_APP_BROWSER) + addCategory(Intent.CATEGORY_PREFERENCE) + setPackage(releaseChannel.packageName) + } + context.startActivity(intent) + true + } catch (_: ActivityNotFoundException) { + false + } + override fun launchUri(uri: Uri) { if (uri.scheme.equals(other = "androidapp", ignoreCase = true)) { val packageName = uri.toString().removePrefix(prefix = "androidapp://") diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultContent.kt index 10a6e000303..74fdeeed3d1 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultContent.kt @@ -37,7 +37,6 @@ fun VaultContent( state: VaultState.ViewState.Content, vaultHandlers: VaultHandlers, onOverflowOptionClick: (action: ListingItemOverflowAction.VaultAction) -> Unit, - showSshKeys: Boolean, modifier: Modifier = Modifier, ) { LazyColumn( @@ -192,11 +191,7 @@ fun VaultContent( supportingLabel = state.secureNoteItemsCount.toString(), onClick = vaultHandlers.secureNoteGroupClick, showDivider = false, - cardStyle = if (showSshKeys) { - CardStyle.Middle(dividerPadding = 56.dp) - } else { - CardStyle.Bottom - }, + cardStyle = CardStyle.Middle(dividerPadding = 56.dp), modifier = Modifier .fillMaxWidth() .testTag("SecureNoteFilter") @@ -204,22 +199,20 @@ fun VaultContent( ) } - if (showSshKeys) { - item { - BitwardenGroupItem( - startIcon = rememberVectorPainter(id = R.drawable.ic_ssh_key), - startIconTestTag = "SshKeyCipherIcon", - label = stringResource(id = R.string.type_ssh_key), - supportingLabel = state.sshKeyItemsCount.toString(), - onClick = vaultHandlers.sshKeyGroupClick, - showDivider = false, - cardStyle = CardStyle.Bottom, - modifier = Modifier - .fillMaxWidth() - .testTag("SshKeyFilter") - .standardHorizontalMargin(), - ) - } + item { + BitwardenGroupItem( + startIcon = rememberVectorPainter(id = R.drawable.ic_ssh_key), + startIconTestTag = "SshKeyCipherIcon", + label = stringResource(id = R.string.type_ssh_key), + supportingLabel = state.sshKeyItemsCount.toString(), + onClick = vaultHandlers.sshKeyGroupClick, + showDivider = false, + cardStyle = CardStyle.Bottom, + modifier = Modifier + .fillMaxWidth() + .testTag("SshKeyFilter") + .standardHorizontalMargin(), + ) } item { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt index 5bbcbe6e3e0..91f52125a53 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt @@ -352,7 +352,6 @@ private fun VaultScreenScaffold( when (val viewState = state.viewState) { is VaultState.ViewState.Content -> VaultContent( state = viewState, - showSshKeys = state.showSshKeys, vaultHandlers = vaultHandlers, onOverflowOptionClick = { masterPasswordRepromptAction = it }, modifier = Modifier.fillMaxSize(), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index 9cfadc9a304..29f39f57a20 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -98,7 +98,6 @@ class VaultViewModel @Inject constructor( .any(), ) val appBarTitle = vaultFilterData.toAppBarTitle() - val showSshKeys = featureFlagManager.getFeatureFlag(FlagKey.SshKeyCipherItems) VaultState( appBarTitle = appBarTitle, initials = activeAccountSummary.initials, @@ -113,7 +112,6 @@ class VaultViewModel @Inject constructor( hasMasterPassword = userState.activeAccount.hasMasterPassword, isRefreshing = false, showImportActionCard = false, - showSshKeys = showSshKeys, ) }, ) { @@ -141,15 +139,9 @@ class VaultViewModel @Inject constructor( .onEach(::sendAction) .launchIn(viewModelScope) - combine( - vaultRepository.vaultDataStateFlow, - featureFlagManager.getFeatureFlagFlow(FlagKey.SshKeyCipherItems), - ) { vaultData, sshKeyCipherItemsEnabled -> - VaultAction.Internal.VaultDataReceive( - vaultData = vaultData, - showSshKeys = sshKeyCipherItemsEnabled, - ) - } + vaultRepository + .vaultDataStateFlow + .map { VaultAction.Internal.VaultDataReceive(it) } .onEach(::sendAction) .launchIn(viewModelScope) @@ -266,7 +258,6 @@ class VaultViewModel @Inject constructor( updateViewState( vaultData = vaultRepository.vaultDataStateFlow.value, - showSshKeys = state.showSshKeys, ) } @@ -405,7 +396,6 @@ class VaultViewModel @Inject constructor( // Re-process the current vault data with the new filter updateViewState( vaultData = vaultRepository.vaultDataStateFlow.value, - showSshKeys = state.showSshKeys, ) } @@ -694,22 +684,19 @@ class VaultViewModel @Inject constructor( updateViewState( vaultData = action.vaultData, - showSshKeys = action.showSshKeys, ) } - private fun updateViewState(vaultData: DataState, showSshKeys: Boolean) { + private fun updateViewState(vaultData: DataState) { when (vaultData) { is DataState.Error -> vaultErrorReceive(vaultData = vaultData) is DataState.Loaded -> vaultLoadedReceive( vaultData = vaultData, - showSshKeys = showSshKeys, ) is DataState.Loading -> vaultLoadingReceive() is DataState.NoNetwork -> vaultNoNetworkReceive( vaultData = vaultData, - showSshKeys = showSshKeys, ) is DataState.Pending -> vaultPendingReceive(vaultData = vaultData) @@ -730,7 +717,7 @@ class VaultViewModel @Inject constructor( ) } - private fun vaultLoadedReceive(vaultData: DataState.Loaded, showSshKeys: Boolean) { + private fun vaultLoadedReceive(vaultData: DataState.Loaded) { if (state.dialog == VaultState.DialogState.Syncing) { sendEvent( VaultEvent.ShowToast( @@ -738,12 +725,11 @@ class VaultViewModel @Inject constructor( ), ) } - updateVaultState(vaultData.data, showSshKeys) + updateVaultState(vaultData.data) } private fun updateVaultState( vaultData: VaultData, - showSshKeys: Boolean, ) { mutableStateFlow.update { it.copy( @@ -753,11 +739,9 @@ class VaultViewModel @Inject constructor( isPremium = state.isPremium, hasMasterPassword = state.hasMasterPassword, vaultFilterType = vaultFilterTypeOrDefault, - showSshKeys = showSshKeys, ), dialog = null, isRefreshing = false, - showSshKeys = showSshKeys, ) } } @@ -768,7 +752,6 @@ class VaultViewModel @Inject constructor( private fun vaultNoNetworkReceive( vaultData: DataState.NoNetwork, - showSshKeys: Boolean, ) { val data = vaultData.data ?: VaultData( cipherViewList = emptyList(), @@ -778,7 +761,6 @@ class VaultViewModel @Inject constructor( ) updateVaultState( vaultData = data, - showSshKeys = showSshKeys, ) } @@ -791,7 +773,6 @@ class VaultViewModel @Inject constructor( isPremium = state.isPremium, hasMasterPassword = state.hasMasterPassword, vaultFilterType = vaultFilterTypeOrDefault, - showSshKeys = state.showSshKeys, ), ) } @@ -862,7 +843,6 @@ data class VaultState( val isIconLoadingDisabled: Boolean, val isRefreshing: Boolean, val showImportActionCard: Boolean, - val showSshKeys: Boolean, ) : Parcelable { /** @@ -1486,7 +1466,6 @@ sealed class VaultAction { */ data class VaultDataReceive( val vaultData: DataState, - val showSshKeys: Boolean, ) : Internal() /** @@ -1527,7 +1506,6 @@ private fun MutableStateFlow.updateToErrorStateOrDialog( hasMasterPassword = hasMasterPassword, vaultFilterType = vaultFilterType, isIconLoadingDisabled = isIconLoadingDisabled, - showSshKeys = it.showSshKeys, ), dialog = VaultState.DialogState.Error( title = errorTitle, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt index 4cc2f950259..7949949423d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt @@ -39,7 +39,6 @@ fun VaultData.toViewState( isIconLoadingDisabled: Boolean, baseIconUrl: String, vaultFilterType: VaultFilterType, - showSshKeys: Boolean, ): VaultState.ViewState { val filteredCipherViewListWithDeletedItems = @@ -47,7 +46,6 @@ fun VaultData.toViewState( val filteredCipherViewList = filteredCipherViewListWithDeletedItems .filter { it.deletedDate == null } - .filterSshKeysIfNecessary(showSshKeys) val filteredFolderViewList = folderViewList .toFilteredList( @@ -63,12 +61,7 @@ fun VaultData.toViewState( val noFolderItems = filteredCipherViewList .filter { it.folderId.isNullOrBlank() } - val itemTypesCount: Int = if (showSshKeys) { - CipherType.entries - } else { - CipherType.entries.filterNot { it == CipherType.SSH_KEY } - } - .size + val itemTypesCount: Int = CipherType.entries.size return if (filteredCipherViewListWithDeletedItems.isEmpty()) { VaultState.ViewState.NoItems @@ -355,16 +348,3 @@ fun List.toFilteredList( } } } - -/** - * Filters out all [CipherView]s that are of type [CipherType.SSH_KEY] if [showSshKeys] is false. - * - * @param showSshKeys Whether to show SSH keys in the vault. - */ -@JvmName("filterSshKeysIfNecessary") -fun List.filterSshKeysIfNecessary(showSshKeys: Boolean): List = - if (showSshKeys) { - this - } else { - filter { it.type != CipherType.SSH_KEY } - } diff --git a/app/src/main/res/drawable-night/img_authenticator.xml b/app/src/main/res/drawable-night/img_authenticator.xml new file mode 100644 index 00000000000..64b35b183e3 --- /dev/null +++ b/app/src/main/res/drawable-night/img_authenticator.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/img_authenticator.xml b/app/src/main/res/drawable/img_authenticator.xml new file mode 100644 index 00000000000..18174ceadfe --- /dev/null +++ b/app/src/main/res/drawable/img_authenticator.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f90740df5f6..e35128786e1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1104,7 +1104,6 @@ Do you want to switch to this account? Save the exported file somewhere on your computer you can find easily. This is not a recognized Bitwarden server. You may need to check with your provider or update your server. Syncing logins... - SSH Key Cipher Item Types Download the browser extension Go to bitwarden.com/download to integrate Bitwarden into your favorite browser for a seamless experience. Use the web app @@ -1221,4 +1220,7 @@ Do you want to switch to this account? Passkey operation failed because user verification attempts exceeded. Passkey operation failed because no item was selected. Self-host server URL + Use Chrome autofill integration + Use Chrome autofill integration (Beta) + Improves login filling for supported websites on Chrome. Once enabled, you’ll be directed to Chrome settings to enable third-party autofill. diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt index a0a92d72a79..d1657d08588 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt @@ -168,7 +168,7 @@ class IdentityServiceTest : BaseServiceTest() { val result = identityService.register(registerRequestBody) assertEquals( RegisterResponseJson.Invalid( - errorMessage = "Slow down! Too many requests. Try again soon.", + invalidMessage = "Slow down! Too many requests. Try again soon.", validationErrors = null, ), result.getOrThrow(), @@ -287,7 +287,7 @@ class IdentityServiceTest : BaseServiceTest() { captchaToken = null, uniqueAppId = UNIQUE_APP_ID, ) - assertEquals(LEGACY_INVALID_LOGIN.asSuccess(), result) + assertEquals(INVALID_LOGIN.asSuccess(), result) } @Suppress("MaxLineLength") @@ -363,7 +363,7 @@ class IdentityServiceTest : BaseServiceTest() { val result = identityService.registerFinish(registerFinishRequestBody) assertEquals( RegisterResponseJson.Invalid( - errorMessage = "Slow down! Too many requests. Try again soon.", + invalidMessage = "Slow down! Too many requests. Try again soon.", validationErrors = null, ), result.getOrThrow(), @@ -651,7 +651,7 @@ private const val INVALID_LOGIN_JSON = """ private const val LEGACY_INVALID_LOGIN_JSON = """ { "errorModel": { - "message": "Legacy-123" + "message": "123" } } """ @@ -688,14 +688,6 @@ private val INVALID_LOGIN = GetTokenResponseJson.Invalid( errorModel = GetTokenResponseJson.Invalid.ErrorModel( errorMessage = "123", ), - legacyErrorModel = null, -) - -private val LEGACY_INVALID_LOGIN = GetTokenResponseJson.Invalid( - errorModel = null, - legacyErrorModel = GetTokenResponseJson.Invalid.LegacyErrorModel( - errorMessage = "Legacy-123", - ), ) private val SEND_VERIFICATION_EMAIL_REQUEST = SendVerificationEmailRequestJson( diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index b17531cb1c4..e37554a0aff 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -1574,7 +1574,6 @@ class AuthRepositoryTest { errorModel = GetTokenResponseJson.Invalid.ErrorModel( errorMessage = "mock_error_message", ), - legacyErrorModel = null, ) .asSuccess() @@ -1617,7 +1616,6 @@ class AuthRepositoryTest { errorModel = GetTokenResponseJson.Invalid.ErrorModel( errorMessage = "new device verification required", ), - legacyErrorModel = null, ) .asSuccess() @@ -2401,7 +2399,6 @@ class AuthRepositoryTest { errorModel = GetTokenResponseJson.Invalid.ErrorModel( errorMessage = "mock_error_message", ), - legacyErrorModel = null, ) .asSuccess() @@ -2870,7 +2867,6 @@ class AuthRepositoryTest { errorModel = GetTokenResponseJson.Invalid.ErrorModel( errorMessage = "mock_error_message", ), - legacyErrorModel = null, ) .asSuccess() @@ -4454,6 +4450,7 @@ class AuthRepositoryTest { every { name } returns "orgName" every { permissions } returns mockk { every { shouldManageResetPassword } returns false + every { shouldManagePolicies } returns false } every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER @@ -4479,6 +4476,7 @@ class AuthRepositoryTest { every { name } returns "orgName" every { permissions } returns mockk { every { shouldManageResetPassword } returns false + every { shouldManagePolicies } returns false } every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER @@ -4515,6 +4513,7 @@ class AuthRepositoryTest { every { name } returns "orgName" every { permissions } returns mockk { every { shouldManageResetPassword } returns false + every { shouldManagePolicies } returns false } every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt index 5c2057461e9..22fd6e99ea1 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt @@ -194,6 +194,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldManagePolicies = false, ), ), ), @@ -204,6 +205,7 @@ class AuthDiskSourceExtensionsTest { id = "mockId-2", name = "mockName-2", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -216,6 +218,7 @@ class AuthDiskSourceExtensionsTest { id = "mockId-3", name = "mockName-3", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -363,6 +366,7 @@ class AuthDiskSourceExtensionsTest { id = "mockId-1", name = "mockName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -394,6 +398,7 @@ class AuthDiskSourceExtensionsTest { id = "mockId-1", name = "mockName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -406,6 +411,7 @@ class AuthDiskSourceExtensionsTest { id = "mockId-2", name = "mockName-2", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt index 4eed0b800bb..c09266428fd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt @@ -21,6 +21,7 @@ class SyncResponseJsonExtensionsTest { id = "mockId-1", name = "mockName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -36,6 +37,7 @@ class SyncResponseJsonExtensionsTest { id = "mockId-1", name = "mockName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = true, role = OrganizationType.ADMIN, ), @@ -43,6 +45,7 @@ class SyncResponseJsonExtensionsTest { id = "mockId-2", name = "mockName-2", shouldManageResetPassword = true, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.USER, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index d0e8580c487..a4f48c0342a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -364,6 +364,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -428,6 +429,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -472,6 +474,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -532,6 +535,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -577,6 +581,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -645,6 +650,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -690,6 +696,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -758,6 +765,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -803,6 +811,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -871,6 +880,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -917,6 +927,7 @@ class UserStateJsonExtensionsTest { // Key part of the result #1, this is true or the role is owner or // admin shouldManageResetPassword = true, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.USER, ), @@ -988,6 +999,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = true, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.USER, ), @@ -1192,6 +1204,7 @@ class UserStateJsonExtensionsTest { // Key part of the result #1, this is true or the role is owner or // admin shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.USER, ), @@ -1262,6 +1275,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.USER, ), @@ -1307,6 +1321,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -1377,6 +1392,7 @@ class UserStateJsonExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/base/util/ColorExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/base/util/ColorExtensionsTest.kt index 31a55b99401..f3c61ad8bb0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/base/util/ColorExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/base/util/ColorExtensionsTest.kt @@ -26,7 +26,7 @@ class ColorExtensionsTest : BaseComposeTest() { @Test fun `toSafeOverlayColor for a dark color in light mode should use the surface color`() = - runTestWithTheme(theme = AppTheme.LIGHT) { + setContent(theme = AppTheme.LIGHT) { assertEquals( BitwardenTheme.colorScheme.background.primary, Color.Blue.toSafeOverlayColor(), @@ -35,7 +35,7 @@ class ColorExtensionsTest : BaseComposeTest() { @Test fun `toSafeOverlayColor for a dark color in dark mode should use the onSurface color`() = - runTestWithTheme(theme = AppTheme.DARK) { + setContent(theme = AppTheme.DARK) { assertEquals( BitwardenTheme.colorScheme.text.primary, Color.Blue.toSafeOverlayColor(), @@ -44,7 +44,7 @@ class ColorExtensionsTest : BaseComposeTest() { @Test fun `toSafeOverlayColor for a light color in light mode should use the onSurface color`() = - runTestWithTheme(theme = AppTheme.LIGHT) { + setContent(theme = AppTheme.LIGHT) { assertEquals( BitwardenTheme.colorScheme.text.primary, Color.Yellow.toSafeOverlayColor(), @@ -53,7 +53,7 @@ class ColorExtensionsTest : BaseComposeTest() { @Test fun `toSafeOverlayColor for a light color in dark mode should use the surface color`() = - runTestWithTheme(theme = AppTheme.DARK) { + setContent(theme = AppTheme.DARK) { assertEquals( BitwardenTheme.colorScheme.background.primary, Color.Yellow.toSafeOverlayColor(), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt index 19d87d7ba2a..c22af73bc14 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt @@ -29,10 +29,6 @@ class FlagKeyTest { FlagKey.ImportLoginsFlow.keyName, "import-logins-flow", ) - assertEquals( - FlagKey.SshKeyCipherItems.keyName, - "ssh-key-vault-item", - ) assertEquals( FlagKey.VerifiedSsoDomainEndpoint.keyName, "pm-12337-refactor-sso-details-endpoint", @@ -96,7 +92,6 @@ class FlagKeyTest { FlagKey.OnboardingCarousel, FlagKey.OnboardingFlow, FlagKey.ImportLoginsFlow, - FlagKey.SshKeyCipherItems, FlagKey.VerifiedSsoDomainEndpoint, FlagKey.CredentialExchangeProtocolImport, FlagKey.CredentialExchangeProtocolExport, @@ -124,7 +119,6 @@ class FlagKeyTest { FlagKey.OnboardingCarousel, FlagKey.OnboardingFlow, FlagKey.ImportLoginsFlow, - FlagKey.SshKeyCipherItems, FlagKey.VerifiedSsoDomainEndpoint, FlagKey.CredentialExchangeProtocolImport, FlagKey.CredentialExchangeProtocolExport, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupAutofillScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupAutofillScreenTest.kt index 805e984a707..55ccab043bc 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupAutofillScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupAutofillScreenTest.kt @@ -37,7 +37,7 @@ class SetupAutofillScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { SetupAutoFillScreen( intentManager = intentManager, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupCompleteScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupCompleteScreenTest.kt index ebcba94ced1..a5043e267d6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupCompleteScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupCompleteScreenTest.kt @@ -15,7 +15,7 @@ class SetupCompleteScreenTest : BaseComposeTest() { @Before fun setup() { - setContentWithBackDispatcher { + setContent { SetupCompleteScreen(viewModel = viewModel) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockScreenTest.kt index 5426e46f4e9..50f3e89a80a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockScreenTest.kt @@ -65,7 +65,7 @@ class SetupUnlockScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { SetupUnlockScreen( viewModel = viewModel, biometricsManager = biometricsManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreenTest.kt index ed735099ede..3250505b437 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreenTest.kt @@ -32,7 +32,7 @@ class CheckEmailScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { CheckEmailScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/CompleteRegistrationScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/CompleteRegistrationScreenTest.kt index 39a364044a5..eadf4d46070 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/CompleteRegistrationScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/CompleteRegistrationScreenTest.kt @@ -55,7 +55,7 @@ class CompleteRegistrationScreenTest : BaseComposeTest() { @Before fun setup() { - setContentWithBackDispatcher { + setContent { CompleteRegistrationScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToPasswordGuidance = { onNavigateToPasswordGuidanceCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicatorTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicatorTest.kt index 0196f8771a2..510cf32121d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicatorTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicatorTest.kt @@ -10,7 +10,7 @@ class PasswordStrengthIndicatorTest : BaseComposeTest() { @Suppress("MaxLineLength") @Test fun `PasswordStrengthIndicator with minimum character count met displays minimum character count`() { - composeTestRule.setContent { + setContent { PasswordStrengthIndicator( state = PasswordStrengthState.WEAK_3, currentCharacterCount = 12, @@ -27,7 +27,7 @@ class PasswordStrengthIndicatorTest : BaseComposeTest() { @Suppress("MaxLineLength") @Test fun `PasswordStrengthIndicator with no minimum character count met does not minimum character count`() { - composeTestRule.setContent { + setContent { PasswordStrengthIndicator( state = PasswordStrengthState.WEAK_3, currentCharacterCount = 12, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreenTest.kt index f462735345b..cc8045ee654 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreenTest.kt @@ -61,7 +61,7 @@ class CreateAccountScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { CreateAccountScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToLogin = { _, _ -> onNavigateToLoginCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt index f44acde739b..d5470ba0994 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt @@ -47,7 +47,7 @@ class EnterpriseSignOnScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { EnterpriseSignOnScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToSetPassword = { onNavigateToSetPasswordCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/environment/EnvironmentScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/environment/EnvironmentScreenTest.kt index 71d12437b68..31ce1f3607d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/environment/EnvironmentScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/environment/EnvironmentScreenTest.kt @@ -47,7 +47,7 @@ class EnvironmentScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { EnvironmentScreen( onNavigateBack = { onNavigateBackCalled = true }, intentManager = mockIntentManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/expiredregistrationlink/ExpiredRegistrationLinkScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/expiredregistrationlink/ExpiredRegistrationLinkScreenTest.kt index e76a6f6ec2b..6bbc7c46b9a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/expiredregistrationlink/ExpiredRegistrationLinkScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/expiredregistrationlink/ExpiredRegistrationLinkScreenTest.kt @@ -25,7 +25,7 @@ class ExpiredRegistrationLinkScreenTest : BaseComposeTest() { @Before fun setUp() { - setContentWithBackDispatcher { + setContent { ExpiredRegistrationLinkScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToLogin = { onNavigateToLoginCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt index 6f6a99adb11..4bfda9ae12b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingScreenTest.kt @@ -59,7 +59,7 @@ class LandingScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { LandingScreen( onNavigateToCreateAccount = { onNavigateToCreateAccountCalled = true }, onNavigateToLogin = { capturedEmail -> diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt index 9ccbd8fb267..82a464cba19 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreenTest.kt @@ -64,7 +64,7 @@ class LoginScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { LoginScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToMasterPasswordHint = { onNavigateToMasterPasswordHintCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt index 6bac14babbc..ad7904f9cca 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt @@ -47,7 +47,7 @@ class LoginWithDeviceScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { LoginWithDeviceScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToTwoFactorLogin = { onNavigateToTwoFactorLoginEmail = it }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordgenerator/MasterPasswordGeneratorScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordgenerator/MasterPasswordGeneratorScreenTest.kt index d60e333fec1..f8b635d009a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordgenerator/MasterPasswordGeneratorScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordgenerator/MasterPasswordGeneratorScreenTest.kt @@ -32,7 +32,7 @@ class MasterPasswordGeneratorScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { MasterPasswordGeneratorScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToPreventLockout = { onNavigateToPreventLockoutCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordguidance/MasterPasswordGuidanceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordguidance/MasterPasswordGuidanceScreenTest.kt index 66a1913ee1f..d9d0d9cf583 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordguidance/MasterPasswordGuidanceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordguidance/MasterPasswordGuidanceScreenTest.kt @@ -25,7 +25,7 @@ class MasterPasswordGuidanceScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { MasterPasswordGuidanceScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToGeneratePassword = { onNavigateToGeneratorCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordhint/MasterPasswordHintScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordhint/MasterPasswordHintScreenTest.kt index bcc71bfe657..6bf75442d2b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordhint/MasterPasswordHintScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/masterpasswordhint/MasterPasswordHintScreenTest.kt @@ -28,7 +28,7 @@ class MasterPasswordHintScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { MasterPasswordHintScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreenTest.kt index ab5ee23522b..cb0c0e3e639 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreenTest.kt @@ -37,7 +37,7 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { NewDeviceNoticeEmailAccessScreen( onNavigateBackToVault = { onNavigateBackToVaultCalled = true }, onNavigateToTwoFactorOptions = { onNavigateToTwoFactorOptionsCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeTwoFactorScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeTwoFactorScreenTest.kt index b16a4c045e2..775c69b73f0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeTwoFactorScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeTwoFactorScreenTest.kt @@ -40,7 +40,7 @@ class NewDeviceNoticeTwoFactorScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { NewDeviceNoticeTwoFactorScreen( onNavigateBackToVault = { onNavigateBackToVaultCalled = true }, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/preventaccountlockout/PreventAccountLockoutScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/preventaccountlockout/PreventAccountLockoutScreenTest.kt index 53565ed9a9d..f02e1d8979d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/preventaccountlockout/PreventAccountLockoutScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/preventaccountlockout/PreventAccountLockoutScreenTest.kt @@ -20,7 +20,7 @@ class PreventAccountLockoutScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { PreventAccountLockoutScreen( onNavigateBack = { onBackHasBeenInvoked = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreenTest.kt index ee937be0b11..c6ba40b23fb 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordScreenTest.kt @@ -32,7 +32,7 @@ class RemovePasswordScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { RemovePasswordScreen( viewModel = viewModel, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt index 4a1657070a8..a64b0084bb6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt @@ -159,6 +159,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( id = "orgId", name = ORGANIZATION_NAME, shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = true, role = OrganizationType.USER, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/resetPassword/ResetPasswordScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/resetPassword/ResetPasswordScreenTest.kt index 109b4ebb35a..c32c076c740 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/resetPassword/ResetPasswordScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/resetPassword/ResetPasswordScreenTest.kt @@ -43,7 +43,7 @@ class ResetPasswordScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { ResetPasswordScreen( onNavigateToPreventAccountLockOut = { onNavigateToLearnToPreventLockoutCalled = true diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreenTest.kt index 96c71d3bb3f..555afa0b9ce 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreenTest.kt @@ -31,7 +31,7 @@ class SetPasswordScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { SetPasswordScreen( viewModel = viewModel, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreenTest.kt index b0299f2ef9f..1a8897bba32 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreenTest.kt @@ -54,7 +54,7 @@ class StartRegistrationScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { StartRegistrationScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToCompleteRegistration = { _, _ -> diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt index 330839b2416..1c1fb77e44e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceScreenTest.kt @@ -39,7 +39,7 @@ class TrustedDeviceScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { TrustedDeviceScreen( viewModel = viewModel, onNavigateToAdminApproval = { onNavigateToAdminApprovalEmail = it }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt index 5967bd9219b..ca5f341db88 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt @@ -49,7 +49,7 @@ class TwoFactorLoginScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { TwoFactorLoginScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensionTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensionTest.kt index ffe43b43b00..d7236a25757 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensionTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/util/TwoFactorAuthMethodExtensionTest.kt @@ -135,7 +135,7 @@ class TwoFactorAuthMethodExtensionTest { @Test fun `imageRes returns the expected value`() { mapOf( - TwoFactorAuthMethod.AUTHENTICATOR_APP to null, + TwoFactorAuthMethod.AUTHENTICATOR_APP to R.drawable.img_authenticator, TwoFactorAuthMethod.EMAIL to R.drawable.img_new_device_verification, TwoFactorAuthMethod.DUO to null, TwoFactorAuthMethod.YUBI_KEY to R.drawable.yubi_key, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt index f7be47f8978..e9eaa166112 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt @@ -83,7 +83,7 @@ class VaultUnlockScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { VaultUnlockScreen( viewModel = viewModel, biometricsManager = biometricsManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt index c6bcf8fc5f4..dcd27e09f5a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt @@ -28,7 +28,7 @@ class WelcomeScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { WelcomeScreen( onNavigateToCreateAccount = { onNavigateToCreateAccountCalled = true }, onNavigateToLogin = { onNavigateToLoginCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/BaseComposeTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/BaseComposeTest.kt index 0f63c18bca5..194ee47d066 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/BaseComposeTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/BaseComposeTest.kt @@ -1,7 +1,6 @@ package com.x8bit.bitwarden.ui.platform.base import androidx.activity.OnBackPressedDispatcher -import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.runtime.Composable import androidx.compose.ui.test.ExperimentalTestApi @@ -25,41 +24,25 @@ abstract class BaseComposeTest : BaseRobolectricTest() { val composeTestRule = createComposeRule(effectContext = dispatcher) /** - * instance of [OnBackPressedDispatcher] made available if testing using - * - * [setContentWithBackDispatcher] or [runTestWithTheme] + * instance of [OnBackPressedDispatcher] made available if testing using [setContent]. */ var backDispatcher: OnBackPressedDispatcher? = null private set /** - * Helper for testing a basic Composable function that only requires a Composable environment - * with the [BitwardenTheme]. + * Helper for testing a basic Composable function that only requires a [Composable]. The + * [AppTheme] is overridable and the [backDispatcher] is configured automatically. */ - protected fun runTestWithTheme( - theme: AppTheme, + protected fun setContent( + theme: AppTheme = AppTheme.DEFAULT, test: @Composable () -> Unit, ) { composeTestRule.setContent { + backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher BitwardenTheme( theme = theme, - ) { - backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher - test() - } - } - } - - /** - * Helper for testing a basic Composable function that provides access to a - * [OnBackPressedDispatcher]. - * - * Use if the [Composable] function being tested uses a [BackHandler] - */ - protected fun setContentWithBackDispatcher(test: @Composable () -> Unit) { - composeTestRule.setContent { - backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher - test() + content = test, + ) } } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/util/StringRestExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/util/StringRestExtensionsTest.kt index a452cabcb21..43bf9d963db 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/util/StringRestExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/base/util/StringRestExtensionsTest.kt @@ -16,7 +16,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Test fun `toAnnotatedString should add Clickable LinkAnnotation to highlighted string`() { var textClickCalled = false - composeTestRule.setContent { + setContent { val annotatedString = R.string.test_for_single_link_annotation.toAnnotatedString { textClickCalled = true @@ -32,7 +32,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Suppress("MaxLineLength") @Test fun `toAnnotatedString should add multiple Clickable LinkAnnotations to highlighted string`() { - composeTestRule.setContent { + setContent { val annotatedString = R.string.test_for_multi_link_annotation.toAnnotatedString() Text(text = annotatedString) @@ -45,7 +45,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Test fun `no link annotations should be applied to non annotated string resource`() { - composeTestRule.setContent { + setContent { Text(text = R.string.test_for_string_with_no_annotations.toAnnotatedString()) } @@ -65,7 +65,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Test fun `string with args should only use the arguments available in the string`() { - composeTestRule.setContent { + setContent { Text( text = R.string.test_for_string_with_annotation_and_arg_annotation @@ -89,7 +89,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Test fun `string with arg annotations but no passed in args should just append empty string`() { - composeTestRule.setContent { + setContent { Text( text = R.string.test_for_string_with_annotation_and_arg_annotation .toAnnotatedString(), @@ -104,7 +104,7 @@ class StringRestExtensionsTest : BaseComposeTest() { @Suppress("MaxLineLength") @Test fun `string with no annotations with args should just be handled as normal annotated string`() { - composeTestRule.setContent { + setContent { Text( text = R.string.test_for_string_with_no_annotations_with_format_arg.toAnnotatedString( args = arrayOf("this"), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreenTest.kt index 1d54c58f6e3..be47ca9a7a2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreenTest.kt @@ -30,7 +30,7 @@ class DebugMenuScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { DebugMenuScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt index 21f011caa8d..8f762181e9a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt @@ -123,7 +123,6 @@ private val DEFAULT_MAP_VALUE: Map, Any> = mapOf( FlagKey.OnboardingCarousel to true, FlagKey.OnboardingFlow to true, FlagKey.ImportLoginsFlow to true, - FlagKey.SshKeyCipherItems to true, FlagKey.VerifiedSsoDomainEndpoint to true, FlagKey.CredentialExchangeProtocolImport to true, FlagKey.CredentialExchangeProtocolExport to true, @@ -145,7 +144,6 @@ private val UPDATED_MAP_VALUE: Map, Any> = mapOf( FlagKey.OnboardingCarousel to true, FlagKey.OnboardingFlow to false, FlagKey.ImportLoginsFlow to false, - FlagKey.SshKeyCipherItems to false, FlagKey.VerifiedSsoDomainEndpoint to false, FlagKey.CredentialExchangeProtocolImport to false, FlagKey.CredentialExchangeProtocolExport to false, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavScreenTest.kt index f5520654c03..c0a4ac9983e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavScreenTest.kt @@ -35,7 +35,7 @@ class RootNavScreenTest : BaseComposeTest() { every { eventFlow } returns emptyFlow() every { stateFlow } returns MutableStateFlow(RootNavState.Splash) } - composeTestRule.setContent { + setContent { RootNavScreen( viewModel = viewModel, navController = fakeNavHostController, @@ -54,7 +54,7 @@ class RootNavScreenTest : BaseComposeTest() { every { stateFlow } returns rootNavStateFlow } var isSplashScreenRemoved = false - composeTestRule.setContent { + setContent { RootNavScreen( viewModel = viewModel, navController = fakeNavHostController, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt index 6bca299bc49..f50db24daa6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt @@ -385,6 +385,7 @@ class RootNavViewModelTest : BaseViewModelTest() { id = "orgId", name = "orgName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = true, role = OrganizationType.USER, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt index db2a374af1d..58e76697988 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt @@ -70,7 +70,7 @@ class SearchScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { SearchScreen( viewModel = viewModel, intentManager = intentManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt index b17ef5ead59..901568ee6f2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt @@ -28,7 +28,7 @@ class SettingsScreenTest : BaseComposeTest() { fun `on about row click should emit SettingsClick`() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.ABOUT)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -48,7 +48,7 @@ class SettingsScreenTest : BaseComposeTest() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.ACCOUNT_SECURITY)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -68,7 +68,7 @@ class SettingsScreenTest : BaseComposeTest() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.APPEARANCE)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -89,7 +89,7 @@ class SettingsScreenTest : BaseComposeTest() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.AUTO_FILL)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -108,7 +108,7 @@ class SettingsScreenTest : BaseComposeTest() { fun `on other row click should emit SettingsClick`() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.OTHER)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -127,7 +127,7 @@ class SettingsScreenTest : BaseComposeTest() { fun `on vault row click should emit SettingsClick`() { every { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.VAULT)) } just runs - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -145,7 +145,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAbout should call onNavigateToAbout`() { var haveCalledNavigateToAbout = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { @@ -165,7 +165,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurity should call onNavigateToAccountSecurity`() { var haveCalledNavigateToAccountSecurity = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -185,7 +185,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurity should call NavigateAppearance`() { var haveCalledNavigateToAppearance = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -203,7 +203,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurity should call onNavigateToAutoFill`() { var haveCalledNavigateToAutoFill = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -223,7 +223,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurity should call onNavigateToOther`() { var haveCalledNavigateToOther = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -243,7 +243,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurity should call NavigateVault`() { var haveCalledNavigateToVault = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -263,7 +263,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `on NavigateAccountSecurityShortcut should call onNavigateToAccountSecurity`() { var haveCalledNavigateToAccountSecurity = false - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = { }, @@ -281,7 +281,7 @@ class SettingsScreenTest : BaseComposeTest() { @Test fun `Settings screen should show correct number of notification badges based on state`() { - composeTestRule.setContent { + setContent { SettingsScreen( viewModel = viewModel, onNavigateToAbout = {}, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutScreenTest.kt index 762bcb9819a..941f50b7aa0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutScreenTest.kt @@ -57,7 +57,7 @@ class AboutScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { AboutScreen( viewModel = viewModel, intentManager = intentManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt index cdf02bd8aab..4a210e2e11f 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt @@ -83,7 +83,7 @@ class AccountSecurityScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { AccountSecurityScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToDeleteAccount = { onNavigateToDeleteAccountCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountScreenTest.kt index 91371f19bd0..57880337e76 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountScreenTest.kt @@ -38,7 +38,7 @@ class DeleteAccountScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { DeleteAccountScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToDeleteAccountConfirmation = { diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccountconfirmation/DeleteAccountConfirmationScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccountconfirmation/DeleteAccountConfirmationScreenTest.kt index 9352a07b01c..c2a6308ff6a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccountconfirmation/DeleteAccountConfirmationScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccountconfirmation/DeleteAccountConfirmationScreenTest.kt @@ -33,7 +33,7 @@ class DeleteAccountConfirmationScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { DeleteAccountConfirmationScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalScreenTest.kt index 359ef2e533f..5de0ef227a8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalScreenTest.kt @@ -40,7 +40,7 @@ class LoginApprovalScreenTest : BaseComposeTest() { @Before fun setUp() { - setContentWithBackDispatcher { + setContent { LoginApprovalScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt index df33a0c95df..4eb9b7cb07b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt @@ -55,7 +55,7 @@ class PendingRequestsScreenTest : BaseComposeTest() { mockkStatic(::isBuildVersionBelow) every { isFdroid } returns false every { isBuildVersionBelow(any()) } returns false - composeTestRule.setContent { + setContent { PendingRequestsScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToLoginApproval = { _ -> onNavigateToLoginApprovalCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/appearance/AppearanceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/appearance/AppearanceScreenTest.kt index aefc6ce69ab..edad812cafa 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/appearance/AppearanceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/appearance/AppearanceScreenTest.kt @@ -37,7 +37,7 @@ class AppearanceScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { AppearanceScreen( onNavigateBack = { haveCalledNavigateBack = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreenTest.kt index 73ba3a893db..957e947a500 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillScreenTest.kt @@ -14,9 +14,11 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest +import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.model.ChromeAutofillSettingsOption import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager import com.x8bit.bitwarden.ui.util.assertNoDialogExists import io.mockk.every @@ -24,6 +26,7 @@ import io.mockk.just import io.mockk.mockk import io.mockk.runs import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import org.junit.Assert.assertTrue @@ -47,11 +50,12 @@ class AutoFillScreenTest : BaseComposeTest() { every { startSystemAutofillSettingsActivity() } answers { isSystemSettingsRequestSuccess } every { startCredentialManagerSettings(any()) } just runs every { startSystemAccessibilitySettingsActivity() } just runs + every { startChromeAutofillSettingsActivity(any()) } returns true } @Before fun setUp() { - composeTestRule.setContent { + setContent { AutoFillScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToBlockAutoFillScreen = { onNavigateToBlockAutoFillScreenCalled = true }, @@ -487,7 +491,7 @@ class AutoFillScreenTest : BaseComposeTest() { .performScrollTo() .performClick() - verify { viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick) } + verify { viewModel.trySendAction(AutoFillAction.AutofillActionCardCtaClick) } } @Test @@ -505,6 +509,83 @@ class AutoFillScreenTest : BaseComposeTest() { mutableEventFlow.tryEmit(AutoFillEvent.NavigateToSetupAutofill) assertTrue(onNavigateToSetupAutoFillScreenCalled) } + + @Test + fun `ChromeAutofillSettingsCard is only displayed when there are options in the list`() { + val chromeAutofillSupportingText = + "Improves login filling for supported websites on Chrome. " + + "Once enabled, you’ll be directed to Chrome settings to enable " + + "third-party autofill." + + composeTestRule + .onNodeWithText(chromeAutofillSupportingText) + .assertDoesNotExist() + + mutableStateFlow.update { + it.copy( + chromeAutofillSettingsOptions = persistentListOf( + ChromeAutofillSettingsOption.Stable(enabled = true), + ), + ) + } + + composeTestRule + .onNodeWithText(chromeAutofillSupportingText) + .performScrollTo() + .assertIsDisplayed() + } + + @Test + fun `when Chrome autofill options are clicked the correct action is sent`() { + mutableStateFlow.update { + it.copy( + isAutoFillServicesEnabled = true, + chromeAutofillSettingsOptions = persistentListOf( + ChromeAutofillSettingsOption.Stable(enabled = true), + ChromeAutofillSettingsOption.Beta(enabled = false), + ), + ) + } + + composeTestRule + .onNodeWithText("Use Chrome autofill integration") + .performScrollTo() + .performClick() + + composeTestRule + .onNodeWithText("Use Chrome autofill integration (Beta)") + .performScrollTo() + .performClick() + + verify(exactly = 1) { + viewModel.trySendAction( + AutoFillAction.ChromeAutofillSelected(ChromeReleaseChannel.BETA), + ) + viewModel.trySendAction( + AutoFillAction.ChromeAutofillSelected(ChromeReleaseChannel.STABLE), + ) + } + } + + @Suppress("MaxLineLength") + @Test + fun `when NavigateToChromeAutofillSettings events are sent they invoke the intent manager with the correct release channel`() { + mutableEventFlow.tryEmit( + AutoFillEvent.NavigateToChromeAutofillSettings( + ChromeReleaseChannel.STABLE, + ), + ) + mutableEventFlow.tryEmit( + AutoFillEvent.NavigateToChromeAutofillSettings( + ChromeReleaseChannel.BETA, + ), + ) + + verify(exactly = 1) { + intentManager.startChromeAutofillSettingsActivity(ChromeReleaseChannel.BETA) + intentManager.startChromeAutofillSettingsActivity(ChromeReleaseChannel.STABLE) + } + } } private val DEFAULT_STATE: AutoFillState = AutoFillState( @@ -518,4 +599,5 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState( defaultUriMatchType = UriMatchType.DOMAIN, showAutofillActionCard = false, activeUserId = "activeUserId", + chromeAutofillSettingsOptions = persistentListOf(), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModelTest.kt index 643ba5f93ca..d7ef45ceed2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/AutoFillViewModelTest.kt @@ -4,12 +4,17 @@ import android.os.Build import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.autofill.manager.chrome.ChromeThirdPartyAutofillEnabledManager +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeReleaseChannel +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeThirdPartyAutoFillData +import com.x8bit.bitwarden.data.autofill.model.chrome.ChromeThirdPartyAutofillStatus import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest +import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.chrome.model.ChromeAutofillSettingsOption import io.mockk.every import io.mockk.just import io.mockk.mockk @@ -17,6 +22,7 @@ import io.mockk.mockkStatic import io.mockk.runs import io.mockk.unmockkStatic import io.mockk.verify +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.runTest @@ -40,6 +46,12 @@ class AutoFillViewModelTest : BaseViewModelTest() { every { storeShowAutoFillSettingBadge(any()) } just runs } + private val mutableChromeAutofillStatusFlow = MutableStateFlow(DEFAULT_AUTOFILL_STATUS) + private val chromeThirdPartyAutofillEnabledManager = + mockk { + every { chromeThirdPartyAutofillStatusFlow } returns mutableChromeAutofillStatusFlow + } + private val settingsRepository: SettingsRepository = mockk { every { isInlineAutofillEnabled } returns true every { isInlineAutofillEnabled = any() } just runs @@ -330,7 +342,7 @@ class AutoFillViewModelTest : BaseViewModelTest() { mutableFirstTimeStateFlow.update { it.copy(showSetupAutofillCard = true) } val viewModel = createViewModel() viewModel.eventFlow.test { - viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick) + viewModel.trySendAction(AutoFillAction.AutofillActionCardCtaClick) assertEquals( AutoFillEvent.NavigateToSetupAutofill, awaitItem(), @@ -354,6 +366,55 @@ class AutoFillViewModelTest : BaseViewModelTest() { } } + @Suppress("MaxLineLength") + @Test + fun `when ChromeAutofillStatusReceive with updated information is processed state updates as expected`() = + runTest { + val viewModel = createViewModel() + viewModel.stateFlow.test { + assertEquals( + DEFAULT_STATE, + awaitItem(), + ) + mutableChromeAutofillStatusFlow.update { + it.copy( + stableStatusData = DEFAULT_CHROME_AUTOFILL_DATA.copy(isAvailable = true), + ) + } + assertEquals( + DEFAULT_STATE.copy( + chromeAutofillSettingsOptions = persistentListOf( + ChromeAutofillSettingsOption.Stable(enabled = false), + ), + ), + awaitItem(), + ) + } + } + + @Suppress("MaxLineLength") + @Test + fun `when ChromeAutofillSelected action is handled the correct NavigateToChromeAutofillSettings event is sent`() = + runTest { + val viewModel = createViewModel() + viewModel.eventFlow.test { + viewModel.trySendAction( + AutoFillAction.ChromeAutofillSelected(ChromeReleaseChannel.STABLE), + ) + assertEquals( + AutoFillEvent.NavigateToChromeAutofillSettings(ChromeReleaseChannel.STABLE), + awaitItem(), + ) + viewModel.trySendAction( + AutoFillAction.ChromeAutofillSelected(ChromeReleaseChannel.BETA), + ) + assertEquals( + AutoFillEvent.NavigateToChromeAutofillSettings(ChromeReleaseChannel.BETA), + awaitItem(), + ) + } + } + private fun createViewModel( state: AutoFillState? = DEFAULT_STATE, ): AutoFillViewModel = AutoFillViewModel( @@ -361,6 +422,7 @@ class AutoFillViewModelTest : BaseViewModelTest() { settingsRepository = settingsRepository, authRepository = authRepository, firstTimeActionManager = firstTimeActionManager, + chromeThirdPartyAutofillEnabledManager = chromeThirdPartyAutofillEnabledManager, ) } @@ -375,4 +437,15 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState( defaultUriMatchType = UriMatchType.DOMAIN, showAutofillActionCard = false, activeUserId = "activeUserId", + chromeAutofillSettingsOptions = persistentListOf(), +) + +private val DEFAULT_CHROME_AUTOFILL_DATA = ChromeThirdPartyAutoFillData( + isAvailable = false, + isThirdPartyEnabled = false, +) + +private val DEFAULT_AUTOFILL_STATUS = ChromeThirdPartyAutofillStatus( + stableStatusData = DEFAULT_CHROME_AUTOFILL_DATA, + betaChannelStatusData = DEFAULT_CHROME_AUTOFILL_DATA, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/blockautofill/BlockAutoFillScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/blockautofill/BlockAutoFillScreenTest.kt index 8e9adf93ed0..0651b7381eb 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/blockautofill/BlockAutoFillScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/autofill/blockautofill/BlockAutoFillScreenTest.kt @@ -33,7 +33,7 @@ class BlockAutoFillScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { BlockAutoFillScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultScreenTest.kt index 5350d6900e3..be29034588c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultScreenTest.kt @@ -45,7 +45,7 @@ class ExportVaultScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { ExportVaultScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersScreenTest.kt index 2639160708b..d5cc08aeceb 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersScreenTest.kt @@ -35,7 +35,7 @@ class FoldersScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { FoldersScreen( viewModel = viewModel, onNavigateToEditFolderScreen = { onNavigateToEditFolderScreenId = it }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditScreenTest.kt index 1c49f515936..0b523f01288 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditScreenTest.kt @@ -38,7 +38,7 @@ class FolderAddEditScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { FolderAddEditScreen( viewModel = viewModel, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherScreenTest.kt index 01ab88d3e96..4af1b2f8675 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherScreenTest.kt @@ -38,7 +38,7 @@ class OtherScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { OtherScreen( viewModel = viewModel, onNavigateBack = { haveCalledNavigateBack = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/vault/VaultSettingsScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/vault/VaultSettingsScreenTest.kt index 090260451e5..6ff631bc343 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/vault/VaultSettingsScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/vault/VaultSettingsScreenTest.kt @@ -54,7 +54,7 @@ class VaultSettingsScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { VaultSettingsScreen( viewModel = viewModel, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt index 4e5db48c3c5..ec9bb5b39a5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreenTest.kt @@ -66,7 +66,7 @@ class GeneratorScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { GeneratorScreen( viewModel = viewModel, onNavigateToPasswordHistory = { diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryScreenTest.kt index 9c5e12bd4b8..2138ed54b72 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryScreenTest.kt @@ -41,7 +41,7 @@ class PasswordHistoryScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { PasswordHistoryScreen( viewModel = viewModel, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt index e0e6c056e79..6dcd39f61d3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt @@ -65,7 +65,7 @@ class SendScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { SendScreen( viewModel = viewModel, onNavigateToAddSend = { onNavigateToNewSendCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendScreenTest.kt index 0d61aeb03fc..6090bb49aca 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendScreenTest.kt @@ -66,7 +66,7 @@ class AddSendScreenTest : BaseComposeTest() { @Before fun setUp() { - setContentWithBackDispatcher { + setContent { AddSendScreen( viewModel = viewModel, exitManager = exitManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt index fa790f196d7..514d8aefb22 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt @@ -117,7 +117,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { VaultAddEditScreen( onNavigateBack = { onNavigateBackCalled = true }, onNavigateToQrCodeScanScreen = { onNavigateQrCodeScanScreenCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index 8983300972d..509f2cfe393 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -4598,6 +4598,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt index 036a88ed7e4..cbcaaad3444 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt @@ -562,6 +562,7 @@ class CipherViewExtensionsTest { id = "mockOrganizationId-1", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreenTest.kt index baea8e3b372..aca148a35f2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreenTest.kt @@ -46,7 +46,7 @@ class AttachmentsScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { AttachmentsScreen( viewModel = viewModel, intentManager = intentManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt index fc5901cd326..a63fe8bd735 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt @@ -53,7 +53,7 @@ class ImportLoginsScreenTest : BaseComposeTest() { @Before fun setup() { - setContentWithBackDispatcher { + setContent { ImportLoginsScreen( onNavigateBack = { navigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt index 41931942904..bbd069b463e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt @@ -77,7 +77,7 @@ class VaultItemScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { VaultItemScreen( viewModel = viewModel, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt index 2767a83e875..966951f0086 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt @@ -117,7 +117,7 @@ class VaultItemListingScreenTest : BaseComposeTest() { fun setUp() { mockkStatic(String::toHostOrPathOrNull) every { AUTOFILL_SELECTION_DATA.uri?.toHostOrPathOrNull() } returns "www.test.com" - setContentWithBackDispatcher { + setContent { VaultItemListingScreen( viewModel = viewModel, exitManager = exitManager, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/manualcodeentry/ManualCodeEntryScreenTests.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/manualcodeentry/ManualCodeEntryScreenTests.kt index 9fcaff9bcda..a384448c2a0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/manualcodeentry/ManualCodeEntryScreenTests.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/manualcodeentry/ManualCodeEntryScreenTests.kt @@ -52,7 +52,7 @@ class ManualCodeEntryScreenTests : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { ManualCodeEntryScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationScreenTest.kt index 31435b4aeda..5dd7b2375f8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationScreenTest.kt @@ -45,7 +45,7 @@ class VaultMoveToOrganizationScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { VaultMoveToOrganizationScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt index abaad2e4de1..c81ed6c9cba 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt @@ -494,6 +494,7 @@ private val DEFAULT_USER_STATE = UserState( id = "mockOrganizationId-1", name = "mockOrganizationName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -501,6 +502,7 @@ private val DEFAULT_USER_STATE = UserState( id = "mockOrganizationId-2", name = "mockOrganizationName-2", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -508,6 +510,7 @@ private val DEFAULT_USER_STATE = UserState( id = "mockOrganizationId-3", name = "mockOrganizationName-3", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt index 900497733cd..4a2b33e6dda 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt @@ -106,6 +106,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = id = "mockOrganizationId-1", name = "mockOrganizationName-1", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -113,6 +114,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = id = "mockOrganizationId-2", name = "mockOrganizationName-2", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -120,6 +122,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = id = "mockOrganizationId-3", name = "mockOrganizationName-3", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/qrcodescan/QrCodeScanScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/qrcodescan/QrCodeScanScreenTest.kt index 3ffe40381e4..7e4c9175583 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/qrcodescan/QrCodeScanScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/qrcodescan/QrCodeScanScreenTest.kt @@ -31,7 +31,7 @@ class QrCodeScanScreenTest : BaseComposeTest() { @Before fun setup() { - composeTestRule.setContent { + setContent { QrCodeScanScreen( onNavigateBack = { onNavigateBackCalled = true }, viewModel = viewModel, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index 7f1da9aad7f..fe197e5c8fd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -11,7 +11,6 @@ import androidx.compose.ui.test.hasScrollToNodeAction import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isDialog import androidx.compose.ui.test.isDisplayed -import androidx.compose.ui.test.isNotDisplayed import androidx.compose.ui.test.isPopup import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithContentDescription @@ -99,7 +98,7 @@ class VaultScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { VaultScreen( viewModel = viewModel, onNavigateToVaultAddItemScreen = { onNavigateToVaultAddItemScreenCalled = true }, @@ -1276,7 +1275,6 @@ class VaultScreenTest : BaseComposeTest() { // Verify SSH key group is displayed when showSshKeys is true mutableStateFlow.update { it.copy( - showSshKeys = true, viewState = DEFAULT_CONTENT_VIEW_STATE.copy( sshKeyItemsCount = count, ), @@ -1286,12 +1284,6 @@ class VaultScreenTest : BaseComposeTest() { .onNodeWithText("SSH key") .assertTextEquals("SSH key", count.toString()) .assertIsDisplayed() - - // Verify SSH key group is hidden when showSshKeys is false - mutableStateFlow.update { it.copy(showSshKeys = false) } - composeTestRule - .onNodeWithText("SSH key") - .assertIsNotDisplayed() } @Test @@ -1299,7 +1291,6 @@ class VaultScreenTest : BaseComposeTest() { // Verify SSH key vault items are displayed when showSshKeys is true mutableStateFlow.update { it.copy( - showSshKeys = true, viewState = DEFAULT_CONTENT_VIEW_STATE.copy( noFolderItems = listOf( VaultState.ViewState.VaultItem.SshKey( @@ -1318,12 +1309,6 @@ class VaultScreenTest : BaseComposeTest() { composeTestRule .onNodeWithTextAfterScroll("mockSshKey") .isDisplayed() - - // Verify SSH key vault items are hidden when showSshKeys is false - mutableStateFlow.update { it.copy(showSshKeys = false) } - composeTestRule - .onNodeWithText("mockSshKey") - .isNotDisplayed() } @Test @@ -1440,7 +1425,6 @@ private val DEFAULT_STATE: VaultState = VaultState( hasMasterPassword = true, isRefreshing = false, showImportActionCard = false, - showSshKeys = false, ) private val DEFAULT_CONTENT_VIEW_STATE: VaultState.ViewState.Content = VaultState.ViewState.Content( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index d17f520b461..de8d9c3c954 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -145,12 +145,6 @@ class VaultViewModelTest : BaseViewModelTest() { every { getFeatureFlagFlow(FlagKey.ImportLoginsFlow) } returns mutableImportLoginsFeatureFlow - every { - getFeatureFlagFlow(FlagKey.SshKeyCipherItems) - } returns mutableSshKeyVaultItemsEnabledFlow - every { - getFeatureFlag(FlagKey.SshKeyCipherItems) - } returns mutableSshKeyVaultItemsEnabledFlow.value } private val reviewPromptManager: ReviewPromptManager = mockk() @@ -246,6 +240,7 @@ class VaultViewModelTest : BaseViewModelTest() { id = "organiationId", name = "Test Organization", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -332,6 +327,7 @@ class VaultViewModelTest : BaseViewModelTest() { id = "organizationId", name = "Test Organization", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -563,6 +559,7 @@ class VaultViewModelTest : BaseViewModelTest() { id = "testOrganizationId", name = "Test Organization", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -579,7 +576,6 @@ class VaultViewModelTest : BaseViewModelTest() { isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled, baseIconUrl = viewModel.stateFlow.value.baseIconUrl, hasMasterPassword = true, - showSshKeys = false, ), ) .copy( @@ -604,7 +600,6 @@ class VaultViewModelTest : BaseViewModelTest() { isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled, baseIconUrl = viewModel.stateFlow.value.baseIconUrl, hasMasterPassword = true, - showSshKeys = false, ), ), viewModel.stateFlow.value, @@ -714,7 +709,6 @@ class VaultViewModelTest : BaseViewModelTest() { itemTypesCount = CipherType.entries.size, sshKeyItemsCount = 1, ), - showSshKeys = true, ), viewModel.stateFlow.value, ) @@ -736,7 +730,7 @@ class VaultViewModelTest : BaseViewModelTest() { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), ) @@ -850,7 +844,7 @@ class VaultViewModelTest : BaseViewModelTest() { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), ), @@ -950,7 +944,7 @@ class VaultViewModelTest : BaseViewModelTest() { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), dialog = VaultState.DialogState.Error( @@ -1048,7 +1042,7 @@ class VaultViewModelTest : BaseViewModelTest() { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), dialog = null, @@ -1093,47 +1087,7 @@ class VaultViewModelTest : BaseViewModelTest() { } @Test - fun `vaultDataStateFlow Loaded should exclude SSH key vault items when showSshKeys is false`() = - runTest { - mutableVaultDataStateFlow.tryEmit( - value = DataState.Loaded( - data = VaultData( - cipherViewList = listOf( - createMockCipherView(number = 1), - createMockCipherView(number = 1, cipherType = CipherType.SSH_KEY), - ), - collectionViewList = listOf(), - folderViewList = listOf(), - sendViewList = listOf(), - ), - ), - ) - - val viewModel = createViewModel() - - assertEquals( - createMockVaultState( - viewState = VaultState.ViewState.Content( - loginItemsCount = 1, - cardItemsCount = 0, - identityItemsCount = 0, - secureNoteItemsCount = 0, - favoriteItems = listOf(), - folderItems = listOf(), - collectionItems = listOf(), - noFolderItems = listOf(), - trashItemsCount = 0, - totpItemsCount = 1, - itemTypesCount = CipherType.entries.size - 1, - sshKeyItemsCount = 0, - ), - ), - viewModel.stateFlow.value, - ) - } - - @Test - fun `vaultDataStateFlow Loaded should include SSH key vault items when showSshKeys is true`() = + fun `vaultDataStateFlow Loaded should include SSH key vault items`() = runTest { mutableSshKeyVaultItemsEnabledFlow.value = true mutableVaultDataStateFlow.tryEmit( @@ -1168,7 +1122,6 @@ class VaultViewModelTest : BaseViewModelTest() { itemTypesCount = CipherType.entries.size, sshKeyItemsCount = 1, ), - showSshKeys = true, ), viewModel.stateFlow.value, ) @@ -2096,7 +2049,6 @@ private val DEFAULT_USER_STATE = UserState( private fun createMockVaultState( viewState: VaultState.ViewState, dialog: VaultState.DialogState? = null, - showSshKeys: Boolean = false, ): VaultState = VaultState( appBarTitle = R.string.my_vault.asText(), @@ -2134,5 +2086,4 @@ private fun createMockVaultState( hasMasterPassword = true, showImportActionCard = true, isRefreshing = false, - showSshKeys = showSshKeys, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt index 4f268160940..0b55258e30c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt @@ -80,6 +80,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -107,6 +108,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -138,6 +140,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -169,6 +172,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -215,6 +219,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -259,6 +264,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -307,6 +313,7 @@ class UserStateExtensionsTest { id = "organizationId", name = "organizationName", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -386,12 +393,14 @@ class UserStateExtensionsTest { name = "Organization B", shouldUseKeyConnector = false, shouldManageResetPassword = false, + shouldManagePolicies = false, role = OrganizationType.ADMIN, ), Organization( id = "organizationId-A", name = "Organization A", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -443,6 +452,7 @@ class UserStateExtensionsTest { id = "organizationId-B", name = "Organization B", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), @@ -450,6 +460,7 @@ class UserStateExtensionsTest { id = "organizationId-A", name = "Organization A", shouldManageResetPassword = false, + shouldManagePolicies = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt index 8219e957dd1..482204bda35 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt @@ -61,7 +61,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -99,7 +98,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -125,7 +124,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.MyVault, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -146,7 +144,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -181,7 +179,6 @@ class VaultDataExtensionsTest { organizationName = "Mock Organization 1", ), hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -208,7 +205,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -230,7 +227,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -254,7 +250,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -279,7 +274,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -294,7 +288,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -317,7 +311,6 @@ class VaultDataExtensionsTest { isIconLoadingDisabled = false, baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -332,7 +325,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 0, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -355,7 +348,6 @@ class VaultDataExtensionsTest { isIconLoadingDisabled = false, baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -370,7 +362,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -395,7 +387,6 @@ class VaultDataExtensionsTest { isIconLoadingDisabled = false, baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -410,7 +401,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -619,7 +610,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -634,7 +624,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 2, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -659,7 +649,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -674,7 +663,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 2, totpItemsCount = 0, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -702,7 +691,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -723,7 +711,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 100, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -758,7 +746,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, ) assertEquals( @@ -801,7 +788,7 @@ class VaultDataExtensionsTest { noFolderItems = listOf(), trashItemsCount = 0, totpItemsCount = 1, - itemTypesCount = 4, + itemTypesCount = 5, sshKeyItemsCount = 0, ), actual, @@ -809,7 +796,7 @@ class VaultDataExtensionsTest { } @Test - fun `toViewState should exclude SSH keys if showSshKeys is false`() { + fun `toViewState should include SSH key vault items and type count`() { val vaultData = VaultData( cipherViewList = listOf( createMockCipherView(number = 1), @@ -827,50 +814,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = false, - ) - - assertEquals( - VaultState.ViewState.Content( - loginItemsCount = 1, - cardItemsCount = 0, - identityItemsCount = 0, - secureNoteItemsCount = 0, - // Verify SSH key vault items are not counted when showSshKeys is false. - sshKeyItemsCount = 0, - favoriteItems = listOf(), - collectionItems = listOf(), - folderItems = listOf(), - noFolderItems = listOf(), - trashItemsCount = 0, - totpItemsCount = 1, - // Verify item types count excludes CipherType.SSH_KEY when showSshKeys is false. - itemTypesCount = 4, - ), - actual, - ) - } - - @Test - fun `toViewState should include SSH key vault items and type count if showSshKeys is true`() { - val vaultData = VaultData( - cipherViewList = listOf( - createMockCipherView(number = 1), - createMockCipherView(number = 2, cipherType = CipherType.SSH_KEY), - ), - collectionViewList = listOf(), - folderViewList = listOf(), - sendViewList = listOf(), - fido2CredentialAutofillViewList = null, - ) - - val actual = vaultData.toViewState( - isPremium = true, - isIconLoadingDisabled = false, - baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, - vaultFilterType = VaultFilterType.AllVaults, - hasMasterPassword = true, - showSshKeys = true, ) assertEquals( @@ -928,7 +871,6 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, - showSshKeys = true, ) assertEquals( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeScreenTest.kt index 269f2dc1fc2..f9e728f931b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeScreenTest.kt @@ -48,7 +48,7 @@ class VerificationCodeScreenTest : BaseComposeTest() { @Before fun setUp() { - composeTestRule.setContent { + setContent { VerificationCodeScreen( viewModel = viewModel, onNavigateBack = { onNavigateBackCalled = true }, diff --git a/fastlane/Fastfile b/fastlane/Fastfile index ef7fa81acfb..a60938ffa90 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -257,4 +257,129 @@ platform :android do ) releaseNotes end + + # Authenticator + desc "Runs tests" + lane :checkAuthenticator do + gradle(tasks: ["authenticator:testDebug", "authenticator:lintDebug", "authenticator:detekt","authenticator:koverXmlReportDebug"]) + end + + desc "Apply build version information" + fastlane_require "time" + lane :setAuthenticatorBuildVersionInfo do |options| + + # Read-in app build config file. + buildConfigPath = "../authenticator/build.gradle.kts" + buildConfigFile = File.open(buildConfigPath) + buildConfigText = buildConfigFile.read + buildConfigFile.close + + currentVersionCode = buildConfigText.match(/versionCode = (\d+)/).captures[0] + currentVersionName = buildConfigText.match(/versionName = "(.+)"/).captures[0] + + if options[:versionName].nil? or options[:versionName].to_s.empty? + puts "Fetching latest tags from origin..." + `git fetch --prune --no-recurse-submodules --filter=tree:0 --depth=1 --tags origin` + puts "Getting latest version name from previous git tag..." + latestTag = `git describe --tags $(git rev-list --tags --max-count=1)`.chomp() + puts "Using tag #{latestTag} to calculate version name..." + latestTag.slice!(0) + puts "Current version name resolved to #{latestTag}." + + versionParts = latestTag.split(".") + currentMajor = versionParts[0] + currentMinor = versionParts[1] + currentRevision = versionParts[2] + + currentDate = Time.new + major = currentDate.year.to_s + minor = currentDate.strftime "%-m" + + revision = 0 + if currentMajor == major and currentMinor == minor + revision = currentRevision.to_i + 1 + end + nextVersionName = "#{major}.#{minor}.#{revision}" + else + nextVersionName = options[:versionName].to_s + end + + # Replace version information. + puts "Setting version code to #{options[:versionCode]}." + buildConfigText.gsub!("versionCode = #{currentVersionCode}", "versionCode = #{options[:versionCode]}") + puts "Setting version name to #{nextVersionName}." + buildConfigText.gsub!("versionName = \"#{currentVersionName}\"", "versionName = \"#{nextVersionName}\"") + + # Save changes + File.open(buildConfigPath, "w") { |buildConfigFile| buildConfigFile << buildConfigText } + end + + desc "Assemble debug variants" + lane :buildAuthenticatorDebug do + gradle( + task: "authenticator:assemble", + build_type: "Debug", + print_command: false, + ) + end + + desc "Assemble and sign release APK" + lane :buildAuthenticatorRelease do |options| + gradle( + task: "authenticator:assemble", + build_type: "Release", + properties: { + "android.injected.signing.store.file" => options[:storeFile], + "android.injected.signing.store.password" => options[:storePassword], + "android.injected.signing.key.alias" => options[:keyAlias], + "android.injected.signing.key.password" => options[:keyPassword] + }, + print_command: false, + ) + end + + desc "Bundle and sign release AAB" + lane :bundleAuthenticatorRelease do |options| + gradle( + task: "authenticator:bundle", + build_type: "Release", + properties: { + "android.injected.signing.store.file" => options[:storeFile], + "android.injected.signing.store.password" => options[:storePassword], + "android.injected.signing.key.alias" => options[:keyAlias], + "android.injected.signing.key.password" => options[:keyPassword] + }, + print_command: false, + ) + end + + desc "Publish release AAB to Firebase" + lane :distributeAuthenticatorReleaseBundleToFirebase do |options| + release_notes = changelog_from_git_commits( + commits_count: 1, + pretty: "- %s" + ) + + puts "Release notes #{release_notes}" + + firebase_app_distribution( + app: "1:867301491091:android:50b626dba42a361651e866", + android_artifact_type: "AAB", + android_artifact_path: "authenticator/build/outputs/bundle/release/com.bitwarden.authenticator.aab", + service_credentials_file: options[:serviceCredentialsFile], + groups: "internal-prod-group, livefront", + release_notes: release_notes, + ) + end + + desc "Publish release to Google Play Store" + lane :publishAuthenticatorReleaseToGooglePlayStore do |options| + upload_to_play_store( + package_name: "com.bitwarden.authenticator", + json_key: options[:serviceCredentialsFile], + track: "internal", + aab: "authenticator/build/outputs/bundle/release/com.bitwarden.authenticator.aab", + mapping: "authenticator/build/outputs/mapping/release/mapping.txt", + ) + end end diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cb8c66c9564..ba3f0b9a9a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ compileSdk = "35" targetSdk = "35" minSdk = "29" +minSdkBwa = "28" # Dependency Versions androidGradlePlugin = "8.8.1" @@ -12,7 +13,7 @@ androidxActivity = "1.10.0" androidXBiometrics = "1.2.0-alpha05" androidxBrowser = "1.8.0" androidxCamera = "1.4.1" -androidxComposeBom = "2025.01.00" +androidxComposeBom = "2025.02.00" androidxCore = "1.15.0" # Versions >= 1.5.0-alpha05 introduce breaking changes and bugs that are not suitable for # production. Do not update until https://issuetracker.google.com/issues/355141766 is resolved. @@ -23,26 +24,33 @@ androidxNavigation = "2.8.0" androidxRoom = "2.6.1" androidXSecurityCrypto = "1.1.0-alpha06" androidxSplash = "1.1.0-rc01" +androidxTest = "1.6.2" +androidxTestRules = "1.6.1" androidXAppCompat = "1.7.0" androdixAutofill = "1.1.0" androidxWork = "2.10.0" bitwardenSdk = "1.0.0-20250213.181812-113" -crashlytics = "3.0.2" +crashlytics = "3.0.3" detekt = "1.23.7" -firebaseBom = "33.8.0" +firebaseBom = "33.9.0" +espresso = "3.6.1" +fastlaneScreengrab = "2.1.1" glide = "1.0.0-beta01" +googleGuava = "33.4.0-jre" +googleProtoBufJava = "4.29.3" +googleProtoBufPlugin = "0.9.4" googleServices = "4.4.2" googleReview = "2.0.2" -hilt = "2.53.1" +hilt = "2.55" junit5 = "5.11.3" jvmTarget = "17" # kotlin and ksp **must** use compatible versions, do not update either without the other. -kotlin = "2.1.0" +kotlin = "2.1.10" kotlinxCollectionsImmutable = "0.3.8" kotlinxCoroutines = "1.10.1" kotlinxSerialization = "1.8.0" kotlinxKover = "0.9.1" -ksp = "2.1.0-1.0.29" +ksp = "2.1.10-1.0.30" mockk = "1.13.13" okhttp = "4.12.0" retrofitBom = "2.11.0" @@ -52,6 +60,7 @@ timber = "5.0.1" turbine = "1.2.0" zxcvbn4j = "1.9.0" zxing = "3.5.3" +testng = "7.10.2" [libraries] # Format: - @@ -86,18 +95,26 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidxRoom" } androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "androidXSecurityCrypto" } androidx-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidxSplash" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidxTest" } +androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidxTestRules" } +androidx-test-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidxWork" } bitwarden-sdk = { module = "com.bitwarden:sdk-android-temp", version.ref = "bitwardenSdk" } bumptech-glide = { module = "com.github.bumptech.glide:compose", version.ref = "glide" } detekt-detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } detekt-detekt-rules = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" } +fastlane-screengrab = { module = "tools.fastlane:screengrab", version.ref = "fastlaneScreengrab" } google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } google-firebase-cloud-messaging = { module = "com.google.firebase:firebase-messaging-ktx" } google-firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" } +google-guava = { module = "com.google.guava:guava", version.ref = "googleGuava" } google-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } google-hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } google-hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } -google-play-review = { module = "com.google.android.play:review", version.ref = "googleReview"} +google-play-review = { module = "com.google.android.play:review", version.ref = "googleReview" } +google-protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "googleProtoBufJava" } +# Included so that Renovate tracks updates since protoc is not referenced directly in `dependency {}` blocks. +google-protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "googleProtoBufJava" } junit-junit5 = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit5" } junit-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" } kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" } @@ -105,6 +122,7 @@ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } +mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } mockk-mockk = { module = "io.mockk:mockk", version.ref = "mockk" } nulab-zxcvbn4j = { module = "com.nulab-inc:zxcvbn", version.ref = "zxcvbn4j" } robolectric-robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } @@ -117,12 +135,14 @@ square-retrofit-kotlinx-serialization = { module = "com.squareup.retrofit2:conve square-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } zxing-zxing-core = { module = "com.google.zxing:core", version.ref = "zxing" } +testng = { group = "org.testng", name = "testng", version.ref = "testng" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "crashlytics" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +google-protobuf = { id = "com.google.protobuf", version.ref = "googleProtoBufPlugin" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }