feat(creator-channel): FanTalk 상세 화면을 추가한다
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.detail
|
||||
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class CreatorChannelFanTalkDetailUiContractTest {
|
||||
|
||||
@Test
|
||||
fun `상세 Activity source는 payload intent와 ViewModel 이벤트를 연결한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/detail/CreatorChannelFanTalkDetailActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("BaseActivity<ActivityCreatorChannelFantalkDetailBinding>"))
|
||||
assertTrue(source.contains("private val viewModel: CreatorChannelFanTalkDetailViewModel by viewModel()"))
|
||||
assertTrue(
|
||||
source.contains(
|
||||
"fun newIntent(context: Context, creatorId: Long, " +
|
||||
"payload: CreatorChannelFanTalkDetailPayload): Intent"
|
||||
)
|
||||
)
|
||||
assertTrue(source.contains("putExtra(EXTRA_PAYLOAD, payload)"))
|
||||
assertTrue(source.contains("IntentCompat.getParcelableExtra"))
|
||||
assertTrue(source.contains("if (creatorId <= 0L || payload.fanTalkId <= 0L)"))
|
||||
assertTrue(source.contains("R.string.creator_channel_community_invalid_post"))
|
||||
assertTrue(source.contains("viewModel.load(creatorId, payload)"))
|
||||
assertTrue(source.contains("doAfterTextChanged"))
|
||||
assertTrue(source.contains("viewModel.updateReplyInput"))
|
||||
assertTrue(source.contains("viewModel.submitReply()"))
|
||||
assertTrue(source.contains("viewModel.cancelReplyEdit()"))
|
||||
assertTrue(source.contains("viewModel.replacementModalEventLiveData.observe"))
|
||||
assertTrue(source.contains("viewModel.replyChangedEventLiveData.observe"))
|
||||
assertTrue(source.contains("setResult(Activity.RESULT_OK)"))
|
||||
assertTrue(source.contains("viewModel.consumeReplyChangedEvent()"))
|
||||
assertTrue(source.contains("viewModel.startReplyEdit()"))
|
||||
assertTrue(source.contains("showDeleteReplyDialog()"))
|
||||
assertTrue(source.contains("ivCreatorChannelFantalkDetailReplyMore.isVisible = reply.fanTalkId > 0L"))
|
||||
assertTrue(source.contains("V2ModalDialog("))
|
||||
assertTrue(source.contains("getString(modal.titleResId)"))
|
||||
assertTrue(source.contains("getString(modal.descriptionResId)"))
|
||||
assertTrue(source.contains("getString(modal.confirmResId)"))
|
||||
assertTrue(source.contains("getString(modal.cancelResId)"))
|
||||
assertTrue(source.contains("R.drawable.bg_creator_channel_community_send_enabled"))
|
||||
assertTrue(source.contains("R.drawable.bg_creator_channel_community_send_disabled"))
|
||||
assertTrue(source.contains("R.drawable.ic_new_arrow_up_white"))
|
||||
assertTrue(source.contains("R.drawable.ic_new_arrow_up_gray"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `상세 layout은 커뮤니티 답글 구조와 FanTalk 답글 card 토큰을 사용한다`() {
|
||||
val layout = projectFile("app/src/main/res/layout/activity_creator_channel_fantalk_detail.xml").readText()
|
||||
|
||||
assertTrue(layout.contains("""android:background="@color/black""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/btn_creator_channel_fantalk_detail_back""""))
|
||||
assertTrue(layout.contains("""android:layout_height="60dp""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/iv_creator_channel_fantalk_detail_profile""""))
|
||||
assertTrue(layout.contains("""android:layout_width="42dp""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/tv_creator_channel_fantalk_detail_nickname""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/tv_creator_channel_fantalk_detail_content""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/layout_creator_channel_fantalk_detail_reply""""))
|
||||
assertTrue(layout.contains("@drawable/bg_creator_channel_fantalk_reply"))
|
||||
assertTrue(layout.contains("""android:id="@+id/iv_creator_channel_fantalk_detail_reply_profile""""))
|
||||
assertTrue(layout.contains("""android:layout_width="20dp""""))
|
||||
assertTextViewUsesStyle(
|
||||
layout,
|
||||
"tv_creator_channel_fantalk_detail_reply_nickname",
|
||||
"@style/Typography.Caption3"
|
||||
)
|
||||
assertTextViewUsesStyle(
|
||||
layout,
|
||||
"tv_creator_channel_fantalk_detail_reply_time",
|
||||
"@style/Typography.Caption3"
|
||||
)
|
||||
assertTextViewContains(
|
||||
layout,
|
||||
"tv_creator_channel_fantalk_detail_reply_nickname",
|
||||
"""android:layout_width="wrap_content"""
|
||||
)
|
||||
assertTextViewContains(
|
||||
layout,
|
||||
"tv_creator_channel_fantalk_detail_reply_time",
|
||||
"""android:layout_marginStart="@dimen/spacing_4"""
|
||||
)
|
||||
assertTrue(layout.contains("""android:id="@+id/iv_creator_channel_fantalk_detail_reply_more""""))
|
||||
assertTrue(layout.contains("""android:id="@+id/layout_creator_channel_fantalk_detail_input_bar""""))
|
||||
assertTrue(layout.contains("view_creator_channel_fantalk_detail_reply_header_spacer"))
|
||||
assertTrue(layout.contains("android:layout_width=\"0dp\""))
|
||||
assertTrue(layout.contains("android:layout_weight=\"1\""))
|
||||
assertTrue(layout.contains("@drawable/bg_creator_channel_community_input_field"))
|
||||
assertTrue(layout.contains("@string/creator_channel_community_reply_placeholder"))
|
||||
assertTrue(layout.contains("""android:maxLines="3""""))
|
||||
assertTrue(layout.contains("@drawable/bg_creator_channel_community_send_disabled"))
|
||||
assertTrue(layout.contains("@drawable/ic_new_arrow_up_gray"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `상세 Manifest DI ViewModel source 계약을 등록한다`() {
|
||||
val manifest = projectFile("app/src/main/AndroidManifest.xml").readText()
|
||||
val appDi = projectFile("app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt").readText()
|
||||
val viewModel = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/detail/CreatorChannelFanTalkDetailViewModel.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(manifest.contains(".v2.creator.channel.fantalk.detail.CreatorChannelFanTalkDetailActivity"))
|
||||
assertTrue(manifest.contains("""android:windowSoftInputMode="stateAlwaysHidden|adjustResize""""))
|
||||
assertTrue(
|
||||
appDi.contains(
|
||||
"import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.detail." +
|
||||
"CreatorChannelFanTalkDetailViewModel"
|
||||
)
|
||||
)
|
||||
assertTrue(appDi.contains("viewModel { CreatorChannelFanTalkDetailViewModel(get(), get()) }"))
|
||||
assertTrue(viewModel.contains("fun cancelReplyEdit()"))
|
||||
assertTrue(viewModel.contains("fun consumeReplyChangedEvent()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `답글 대체 모달 문자열은 리소스 id와 다국어 strings로 제공한다`() {
|
||||
val uiModels = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/detail/CreatorChannelFanTalkDetailUiModels.kt"
|
||||
).readText()
|
||||
val koStrings = projectFile("app/src/main/res/values/strings.xml").readText()
|
||||
val enStrings = projectFile("app/src/main/res/values-en/strings.xml").readText()
|
||||
val jaStrings = projectFile("app/src/main/res/values-ja/strings.xml").readText()
|
||||
|
||||
assertTrue(uiModels.contains("R.string.screen_user_profile_reply_edit"))
|
||||
assertTrue(uiModels.contains("R.string.creator_channel_fantalk_reply_replace_description"))
|
||||
assertTrue(uiModels.contains("R.string.cancel"))
|
||||
assertTrue(uiModels.contains("R.string.confirm"))
|
||||
assertFalse(uiModels.contains("기존 답글이 신규 답글로 대체됩니다"))
|
||||
assertTrue(koStrings.contains("creator_channel_fantalk_reply_replace_description"))
|
||||
assertTrue(enStrings.contains("creator_channel_fantalk_reply_replace_description"))
|
||||
assertTrue(jaStrings.contains("creator_channel_fantalk_reply_replace_description"))
|
||||
assertEquals(R.string.screen_user_profile_reply_edit, CreatorChannelFanTalkReplacementModalUiModel().titleResId)
|
||||
}
|
||||
|
||||
private fun assertTextViewUsesStyle(layout: String, id: String, style: String) {
|
||||
val textViewPattern = Regex("<TextView[\\s\\S]*?android:id=\"@\\+id/$id\"[\\s\\S]*?style=\"$style\"")
|
||||
|
||||
assertTrue("Expected $id to use $style", textViewPattern.containsMatchIn(layout))
|
||||
}
|
||||
|
||||
private fun assertTextViewContains(layout: String, id: String, expected: String) {
|
||||
val textViewPattern = Regex("<TextView[\\s\\S]*?android:id=\"@\\+id/$id\"[\\s\\S]*?/>")
|
||||
val textView = textViewPattern.find(layout)?.value.orEmpty()
|
||||
|
||||
assertTrue("Expected $id to contain $expected", textView.contains(expected))
|
||||
}
|
||||
|
||||
private fun projectFile(path: String): File = File(projectRoot(), path)
|
||||
|
||||
private fun projectRoot(): File {
|
||||
return generateSequence(File(System.getProperty("user.dir") ?: ".").absoluteFile) { it.parentFile }
|
||||
.first { File(it, "settings.gradle").exists() }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user