diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt index d66669d26c..ea4d9c787b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue 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.Alignment import androidx.compose.ui.Modifier @@ -209,10 +210,13 @@ private fun ResetPasswordScreenContent( Spacer(modifier = Modifier.height(16.dp)) } + var isPasswordVisible by rememberSaveable { mutableStateOf(false) } BitwardenPasswordField( label = stringResource(id = R.string.master_password), value = state.passwordInput, onValueChange = onPasswordInputChanged, + showPassword = isPasswordVisible, + showPasswordChange = { isPasswordVisible = it }, modifier = Modifier .semantics { testTag = "NewPasswordField" } .padding(horizontal = 16.dp) @@ -225,6 +229,8 @@ private fun ResetPasswordScreenContent( label = stringResource(id = R.string.retype_master_password), value = state.retypePasswordInput, onValueChange = onRetypePasswordInputChanged, + showPassword = isPasswordVisible, + showPasswordChange = { isPasswordVisible = it }, modifier = Modifier .semantics { testTag = "RetypePasswordField" } .padding(horizontal = 16.dp) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt index 49f25a4179..1672a7da27 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt @@ -17,7 +17,10 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +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.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource @@ -137,10 +140,13 @@ private fun SetPasswordScreenContent( Spacer(modifier = Modifier.height(16.dp)) + var isPasswordVisible by rememberSaveable { mutableStateOf(false) } BitwardenPasswordField( label = stringResource(id = R.string.master_password), value = state.passwordInput, onValueChange = onPasswordInputChanged, + showPassword = isPasswordVisible, + showPasswordChange = { isPasswordVisible = it }, hint = stringResource(id = R.string.master_password_description), modifier = Modifier .semantics { testTag = "NewPasswordField" } @@ -154,6 +160,8 @@ private fun SetPasswordScreenContent( label = stringResource(id = R.string.retype_master_password), value = state.retypePasswordInput, onValueChange = onRetypePasswordInputChanged, + showPassword = isPasswordVisible, + showPasswordChange = { isPasswordVisible = it }, modifier = Modifier .semantics { testTag = "RetypePasswordField" } .padding(horizontal = 16.dp) 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 9a1a73ae2a..18f8fbefdd 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 @@ -1,10 +1,12 @@ package com.x8bit.bitwarden.ui.auth.feature.resetPassword import androidx.compose.ui.test.assert +import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.isDialog import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput @@ -223,6 +225,31 @@ class ResetPasswordScreenTest : BaseComposeTest() { viewModel.trySendAction(ResetPasswordAction.PasswordHintInputChanged("Test123")) } } + + @Test + fun `toggling one password field visibility should toggle the other`() { + // should start with 3 Show buttons: + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(3)[1] + .performClick() + + // after clicking, only the "previous master password" field in the "Show" state + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(1) + + // and there should be 2 hide buttons now, and we'll click the second one: + composeTestRule + .onAllNodesWithContentDescription("Hide") + .assertCountEquals(2)[0] + .performClick() + + // then there should be all three show buttons again + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(3) + } } private val DEFAULT_STATE = ResetPasswordState( 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 33cd0fcd59..96c71d3bb3 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 @@ -1,9 +1,11 @@ package com.x8bit.bitwarden.ui.auth.feature.setpassword import androidx.compose.ui.test.assert +import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.isDialog import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput @@ -117,6 +119,31 @@ class SetPasswordScreenTest : BaseComposeTest() { viewModel.trySendAction(SetPasswordAction.PasswordHintInputChanged("Test123")) } } + + @Test + fun `toggling one password field visibility should toggle the other`() { + // should start with 2 Show buttons: + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(2)[0] + .performClick() + + // after clicking there should be no Show buttons: + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(0) + + // and there should be 2 hide buttons now, and we'll click the second one: + composeTestRule + .onAllNodesWithContentDescription("Hide") + .assertCountEquals(2)[1] + .performClick() + + // then there should be two show buttons again + composeTestRule + .onAllNodesWithContentDescription("Show") + .assertCountEquals(2) + } } private val DEFAULT_STATE = SetPasswordState(