Skip to content
10 changes: 10 additions & 0 deletions presentation/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package daily.dayo.presentation.screen.account

import androidx.activity.ComponentActivity
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.test.ext.junit.runners.AndroidJUnit4
import daily.dayo.presentation.theme.DayoTheme
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AuthPasswordFieldUsageTest {

@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()

private fun assertContentDescriptionCount(contentDescription: String, expectedCount: Int) {
composeRule.onAllNodesWithContentDescription(contentDescription)
.assertCountEquals(expectedCount)
}

@Test
fun signInEmailInputLayout_showsClearAndVisibilityOnEditablePasswordField() {
var email by mutableStateOf("")
var password by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
SignInEmailInputLayout(
emailValue = email,
onEmailChange = { email = it },
passwordValue = password,
onPasswordChange = { password = it }
)
}
}

assertContentDescriptionCount("Clear password", 1)
assertContentDescriptionCount("Show password", 1)
}

@Test
fun signUpPasswordConfirmLayout_hidesClearOnDisabledReferenceField() {
var password by mutableStateOf("password")
var passwordConfirmation by mutableStateOf("confirm")

composeRule.setContent {
DayoTheme {
SetPasswordView(
passwordInputViewCondition = false,
passwordConfirmationViewCondition = true,
password = password,
setPassword = { password = it },
isPasswordFormatValid = true,
passwordConfirmation = passwordConfirmation,
setPasswordConfirmation = { passwordConfirmation = it }
)
}
}

assertContentDescriptionCount("Clear password", 1)
assertContentDescriptionCount("Show password", 2)
}

@Test
fun resetPasswordConfirmLayout_hidesClearOnDisabledReferenceField() {
var password by mutableStateOf("password")
var passwordConfirmation by mutableStateOf("confirm")

composeRule.setContent {
DayoTheme {
NewPasswordLayout(
resetPasswordStep = ResetPasswordStep.NEW_PASSWORD_CONFIRM,
password = password,
setPassword = { password = it },
isPasswordFormatValid = true,
passwordConfirmation = passwordConfirmation,
setPasswordConfirmation = { passwordConfirmation = it }
)
}
}

assertContentDescriptionCount("Clear password", 1)
assertContentDescriptionCount("Show password", 2)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package daily.dayo.presentation.view

import androidx.activity.ComponentActivity
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import daily.dayo.presentation.theme.DayoTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class DayoPasswordTextFieldTest {

@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()

private fun assertContentDescriptionCount(contentDescription: String, expectedCount: Int) {
composeRule.onAllNodesWithContentDescription(contentDescription)
.assertCountEquals(expectedCount)
}

@Test
fun givenTextAndDefaultFlags_whenRendered_thenClearAndEyeIconsAreBothShown() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it }
)
}
}

assertContentDescriptionCount("Clear password", 1)
assertContentDescriptionCount("Show password", 1)
}

@Test
fun givenText_whenClearIconTapped_thenFieldIsEmptied() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it }
)
}
}

composeRule.onNodeWithContentDescription("Clear password").performClick()

composeRule.runOnIdle {
assertEquals("", passwordValue)
}
assertContentDescriptionCount("Clear password", 0)
assertContentDescriptionCount("Show password", 1)
}

@Test
fun givenDefaultVisibilityIcon_whenTapped_thenContentDescriptionChangesToHidePassword() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it }
)
}
}

composeRule.onNodeWithContentDescription("Show password").performClick()

assertContentDescriptionCount("Hide password", 1)
}

@Test
fun givenErrorState_whenRendered_thenOnlyErrorIconIsShown() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
isError = true,
errorMessage = "error"
)
}
}

assertContentDescriptionCount("error icon", 1)
assertContentDescriptionCount("Clear password", 0)
assertContentDescriptionCount("Show password", 0)
}

@Test
fun givenVisibilityIconHiddenAndTextExists_whenRendered_thenOnlyClearIconIsShown() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
showVisibilityIcon = false
)
}
}

assertContentDescriptionCount("Clear password", 1)
assertContentDescriptionCount("Show password", 0)
assertContentDescriptionCount("Hide password", 0)
}

@Test
fun givenVisibilityIconHiddenAndTextBlank_whenRendered_thenNoTrailingIconsAreShown() {
var passwordValue by mutableStateOf("")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
showVisibilityIcon = false
)
}
}

assertContentDescriptionCount("Clear password", 0)
assertContentDescriptionCount("Show password", 0)
assertContentDescriptionCount("Hide password", 0)
assertContentDescriptionCount("error icon", 0)
}

