feat(modal): V2 공통 모달창을 추가한다

This commit is contained in:
2026-07-08 14:20:27 +09:00
parent bc6443e5bd
commit 8e086c4888
4 changed files with 314 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
package kr.co.vividnext.sodalive.v2.components.modal
import android.app.Application
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.io.File
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [28], application = Application::class)
class V2ModalDialogTest {
@Test
fun `cancelButtonTitle이 blank이면 확인 버튼만 표시한다`() {
val activity = Robolectric.buildActivity(FragmentActivity::class.java).setup().get()
var confirmClickCount = 0
val dialog = V2ModalDialog(
activity = activity,
layoutInflater = activity.layoutInflater,
title = "안내",
desc = "확인만 있는 모달",
confirmButtonTitle = "확인",
confirmButtonClick = { confirmClickCount += 1 },
cancelButtonTitle = " "
)
dialog.show(width = 360)
val alertDialog = alertDialogOf(dialog)
assertTrue(alertDialog.isShowing)
dialog.dialogView.tvConfirm.performClick()
assertEquals(View.GONE, dialog.dialogView.tvCancel.visibility)
assertEquals(View.VISIBLE, dialog.dialogView.tvConfirm.visibility)
assertEquals("확인", dialog.dialogView.tvConfirm.text.toString())
assertFalse(alertDialog.isShowing)
assertEquals(1, confirmClickCount)
}
@Test
fun `확인 버튼을 빠르게 여러 번 눌러도 액션은 한 번만 실행한다`() {
val activity = Robolectric.buildActivity(FragmentActivity::class.java).setup().get()
var confirmClickCount = 0
val dialog = V2ModalDialog(
activity = activity,
layoutInflater = activity.layoutInflater,
title = "안내",
desc = "중복 클릭 방지",
confirmButtonTitle = "확인",
confirmButtonClick = { confirmClickCount += 1 }
)
dialog.show(width = 360)
dialog.dialogView.tvConfirm.performClick()
dialog.dialogView.tvConfirm.performClick()
assertEquals(1, confirmClickCount)
}
@Test
fun `cancelButtonTitle이 있으면 두 버튼을 표시하고 취소 액션을 실행한다`() {
val activity = Robolectric.buildActivity(FragmentActivity::class.java).setup().get()
var cancelClickCount = 0
val dialog = V2ModalDialog(
activity = activity,
layoutInflater = activity.layoutInflater,
title = "삭제",
desc = "정말 삭제할까요?",
confirmButtonTitle = "삭제",
confirmButtonClick = {},
cancelButtonTitle = "취소",
cancelButtonClick = { cancelClickCount += 1 }
)
dialog.show(width = 360)
val alertDialog = alertDialogOf(dialog)
assertTrue(alertDialog.isShowing)
dialog.dialogView.tvCancel.performClick()
assertEquals(View.VISIBLE, dialog.dialogView.tvCancel.visibility)
assertEquals(View.VISIBLE, dialog.dialogView.tvConfirm.visibility)
assertEquals("취소", dialog.dialogView.tvCancel.text.toString())
assertEquals("삭제", dialog.dialogView.tvConfirm.text.toString())
assertFalse(alertDialog.isShowing)
assertEquals(1, cancelClickCount)
}
@Test
fun `title과 desc는 파라미터 값을 표시한다`() {
val activity = Robolectric.buildActivity(FragmentActivity::class.java).setup().get()
val dialog = V2ModalDialog(
activity = activity,
layoutInflater = activity.layoutInflater,
title = "모달 제목",
desc = "첫 줄\n두 번째 줄",
confirmButtonTitle = "확인",
confirmButtonClick = {}
)
assertEquals("모달 제목", dialog.dialogView.tvTitle.text.toString())
assertEquals("첫 줄\n두 번째 줄", dialog.dialogView.tvDesc.text.toString())
}
@Test
fun `cancelButtonClick이 null이어도 취소 버튼은 모달을 닫는다`() {
val activity = Robolectric.buildActivity(FragmentActivity::class.java).setup().get()
val dialog = V2ModalDialog(
activity = activity,
layoutInflater = activity.layoutInflater,
title = "안내",
desc = "취소 액션 없음",
confirmButtonTitle = "확인",
confirmButtonClick = {},
cancelButtonTitle = "취소"
)
dialog.show(width = 360)
val alertDialog = alertDialogOf(dialog)
assertTrue(alertDialog.isShowing)
dialog.dialogView.tvCancel.performClick()
assertFalse(alertDialog.isShowing)
}
@Test
fun `layout과 drawable은 Figma 확정 리소스 계약을 포함한다`() {
val layout = projectFile("app/src/main/res/layout/dialog_v2_modal.xml").readText()
val background = projectFile("app/src/main/res/drawable/bg_v2_modal.xml").readText()
assertTrue(layout.contains("android:background=\"@drawable/bg_v2_modal\""))
assertTrue(layout.contains("android:id=\"@+id/tv_title\""))
assertTrue(layout.contains("android:minHeight=\"64dp\""))
assertTrue(layout.contains("android:id=\"@+id/tv_desc\""))
assertTrue(layout.contains("android:id=\"@+id/tv_cancel\""))
assertTrue(layout.contains("android:id=\"@+id/tv_confirm\""))
assertTrue(layout.contains("android:textColor=\"@color/soda_400\""))
assertTrue(layout.contains("android:textColor=\"@color/white\""))
assertTrue(background.contains("@color/gray_900"))
assertTrue(background.contains("@dimen/radius_14"))
}
private fun projectFile(path: String): File = File(projectRoot(), path)
private fun alertDialogOf(dialog: V2ModalDialog): AlertDialog {
val field = V2ModalDialog::class.java.getDeclaredField("alertDialog")
field.isAccessible = true
return field.get(dialog) as AlertDialog
}
private fun projectRoot(): File {
return generateSequence(File(System.getProperty("user.dir") ?: ".").absoluteFile) { it.parentFile }
.first { File(it, "settings.gradle").exists() }
}
}