From 7fe331ace4d2e7b1329b3cfa524b299d05ec8c09 Mon Sep 17 00:00:00 2001 From: Joshua Queen <139182194+joshua-livefront@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:17:38 -0500 Subject: [PATCH] BIT-1645: Adding element IDs to the vault unlock screen (#917) --- .../feature/vaultunlock/VaultUnlockScreen.kt | 13 ++++++++++++- .../util/VaultUnlockTypeExtensions.kt | 18 ++++++++++++++++++ .../components/BitwardenAccountActionItem.kt | 13 ++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt index 470a24442e..6d5cb69ecd 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreen.kt @@ -22,15 +22,21 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag +import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.inputFieldVisibilityToggleTestTag import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenInputLabel +import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenInputTestTag import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenKeyboardType import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenMessage import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenTitle @@ -58,7 +64,7 @@ import kotlinx.collections.immutable.toImmutableList /** * The top level composable for the Vault Unlock screen. */ -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Suppress("LongMethod") @Composable fun VaultUnlockScreen( @@ -127,6 +133,7 @@ fun VaultUnlockScreen( BitwardenScaffold( modifier = Modifier .fillMaxSize() + .semantics { testTagsAsResourceId = true } .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { BitwardenTopAppBar( @@ -138,6 +145,7 @@ fun VaultUnlockScreen( initials = state.initials, color = state.avatarColor, onClick = { accountMenuVisible = !accountMenuVisible }, + actionTestTag = "AccountIconButton", ) BitwardenOverflowActionItem( menuItemDataList = persistentListOf( @@ -165,7 +173,9 @@ fun VaultUnlockScreen( { viewModel.trySendAction(VaultUnlockAction.InputChanged(it)) } }, keyboardType = state.vaultUnlockType.unlockScreenKeyboardType, + showPasswordTestTag = state.vaultUnlockType.inputFieldVisibilityToggleTestTag, modifier = Modifier + .semantics { testTag = state.vaultUnlockType.unlockScreenInputTestTag } .padding(horizontal = 16.dp) .fillMaxWidth(), ) @@ -220,6 +230,7 @@ fun VaultUnlockScreen( }, isEnabled = state.input.isNotEmpty(), modifier = Modifier + .semantics { testTag = "UnlockVaultButton" } .padding(horizontal = 16.dp) .fillMaxWidth(), ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/util/VaultUnlockTypeExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/util/VaultUnlockTypeExtensions.kt index e2620bb40b..499bf953ec 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/util/VaultUnlockTypeExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/util/VaultUnlockTypeExtensions.kt @@ -26,6 +26,15 @@ val VaultUnlockType.unlockScreenMessage: Text } .asText() +/** + * The semantic test tag to use for the main input field on the Vault Unlock screen. + */ +val VaultUnlockType.unlockScreenInputTestTag: String + get() = when (this) { + VaultUnlockType.MASTER_PASSWORD -> "MasterPasswordEntry" + VaultUnlockType.PIN -> "PinEntry" + } + /** * The label for the main text input to use on the Vault Unlock screen. */ @@ -36,6 +45,15 @@ val VaultUnlockType.unlockScreenInputLabel: Text } .asText() +/** + * The semantic test tag to use for the hide button in the input field on the Vault Unlock screen. + */ +val VaultUnlockType.inputFieldVisibilityToggleTestTag: String + get() = when (this) { + VaultUnlockType.MASTER_PASSWORD -> "PasswordVisibilityToggle" + VaultUnlockType.PIN -> "PinVisibilityToggle" + } + /** * The error message to use for a failed unlock on the Vault Unlock screen. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenAccountActionItem.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenAccountActionItem.kt index 484fe14a49..732065ccd5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenAccountActionItem.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenAccountActionItem.kt @@ -4,10 +4,13 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily @@ -31,11 +34,19 @@ fun BitwardenAccountActionItem( initials: String, color: Color, onClick: () -> Unit, + actionTestTag: String? = null, ) { val iconPainter = painterResource(id = R.drawable.ic_account_initials_container) val contentDescription = stringResource(id = R.string.account) - IconButton(onClick = onClick) { + IconButton( + onClick = onClick, + modifier = Modifier.semantics { + if (actionTestTag != null) { + testTag = actionTestTag + } + }, + ) { Icon( painter = iconPainter, contentDescription = contentDescription,