Skip to content

Commit d7cb241

Browse files
authored
GiveNicknameActivity Compose 전환 및 테스트 추가 (#404)
* GiveNicknameActivity Compose 전환 및 테스트 추가 * 닉네임 입력 로딩 및 길이 제한 처리 보강 --------- Co-authored-by: unam98 <unam98@users.noreply.github.com>
1 parent 8a6a84c commit d7cb241

6 files changed

Lines changed: 504 additions & 173 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package com.runnect.runnect.presentation.login
2+
3+
import androidx.compose.ui.test.assertIsDisplayed
4+
import androidx.compose.ui.test.assertIsNotEnabled
5+
import androidx.compose.ui.test.junit4.createComposeRule
6+
import androidx.compose.ui.test.onNodeWithTag
7+
import androidx.compose.ui.test.onNodeWithText
8+
import androidx.compose.ui.test.performClick
9+
import androidx.compose.ui.test.performTextInput
10+
import com.runnect.runnect.presentation.ui.theme.RunnectTheme
11+
import org.junit.Assert.assertEquals
12+
import org.junit.Rule
13+
import org.junit.Test
14+
15+
class GiveNicknameScreenTest {
16+
17+
@get:Rule
18+
val composeTestRule = createComposeRule()
19+
20+
@Test
21+
fun 닉네임_입력_화면_요소가_노출된다() {
22+
composeTestRule.setContent {
23+
RunnectTheme {
24+
GiveNicknameScreen(
25+
state = GiveNicknameUiState(),
26+
onNickNameChange = {},
27+
onStartClick = {}
28+
)
29+
}
30+
}
31+
32+
composeTestRule.onNodeWithText("RUNNECT").assertIsDisplayed()
33+
composeTestRule.onNodeWithText("에서 사용할").assertIsDisplayed()
34+
composeTestRule.onNodeWithText("이름을 입력해주세요").assertIsDisplayed()
35+
composeTestRule.onNodeWithText("닉네임을 입력해주세요").assertIsDisplayed()
36+
composeTestRule.onNodeWithText("시작하기").assertIsDisplayed()
37+
}
38+
39+
@Test
40+
fun 닉네임이_비어있으면_시작하기_버튼은_비활성화된다() {
41+
composeTestRule.setContent {
42+
RunnectTheme {
43+
GiveNicknameScreen(
44+
state = GiveNicknameUiState.from(
45+
nickName = "",
46+
uiState = com.runnect.runnect.presentation.state.UiState.Empty
47+
),
48+
onNickNameChange = {},
49+
onStartClick = {}
50+
)
51+
}
52+
}
53+
54+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.START_BUTTON).assertIsNotEnabled()
55+
}
56+
57+
@Test
58+
fun 닉네임을_입력하면_변경_콜백이_호출된다() {
59+
val inputs = mutableListOf<String>()
60+
61+
composeTestRule.setContent {
62+
RunnectTheme {
63+
GiveNicknameScreen(
64+
state = GiveNicknameUiState(),
65+
onNickNameChange = { inputs.add(it) },
66+
onStartClick = {}
67+
)
68+
}
69+
}
70+
71+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.NICKNAME_INPUT)
72+
.performTextInput("러너")
73+
74+
assertEquals("러너", inputs.last())
75+
}
76+
77+
@Test
78+
fun 닉네임을_최대_길이보다_길게_입력하면_잘라서_콜백이_호출된다() {
79+
val inputs = mutableListOf<String>()
80+
81+
composeTestRule.setContent {
82+
RunnectTheme {
83+
GiveNicknameScreen(
84+
state = GiveNicknameUiState(),
85+
onNickNameChange = { inputs.add(it) },
86+
onStartClick = {}
87+
)
88+
}
89+
}
90+
91+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.NICKNAME_INPUT)
92+
.performTextInput("러너러너러너러너")
93+
94+
assertEquals("러너러너러너러", inputs.last())
95+
}
96+
97+
@Test
98+
fun 로딩_상태면_닉네임_입력은_비활성화된다() {
99+
composeTestRule.setContent {
100+
RunnectTheme {
101+
GiveNicknameScreen(
102+
state = GiveNicknameUiState.from(
103+
nickName = "러너",
104+
uiState = com.runnect.runnect.presentation.state.UiState.Loading
105+
),
106+
onNickNameChange = {},
107+
onStartClick = {}
108+
)
109+
}
110+
}
111+
112+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.NICKNAME_INPUT)
113+
.assertIsNotEnabled()
114+
}
115+
116+
@Test
117+
fun 시작하기_버튼을_누르면_콜백이_호출된다() {
118+
var clickedCount = 0
119+
120+
composeTestRule.setContent {
121+
RunnectTheme {
122+
GiveNicknameScreen(
123+
state = GiveNicknameUiState.from(
124+
nickName = "러너",
125+
uiState = com.runnect.runnect.presentation.state.UiState.Empty
126+
),
127+
onNickNameChange = {},
128+
onStartClick = { clickedCount += 1 }
129+
)
130+
}
131+
}
132+
133+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.START_BUTTON).performClick()
134+
135+
assertEquals(1, clickedCount)
136+
}
137+
138+
@Test
139+
fun 로딩_상태면_인디케이터가_노출된다() {
140+
composeTestRule.setContent {
141+
RunnectTheme {
142+
GiveNicknameScreen(
143+
state = GiveNicknameUiState.from(
144+
nickName = "러너",
145+
uiState = com.runnect.runnect.presentation.state.UiState.Loading
146+
),
147+
onNickNameChange = {},
148+
onStartClick = {}
149+
)
150+
}
151+
}
152+
153+
composeTestRule.onNodeWithTag(GiveNicknameScreenTestTags.LOADING_INDICATOR)
154+
.assertIsDisplayed()
155+
}
156+
}