@Test
fun givenDisabledFieldWithText_whenRendered_thenClearIsHiddenAndEyeRemains() {
var passwordValue by mutableStateOf("password")

composeRule.setContent {
DayoTheme {
DayoPasswordTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
isEnabled = false
)
}
}

assertContentDescriptionCount("Clear password", 0)
assertContentDescriptionCount("Show password", 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import daily.dayo.presentation.screen.account.model.EmailExistenceStatus
import daily.dayo.presentation.theme.Dark
import daily.dayo.presentation.theme.DayoTheme
import daily.dayo.presentation.theme.Gray2_767B83
import daily.dayo.presentation.theme.Gray3_9FA5AE
import daily.dayo.presentation.theme.White_FFFFFF
import daily.dayo.presentation.view.DayoPasswordTextField
import daily.dayo.presentation.view.DayoTextButton
Expand Down Expand Up @@ -354,19 +355,21 @@ fun ResetPasswordScreen(
AnimatedVisibility(
visible = (resetPasswordStep.stepNum in 1..2),
) {
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(4.dp)
)
Text(
text = if (resetPasswordStep == ResetPasswordStep.EMAIL_INPUT)
stringResource(R.string.reset_password_email_sub_title)
else if (resetPasswordStep == ResetPasswordStep.EMAIL_VERIFICATION)
stringResource(R.string.reset_password_new_password_sub_title)
else "",
style = DayoTheme.typography.b6.copy(color = Gray2_767B83),
)
Column {
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(4.dp)
)
Text(
text = if (resetPasswordStep == ResetPasswordStep.EMAIL_INPUT)
stringResource(R.string.reset_password_email_sub_title)
else if (resetPasswordStep == ResetPasswordStep.EMAIL_VERIFICATION)
stringResource(R.string.reset_password_new_password_sub_title)
else "",
style = DayoTheme.typography.b6.copy(color = Gray2_767B83),
)
}
}

// Contents 영역
Expand Down Expand Up @@ -647,6 +650,9 @@ private fun EmailCertificationLayout(
requestEmailCertification: (String) -> Unit = {},
) {
val certificateEmailAuthCodeFormat = Regex("^\\d{6}$")
val isServerCertificationCodeReady =
certificationCode != EMAIL_CERTIFICATE_AUTH_CODE_INITIAL.toString() &&
certificationCode != RESET_PASSWORD_EMAIL_CERTIFICATE_AUTH_CODE_FAIL.toString()

var tryCount by remember { mutableStateOf(1) }
val isPaused = remember { mutableStateOf(false) }
Expand All @@ -657,10 +663,12 @@ private fun EmailCertificationLayout(
remember { mutableStateOf((R.string.reset_password_email_certification_fail_wrong)) }

setNextButtonEnabled(
certificateEmailAuthCodeFormat.matches(certificationInputCode)
certificateEmailAuthCodeFormat.matches(certificationInputCode) &&
isServerCertificationCodeReady
)
setIsNextButtonClickable(
certificateEmailAuthCodeFormat.matches(certificationInputCode)
certificateEmailAuthCodeFormat.matches(certificationInputCode) &&
isServerCertificationCodeReady
)

key(tryCount) {
Expand All @@ -678,6 +686,7 @@ private fun EmailCertificationLayout(
isError = isEmailCertificateError ?: false,
errorMessage = stringResource(timerErrorMessageRedId.value),
timeOutErrorMessage = stringResource(R.string.reset_password_email_certification_fail_time_out),
labelColor = Gray3_9FA5AE,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
)
}
Expand Down Expand Up @@ -712,6 +721,10 @@ private fun EmailCertificationLayout(
text = stringResource(R.string.reset_password_email_certification_resend_button),
onClick = {
tryCount++
setCertificationInputCode("")
timerErrorMessageRedId.value =
R.string.reset_password_email_certification_fail_wrong
setIsEmailCertificateError(false)
requestEmailCertification(email)
},
underline = true,
Expand All @@ -725,7 +738,7 @@ private fun EmailCertificationLayout(

@Composable
@Preview
private fun NewPasswordLayout(
internal fun NewPasswordLayout(
resetPasswordStep: ResetPasswordStep = ResetPasswordStep.NEW_PASSWORD_INPUT,
isNextButtonEnabled: Boolean = false,
setNextButtonEnabled: (Boolean) -> Unit = {},
Expand Down Expand Up @@ -808,4 +821,4 @@ enum class ResetPasswordStep(val stepNum: Int) {
EMAIL_VERIFICATION(2), // 인증번호 입력
NEW_PASSWORD_INPUT(3), // 비밀번호 입력
NEW_PASSWORD_CONFIRM(4), // 비밀번호 재입력
}
}
Loading
Loading