diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 492c5e66..7b71a160 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -361,6 +361,14 @@
Could not load community.
Retry
Notice
+ Community
+ Replies
+ Enter a comment
+ Enter a reply
+ Comments %1$s
+ Comments are unavailable for this post.
+ Could not load the post.
+ Purchased
%1$s · Total %2$d
Be the first\nto cheer them on!
Leave support
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 6dd9ef24..7b3bcbbf 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -361,6 +361,14 @@
コミュニティを読み込めませんでした。
再試行
お知らせ
+ コミュニティ
+ 返信
+ コメントを入力してください
+ 返信を入力してください
+ コメント %1$s
+ この投稿ではコメントを利用できません。
+ 投稿を読み込めません。
+ 購入済み
%1$s · 全体 %2$d
最初の応援を\n待っています!
応援を残す
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 00c0e742..5e73da2e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -360,6 +360,14 @@
커뮤니티를 불러오지 못했습니다.
다시 시도
공지
+ 커뮤니티
+ 답글
+ 댓글을 입력하세요
+ 답글을 입력하세요
+ 댓글 %1$s
+ 댓글을 사용할 수 없는 게시물입니다.
+ 게시물을 불러올 수 없습니다.
+ 구매 완료
%1$s · 전체 %2$d
당신의 첫 응원을\n기다리고 있어요!
응원 남기기
diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailReplyUiContractTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailReplyUiContractTest.kt
new file mode 100644
index 00000000..0d4185c4
--- /dev/null
+++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailReplyUiContractTest.kt
@@ -0,0 +1,109 @@
+package kr.co.vividnext.sodalive.v2.creator.channel.community
+
+import android.app.Application
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.EditText
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.core.app.ApplicationProvider
+import kr.co.vividnext.sodalive.R
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import java.io.File
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [28], application = Application::class)
+class CreatorChannelCommunityDetailReplyUiContractTest {
+
+ @Test
+ fun `상세와 답글 activity layout은 title back content input bar 계약을 제공한다`() {
+ val detail = inflateView(R.layout.activity_creator_channel_community_detail)
+ val reply = inflateView(R.layout.activity_creator_channel_community_reply)
+
+ assertNotNull(detail.findViewById(R.id.btn_creator_channel_community_detail_back))
+ assertNotNull(detail.findViewById(R.id.tv_creator_channel_community_detail_title))
+ assertNotNull(detail.findViewById(R.id.tv_creator_channel_community_detail_comment_header))
+ assertNotNull(detail.findViewById(R.id.rv_creator_channel_community_detail_comments))
+ assertNotNull(detail.findViewById(R.id.layout_creator_channel_community_detail_paywall))
+ assertNotNull(detail.findViewById(R.id.tv_creator_channel_community_detail_paywall_price))
+ assertNotNull(detail.findViewById(R.id.layout_creator_channel_community_detail_input_bar))
+ assertNotNull(detail.findViewById(R.id.et_creator_channel_community_detail_comment))
+ assertNotNull(detail.findViewById(R.id.btn_creator_channel_community_detail_send))
+ assertRemovedId("tv_creator_channel_community_detail_comment_unavailable")
+
+ assertNotNull(reply.findViewById(R.id.btn_creator_channel_community_reply_back))
+ assertNotNull(reply.findViewById(R.id.tv_creator_channel_community_reply_title))
+ assertNotNull(reply.findViewById(R.id.rv_creator_channel_community_reply))
+ assertNotNull(reply.findViewById(R.id.layout_creator_channel_community_reply_input_bar))
+ assertNotNull(reply.findViewById(R.id.et_creator_channel_community_reply))
+ assertNotNull(reply.findViewById(R.id.btn_creator_channel_community_reply_send))
+ }
+
+ @Test
+ fun `상세와 답글 item layout은 more latest reply만 제공하고 reaction row를 제거한다`() {
+ val commentItem = inflateView(R.layout.item_creator_channel_community_comment)
+ val replyItem = inflateView(R.layout.item_creator_channel_community_reply)
+
+ assertNotNull(commentItem.findViewById(R.id.iv_creator_channel_community_comment_more))
+ assertNotNull(commentItem.findViewById(R.id.layout_creator_channel_community_comment_latest_reply))
+ assertNotNull(commentItem.findViewById(R.id.tv_creator_channel_community_comment_latest_reply_body))
+ assertRemovedId("iv_creator_channel_community_comment_heart")
+ assertRemovedId("layout_creator_channel_community_comment_reaction")
+
+ assertNotNull(replyItem.findViewById(R.id.iv_creator_channel_community_reply_item_more))
+ assertRemovedId("iv_creator_channel_community_reply_item_heart")
+ }
+
+ @Test
+ fun `커뮤니티 상세 답글 문자열은 한국어 영어 일본어에 존재한다`() {
+ listOf("values", "values-en", "values-ja").forEach { valuesDir ->
+ val strings = projectFile("app/src/main/res/$valuesDir/strings.xml").readText()
+
+ assertTrue(strings.contains("name=\"creator_channel_community_detail_title\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_reply_title\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_comment_placeholder\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_reply_placeholder\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_comment_header\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_comment_unavailable\""))
+ assertTrue(strings.contains("name=\"creator_channel_community_invalid_post\""))
+ }
+ }
+
+ @Test
+ fun `AppDI와 manifest는 상세 답글 Activity ViewModel을 등록한다`() {
+ val appDi = projectFile("app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt").readText()
+ val manifest = projectFile("app/src/main/AndroidManifest.xml").readText()
+
+ assertTrue(appDi.contains("CreatorChannelCommunityDetailViewModel"))
+ assertTrue(appDi.contains("CreatorChannelCommunityReplyViewModel"))
+ assertTrue(manifest.contains(".v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity"))
+ assertTrue(manifest.contains(".v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyActivity"))
+ assertTrue(manifest.contains("android:windowSoftInputMode=\"stateAlwaysHidden|adjustResize\""))
+ }
+
+ private fun inflateView(layoutId: Int): View {
+ val context = ApplicationProvider.getApplicationContext()
+ return LayoutInflater.from(context).inflate(layoutId, null, false)
+ }
+
+ private fun assertRemovedId(name: String) {
+ val context = ApplicationProvider.getApplicationContext()
+ assertEquals(0, context.resources.getIdentifier(name, "id", context.packageName))
+ }
+
+ private fun projectFile(path: String): File {
+ var current = File(checkNotNull(System.getProperty("user.dir"))).absoluteFile
+ while (current.parentFile != null && !File(current, "settings.gradle").exists()) {
+ current = checkNotNull(current.parentFile)
+ }
+ return File(current, path)
+ }
+}