app/src/main/java/com/runnect/runnect/presentation/login/GiveNickNameViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class GiveNickNameViewModel @Inject constructor(
2424
get() = _statusCode
2525
private val _statusCode = MutableLiveData<Int>()
2626

27+
fun updateNickNameInput(nickName: String) {
28+
this.nickName.value = nickName
29+
}
30+
2731
fun updateNickName() = launchWithHandler {
2832
val requestPatchNickName = RequestPatchNickName(nickName.value.toString())
2933

@@ -44,4 +48,4 @@ class GiveNickNameViewModel @Inject constructor(
4448
companion object {
4549
const val REDUNDANT_NICKNAME_ERROR = 400
4650
}
47-
}
51+
}
Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
package com.runnect.runnect.presentation.login
22

33
import android.content.Intent
4-
import android.graphics.Rect
54
import android.os.Bundle
6-
import android.view.MotionEvent
5+
import androidx.activity.compose.setContent
76
import androidx.activity.viewModels
8-
import androidx.core.view.isVisible
7+
import androidx.appcompat.app.AppCompatActivity
8+
import androidx.compose.runtime.getValue
9+
import androidx.compose.runtime.livedata.observeAsState
910
import com.runnect.runnect.R
10-
import com.runnect.runnect.binding.BindingActivity
11-
import com.runnect.runnect.databinding.ActivityGiveNicknameBinding
1211
import com.runnect.runnect.presentation.MainActivity
1312
import com.runnect.runnect.presentation.state.UiState
13+
import com.runnect.runnect.presentation.ui.theme.RunnectTheme
1414
import com.runnect.runnect.util.analytics.Analytics
1515
import com.runnect.runnect.util.analytics.EventName
1616
import com.runnect.runnect.util.analytics.EventName.Param
17-
import com.runnect.runnect.util.extension.hideKeyboard
1817
import com.runnect.runnect.util.extension.showToast
1918
import com.runnect.runnect.util.preference.AuthUtil.saveToken
2019
import dagger.hilt.android.AndroidEntryPoint
2120

2221
@AndroidEntryPoint
23-
class GiveNicknameActivity :
24-
BindingActivity<ActivityGiveNicknameBinding>(R.layout.activity_give_nickname) {
22+
class GiveNicknameActivity : AppCompatActivity() {
2523
private val viewModel: GiveNickNameViewModel by viewModels()
24+
2625
override fun onCreate(savedInstanceState: Bundle?) {
2726
super.onCreate(savedInstanceState)
28-
binding.vm = viewModel
29-
binding.lifecycleOwner = this
3027
Analytics.logEvent(EventName.VIEW_GIVE_NICKNAME)
31-
addListener()
3228
addObserver()
33-
}
3429

35-
private fun addListener() {
36-
binding.tvGiveNicknameFinish.setOnClickListener {
37-
viewModel.updateNickName()
30+
setContent {
31+
val nickName by viewModel.nickName.observeAsState("")
32+
val uiState by viewModel.uiState.observeAsState(UiState.Empty)
33+
34+
RunnectTheme {
35+
GiveNicknameScreen(
36+
state = GiveNicknameUiState.from(
37+
nickName = nickName,
38+
uiState = uiState
39+
),
40+
onNickNameChange = viewModel::updateNickNameInput,
41+
onStartClick = viewModel::updateNickName
42+
)
43+
}
3844
}
3945
}
4046

@@ -46,27 +52,10 @@ class GiveNicknameActivity :
4652
}
4753

4854
private fun addObserver() {
49-
viewModel.nickName.observe(this) {
50-
with(binding.tvGiveNicknameFinish) {
51-
if (it.isNullOrEmpty()) {
52-
isActivated = false
53-
isClickable = false
54-
} else {
55-
isActivated = true
56-
isClickable = true
57-
}
58-
}
59-
}
6055
viewModel.uiState.observe(this) { state ->
6156
when (state) {
62-
UiState.Empty -> binding.indeterminateBar.isVisible = false
63-
UiState.Loading -> {
64-
with(binding) {
65-
indeterminateBar.isVisible = true
66-
tvGiveNicknameFinish.isClickable = false
67-
}
68-
}
69-
57+
UiState.Empty,
58+
UiState.Loading -> Unit
7059
UiState.Success -> handleSuccessfulSignup()
7160
UiState.Failure -> handleUnSuccessfulSignup()
7261
}
@@ -80,7 +69,6 @@ class GiveNicknameActivity :
8069
)
8170
saveSignTokenInfo()
8271
showToast("회원가입 되었습니다")
83-
binding.indeterminateBar.isVisible = false
8472
moveToMain()
8573
}
8674

@@ -93,27 +81,9 @@ class GiveNicknameActivity :
9381
}
9482

9583
private fun handleUnSuccessfulSignup() {
96-
binding.indeterminateBar.isVisible = false
9784
if (viewModel.statusCode.value == 400) {
9885
showToast(getString(R.string.my_page_edit_name_redundant_warning))
9986
}
100-
binding.tvGiveNicknameFinish.isClickable = true
101-
}
102-
103-
//키보드 밖 터치 시, 키보드 내림
104-
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
105-
val focusView = currentFocus
106-
if (focusView != null) {
107-
val rect = Rect()
108-
focusView.getGlobalVisibleRect(rect)
109-
val x = ev!!.x.toInt()
110-
val y = ev.y.toInt()
111-
if (!rect.contains(x, y)) {
112-
hideKeyboard(focusView)
113-
}
114-
}
115-
return super.dispatchTouchEvent(ev)
11687
}
11788

11889
}
119-

0 commit comments

Comments
 (0)