diff --git a/app/src/main/java/com/kickstarter/ui/activities/compose/ChangePasswordScreen.kt b/app/src/main/java/com/kickstarter/ui/activities/compose/ChangePasswordScreen.kt index 9f92105bbf..bc07b460d8 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/compose/ChangePasswordScreen.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/compose/ChangePasswordScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -59,6 +60,18 @@ fun ChangePasswordPreview() { } } +enum class ChangePasswordScreenTestTag { + BACK_BUTTON, + ACCEPT_BUTTON, + CURRENT_PASSWORD, + NEW_PASSWORD_1, + NEW_PASSWORD_2, + WARNING_TEXT, + PAGE_TITLE, + PROGRESS_BAR, + SUBTITLE, +} + @Composable fun ChangePasswordScreen( onBackClicked: () -> Unit, @@ -101,11 +114,14 @@ fun ChangePasswordScreen( TopToolBar( title = stringResource(id = R.string.Change_password), titleColor = colors.kds_support_700, + titleModifier = Modifier.testTag(ChangePasswordScreenTestTag.PAGE_TITLE.name), leftOnClickAction = onBackClicked, leftIconColor = colors.kds_support_700, + leftIconModifier = Modifier.testTag(ChangePasswordScreenTestTag.BACK_BUTTON.name), backgroundColor = colors.kds_white, right = { IconButton( + modifier = Modifier.testTag(ChangePasswordScreenTestTag.ACCEPT_BUTTON.name), onClick = { onAcceptButtonClicked.invoke( currentPassword, @@ -144,11 +160,13 @@ fun ChangePasswordScreen( .padding(padding) ) { AnimatedVisibility(visible = showProgressBar) { - KSLinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + KSLinearProgressIndicator( + modifier = Modifier.fillMaxWidth().testTag(ChangePasswordScreenTestTag.PROGRESS_BAR.name) + ) } Text( - modifier = Modifier.padding(dimensions.paddingMedium), + modifier = Modifier.padding(dimensions.paddingMedium).testTag(ChangePasswordScreenTestTag.SUBTITLE.name), text = stringResource( id = R.string.Well_ask_you_to_sign_back_into_the_Kickstarter_app_once_youve_changed_your_password ), @@ -164,7 +182,7 @@ fun ChangePasswordScreen( ) { KSHiddenTextInput( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().testTag(ChangePasswordScreenTestTag.CURRENT_PASSWORD.name), onValueChanged = { currentPassword = it }, label = stringResource(id = R.string.Current_password), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), @@ -180,7 +198,7 @@ fun ChangePasswordScreen( Spacer(modifier = Modifier.height(dimensions.listItemSpacingMedium)) KSHiddenTextInput( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().testTag(ChangePasswordScreenTestTag.NEW_PASSWORD_1.name), onValueChanged = { newPasswordLine1 = it }, label = stringResource(id = R.string.New_password), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), @@ -196,7 +214,7 @@ fun ChangePasswordScreen( Spacer(modifier = Modifier.height(dimensions.listItemSpacingMedium)) KSHiddenTextInput( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().testTag(ChangePasswordScreenTestTag.NEW_PASSWORD_2.name), onValueChanged = { newPasswordLine2 = it }, label = stringResource(id = R.string.Confirm_password), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), @@ -212,7 +230,7 @@ fun ChangePasswordScreen( AnimatedVisibility(visible = warningText.isNotEmpty()) { Text( - modifier = Modifier.padding(dimensions.paddingMedium), + modifier = Modifier.padding(dimensions.paddingMedium).testTag(ChangePasswordScreenTestTag.WARNING_TEXT.name), text = warningText, style = typography.body2, color = colors.kds_support_700 diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/ChangePasswordScreenTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/ChangePasswordScreenTest.kt new file mode 100644 index 0000000000..cb812cb09d --- /dev/null +++ b/app/src/test/java/com/kickstarter/ui/activities/compose/ChangePasswordScreenTest.kt @@ -0,0 +1,178 @@ +package com.kickstarter.ui.activities.compose + +import androidx.compose.material.rememberScaffoldState +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.isNotDisplayed +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextClearance +import androidx.compose.ui.test.performTextInput +import androidx.test.platform.app.InstrumentationRegistry +import com.kickstarter.KSRobolectricTestCase +import com.kickstarter.R +import com.kickstarter.ui.compose.designsystem.KSTheme +import org.junit.Test + +class ChangePasswordScreenTest : KSRobolectricTestCase() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + + private val backButton = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.BACK_BUTTON.name) + + private val acceptButton = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.ACCEPT_BUTTON.name) + + private val currentPasswordEditText = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.CURRENT_PASSWORD.name) + + private val newPasswordLine1EditText = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.NEW_PASSWORD_1.name) + + private val newPasswordLine2EditText = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.NEW_PASSWORD_2.name) + + private val warningText = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.WARNING_TEXT.name) + + private val pageTitle = + composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.PAGE_TITLE.name) + + private val progressBar = composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.PROGRESS_BAR.name) + + private val subtitle = composeTestRule.onNodeWithTag(ChangePasswordScreenTestTag.SUBTITLE.name) + + @Test + fun `test screen init`() { + composeTestRule.setContent { + KSTheme { + ChangePasswordScreen( + onBackClicked = { }, + onAcceptButtonClicked = { _, _ -> }, + showProgressBar = false, + scaffoldState = rememberScaffoldState() + ) + } + } + + val pageTitleText = context.getString(R.string.Change_password) + val changePasswordDescriptionText = context.getString(R.string.Well_ask_you_to_sign_back_into_the_Kickstarter_app_once_youve_changed_your_password) + + pageTitle.assertIsDisplayed() + pageTitle.assertTextEquals(pageTitleText) + subtitle.assertIsDisplayed() + subtitle.assertTextEquals(changePasswordDescriptionText) + backButton.assertIsDisplayed() + acceptButton.assertIsDisplayed() + acceptButton.assertIsNotEnabled() + progressBar.assertDoesNotExist() + newPasswordLine1EditText.assertIsDisplayed() + newPasswordLine2EditText.assertIsDisplayed() + currentPasswordEditText.assertIsDisplayed() + warningText.isNotDisplayed() + } + + @Test + fun `test back button clicks`() { + composeTestRule.setContent { + KSTheme { + ChangePasswordScreen( + onBackClicked = { }, + onAcceptButtonClicked = { _, _ -> }, + showProgressBar = false, + scaffoldState = rememberScaffoldState() + ) + } + } + } + + @Test + fun `when passwords not long enough or matching, accept button disabled and warning text displayed`() { + composeTestRule.setContent { + KSTheme { + ChangePasswordScreen( + onBackClicked = { }, + onAcceptButtonClicked = { _, _ -> }, + showProgressBar = false, + scaffoldState = rememberScaffoldState() + ) + } + } + + val passwordMismatchWarningText = context.getString(R.string.Passwords_matching_message) + val passwordLengthWarningText = context.getString(R.string.Password_min_length_message) + + // current password too short, button should be disabled but no warning text + currentPasswordEditText.performTextInput("pass") + newPasswordLine1EditText.performTextInput("password1") + newPasswordLine2EditText.performTextInput("password1") + + acceptButton.assertIsNotEnabled() + warningText.isNotDisplayed() + + currentPasswordEditText.performTextClearance() + newPasswordLine1EditText.performTextClearance() + newPasswordLine2EditText.performTextClearance() + + // only one new password field filled out, button should be disabled but no warning text + currentPasswordEditText.performTextInput("password") + newPasswordLine1EditText.performTextInput("password1") + + acceptButton.assertIsNotEnabled() + warningText.isNotDisplayed() + + currentPasswordEditText.performTextClearance() + newPasswordLine1EditText.performTextClearance() + newPasswordLine2EditText.performTextClearance() + + // new passwords not long enough, button should be disabled and no warning text + currentPasswordEditText.performTextInput("password") + newPasswordLine1EditText.performTextInput("pass") + newPasswordLine2EditText.performTextInput("password2") + + acceptButton.assertIsNotEnabled() + warningText.isDisplayed() + warningText.assertTextEquals(passwordLengthWarningText) + + currentPasswordEditText.performTextClearance() + newPasswordLine1EditText.performTextClearance() + newPasswordLine2EditText.performTextClearance() + + // passwords are long enough but don't match, button should be disabled and warning text shows + currentPasswordEditText.performTextInput("password") + newPasswordLine1EditText.performTextInput("password1") + newPasswordLine2EditText.performTextInput("password2") + + acceptButton.assertIsNotEnabled() + warningText.isDisplayed() + warningText.assertTextEquals(passwordMismatchWarningText) + } + + @Test + fun `when passwords valid and matching, accept button enabled and no warning text visible`() { + var acceptButtonClickedCount = 0 + composeTestRule.setContent { + KSTheme { + ChangePasswordScreen( + onBackClicked = { }, + onAcceptButtonClicked = { _, _ -> acceptButtonClickedCount++ }, + showProgressBar = false, + scaffoldState = rememberScaffoldState() + ) + } + } + + // valid password and matching + currentPasswordEditText.performTextInput("password") + newPasswordLine1EditText.performTextInput("passwordA") + newPasswordLine2EditText.performTextInput("passwordA") + + acceptButton.assertIsEnabled() + warningText.isNotDisplayed() + acceptButton.performClick() + assertEquals(1, acceptButtonClickedCount) + } +}