Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸš€ 3단계 - νšŒμ›κ°€μž…(μœ νš¨μ„± 검사) #92

Open
wants to merge 14 commits into
base: hyunjung-choi
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1 align="center">πŸ“ android-signup</h1>
<h1 align="center">android-signup</h1>

## πŸš€ 2단계 - νšŒμ›κ°€μž…(λ·°)

Expand All @@ -11,4 +11,23 @@
- [x] Password Confirm μž…λ ₯ κ΅¬ν˜„
- [x] Sign Up λ²„νŠΌ κ΅¬ν˜„
- [x] λ ˆμ΄μ•„μ›ƒ λ°°μΉ˜ν•˜κΈ°(padding)
- [x] ν•¨μˆ˜ 뢄리 및 λ¦¬νŒ©ν† λ§
- [x] ν•¨μˆ˜ 뢄리 및 λ¦¬νŒ©ν† λ§

## πŸš€ 3단계 - νšŒμ›κ°€μž…(μœ νš¨μ„± 검사)

### βœ… κ΅¬ν˜„ν•  κΈ°λŠ₯ λͺ©λ‘

- [x] 2단계 ν”Όλ“œλ°± 반영
- [x] μ—λŸ¬ λ©”μ‹œμ§€ μΆ”κ°€
- [ ] `SignupTextField` 뢄리
- [ ] `UsernameTextField`
- [x] `EmailTextField`
- [ ] `PasswordTextField`
- [ ] Username μœ νš¨μ„± 검사
- [x] Email μœ νš¨μ„± 검사
- [ ] Password μœ νš¨μ„± 검사
- [ ] Password Confirm μœ νš¨μ„± 검사
- [ ] UI ν…ŒμŠ€νŠΈ μž‘μ„±
- [ ] `UsernameTextField`
- [x] `EmailTextField`
- [ ] `PasswordTextField`
84 changes: 84 additions & 0 deletions app/src/androidTest/java/nextstep/signup/InputValidationTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package nextstep.signup

import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTextInput
import nextstep.signup.ui.component.EmailTextField
import nextstep.signup.ui.component.UsernameTextField
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class InputValidationTest {

@get:Rule
val composeTestRule = createComposeRule()
private val username = mutableStateOf("")
private val email = mutableStateOf("")
Comment on lines +18 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutableStateOfλ₯Ό ν™œμš©ν•΄μ„œ μ»΄ν¬λ„ŒνŠΈμ˜ 값을 변경해주기보닀,
맀 ν…ŒμŠ€νŠΈλ§ˆλ‹€ μƒˆ μ»΄ν¬λ„ŒνŠΈλ₯Ό setContentν•΄μ„œ, 거기에 ν•„μš”ν•œ 값을 직접 λ„£μ–΄μ£ΌλŠ” ν˜•νƒœλŠ” μ–΄λ–¨κΉŒμš”?


@Before
fun setup() {
composeTestRule.setContent {
Column {
UsernameTextField(username = username)
EmailTextField(email = email)
}
}
}
hyunjung-choi marked this conversation as resolved.
Show resolved Hide resolved

@Test
fun μ‚¬μš©μž_이름은_2μ—μ„œ_5μžμ—¬μ•Ό_ν•œλ‹€() {
// when
username.value = "κΉ€μ»΄ν¬μ¦ˆ"

// then
composeTestRule
.onNodeWithText(USERNAME_LENGTH_ERROR)
.assertDoesNotExist()
}

@Test
fun μ‚¬μš©μž_이름이_2μ—μ„œ_5μžκ°€_μ•„λ‹ˆλ©΄_μ—λŸ¬λ©”μ‹œμ§€κ°€_λ…ΈμΆœλœλ‹€() {
// when
username.value = "κΉ€μ»΄ν¬μ¦ˆμž…λ‹ˆλ‹€"

// then
composeTestRule
.onNodeWithText(USERNAME_LENGTH_ERROR)
.assertExists()
}

@Test
fun 이메일_ν˜•μ‹μ΄_μ˜¬λ°”λ₯Έ_경우() {
// when
email.value = "[email protected]"

// then
composeTestRule
.onNodeWithText(EMAIL_ERROR)
.assertDoesNotExist()
}

@Test
fun 이메일_ν˜•μ‹μ΄_μ˜¬λ°”λ₯΄μ§€_μ•Šμ€_경우() {
// when
composeTestRule.onNodeWithTag("emailTextField").performTextInput("compose")

// then
composeTestRule
.onNodeWithText(EMAIL_ERROR)
.assertExists()
}

companion object {
private const val USERNAME_LENGTH_ERROR = "이름은 2~5μžμ—¬μ•Ό ν•©λ‹ˆλ‹€."
private const val USERNAME_LETTER_ERROR = "μ΄λ¦„μ—λŠ” μˆ«μžλ‚˜ κΈ°ν˜Έκ°€ 포함될 수 μ—†μŠ΅λ‹ˆλ‹€."
private const val EMAIL_ERROR = "이메일 ν˜•μ‹μ΄ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."
private const val PASSWORD_LENGTH_ERROR = "λΉ„λ°€λ²ˆν˜ΈλŠ” 8~16μžμ—¬μ•Ό ν•©λ‹ˆλ‹€."
private const val PASSWORD_LETTER_ERROR = "λΉ„λ°€λ²ˆν˜ΈλŠ” 영문과 숫자λ₯Ό 포함해야 ν•©λ‹ˆλ‹€."
private const val PASSWORD_CONFIRM_ERROR = "λΉ„λ°€λ²ˆν˜Έκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."
}
}
191 changes: 2 additions & 189 deletions app/src/main/java/nextstep/signup/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,203 +3,16 @@ package nextstep.signup
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import nextstep.signup.ui.theme.Pink80
import nextstep.signup.ui.screen.SignupScreen
import nextstep.signup.ui.theme.SignupTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SignupTheme {
ScreenLayoutSetting()
SignupScreen()
}
}
}
}

