From b4b4f753cabb922e33893af2424e370efe810b9d Mon Sep 17 00:00:00 2001 From: David Perez Date: Tue, 25 Mar 2025 16:20:15 -0500 Subject: [PATCH] PM-8953: Require 4 digits for pin entry (#4914) --- .../accountsecurity/PinInputDialog.kt | 3 +++ app/src/main/res/values/strings.xml | 2 +- .../accountsetup/SetupUnlockScreenTest.kt | 17 +++++------------ .../AccountSecurityScreenTest.kt | 19 ++++++------------- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/PinInputDialog.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/PinInputDialog.kt index dd4e09c69a..0794e5d253 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/PinInputDialog.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/PinInputDialog.kt @@ -38,6 +38,8 @@ import com.x8bit.bitwarden.ui.platform.components.model.CardStyle import com.x8bit.bitwarden.ui.platform.components.util.maxDialogHeight import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme +private const val MINIMUM_PIN_LENGTH: Int = 4 + /** * A dialog for setting a user's PIN. * @@ -138,6 +140,7 @@ fun PinInputDialog( BitwardenFilledButton( label = stringResource(id = R.string.submit), + isEnabled = !isPinCreation || pin.length >= MINIMUM_PIN_LENGTH, onClick = { onSubmitClick(pin) }, modifier = Modifier.testTag(tag = "AcceptAlertButton"), ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09eb9d7b6e..82204babf1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -480,7 +480,7 @@ Scanning will happen automatically. Unlock Unlock vault 30 minutes - Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application. + Your PIN must be at least 4 characters. Your PIN settings will be reset if you ever fully log out of the application. Logged in as %1$s on %2$s. Your vault is locked. Verify your master password to continue. Your vault is locked. Verify your PIN code to continue. 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 ed27aa1b14..93e66d730f 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 @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.auth.feature.accountsetup import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.assertIsOff import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.filterToOne @@ -257,8 +258,8 @@ class SetupUnlockScreenTest : BaseComposeTest() { .assertIsDisplayed() composeTestRule .onAllNodesWithText( - text = "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if " + - "you ever fully log out of the application.", + text = "Your PIN must be at least 4 characters. Your PIN settings will be reset " + + "if you ever fully log out of the application.", ) .filterToOne(hasAnyAncestor(isDialog())) .assertIsDisplayed() @@ -306,9 +307,8 @@ class SetupUnlockScreenTest : BaseComposeTest() { composeTestRule.assertNoDialogExists() } - @Suppress("MaxLineLength") @Test - fun `PIN input dialog Submit click with empty pin should clear the dialog and send UnlockWithPinToggle Disabled`() { + fun `PIN input dialog with empty pin should disable submit button`() { mutableStateFlow.update { it.copy(isUnlockWithPinEnabled = false) } @@ -320,14 +320,7 @@ class SetupUnlockScreenTest : BaseComposeTest() { composeTestRule .onAllNodesWithText(text = "Submit") .filterToOne(hasAnyAncestor(isDialog())) - .performClick() - - verify { - viewModel.trySendAction( - SetupUnlockAction.UnlockWithPinToggle(UnlockWithPinState.Disabled), - ) - } - composeTestRule.assertNoDialogExists() + .assertIsNotEnabled() } @Suppress("MaxLineLength") 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 7eacd87a42..4fca3b62f4 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 @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsFocused +import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.assertIsOff import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.filterToOne @@ -352,8 +353,8 @@ class AccountSecurityScreenTest : BaseComposeTest() { .assertIsDisplayed() composeTestRule .onAllNodesWithText( - "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if " + - "you ever fully log out of the application.", + text = "Your PIN must be at least 4 characters. Your PIN settings will be reset " + + "if you ever fully log out of the application.", ) .filterToOne(hasAnyAncestor(isDialog())) .assertIsDisplayed() @@ -402,9 +403,8 @@ class AccountSecurityScreenTest : BaseComposeTest() { composeTestRule.assertNoDialogExists() } - @Suppress("MaxLineLength") @Test - fun `PIN input dialog Submit click with empty pin should clear the dialog and send UnlockWithPinToggle Disabled`() { + fun `PIN input dialog with empty pin should disable the Submit button`() { mutableStateFlow.update { it.copy(isUnlockWithPinEnabled = false) } @@ -414,16 +414,9 @@ class AccountSecurityScreenTest : BaseComposeTest() { .performClick() composeTestRule - .onAllNodesWithText("Submit") + .onAllNodesWithText(text = "Submit") .filterToOne(hasAnyAncestor(isDialog())) - .performClick() - - verify { - viewModel.trySendAction( - AccountSecurityAction.UnlockWithPinToggle(UnlockWithPinState.Disabled), - ) - } - composeTestRule.assertNoDialogExists() + .assertIsNotEnabled() } @Suppress("MaxLineLength")