@Composable
private fun TitleTextComponent() {
Text(
text = stringResource(id = R.string.signup_title),
style = TextStyle(
fontSize = 26.sp,
lineHeight = 20.sp,
fontWeight = FontWeight(700),
color = Color.Black,
letterSpacing = 0.26.sp
),
modifier = Modifier
.fillMaxWidth(1.0F)
.padding(bottom = 42.dp),
textAlign = TextAlign.Center,
)
}

@Composable
private fun TextFieldComponent(
label: Int,
input: MutableState<String>,
inputEntered: MutableState<Boolean>
) {
Row(modifier = Modifier.padding(bottom = 32.dp)) {
val focusManager = LocalFocusManager.current

TextField(
value = input.value,
onValueChange = {
input.value = it
},
label = {
Text(text = stringResource(id = label))
},
modifier = Modifier
.alignByBaseline()
.weight(1.0F),
singleLine = true,
visualTransformation = if (label == R.string.signup_password || label == R.string.signup_password_confirm) PasswordVisualTransformation() else VisualTransformation.None,
keyboardOptions = KeyboardOptions(
autoCorrect = false,
keyboardType = when (label) {
R.string.signup_username -> KeyboardType.Text
R.string.signup_email -> KeyboardType.Email
R.string.signup_password,
R.string.signup_password_confirm -> KeyboardType.Password

else -> KeyboardType.Text
}
),
keyboardActions = KeyboardActions(
onDone = {
inputEntered.value = true
if (label == R.string.signup_password_confirm) focusManager.clearFocus()
else focusManager.moveFocus(FocusDirection.Next)
}
)
)
}
}

@Composable
private fun SignupButtonComponent() {
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.fillMaxWidth(1.0F),
colors = ButtonColors(
containerColor = Color(Pink80.value),
contentColor = Color.White,
disabledContainerColor = Color.LightGray,
disabledContentColor = Color.White
)
) {
Text(
text = stringResource(id = R.string.signup_button),
modifier = Modifier
.padding(vertical = 15.dp),
color = Color.White,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight(500),
letterSpacing = 0.1.sp
)
)
}
}

@Composable
private fun ScreenLayoutSetting() {
val name = remember {
mutableStateOf("")
}
val nameEntered = remember {
mutableStateOf(false)
}
val email = remember {
mutableStateOf("")
}
val emailEntered = remember {
mutableStateOf(false)
}
val password = remember {
mutableStateOf("")
}
val passwordEntered = remember {
mutableStateOf(false)
}
val passwordConfirm = remember {
mutableStateOf("")
}
val passwordConfirmEntered = remember {
mutableStateOf(false)
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
TitleTextComponent()
TextFieldComponent(
label = R.string.signup_username,
input = name,
inputEntered = nameEntered
)
TextFieldComponent(
label = R.string.signup_email,
input = email,
inputEntered = emailEntered
)
TextFieldComponent(
label = R.string.signup_password,
input = password,
inputEntered = passwordEntered
)
TextFieldComponent(
label = R.string.signup_password_confirm,
input = passwordConfirm,
inputEntered = passwordConfirmEntered
)
SignupButtonComponent()
}
}
}

@Preview(showBackground = true)
@Composable
fun TextFieldSettingPreview() {
SignupTheme {
ScreenLayoutSetting()
}
}
Loading