feat(chat): 채팅 탭 화면 동작을 연결한다

This commit is contained in:
2026-06-10 15:06:35 +09:00
parent 516e4a94bf
commit b38e58af9a
2 changed files with 186 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ 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)
@@ -83,11 +84,80 @@ class ChatMainFragmentLayoutTest {
assertEquals(ConstraintLayout.LayoutParams.PARENT_ID, buttonParams.bottomToBottom)
}
@Test
fun `채팅 fragment source는 화면 초기 구성과 첫 페이지 로드를 연결한다`() {
val source = chatMainFragmentSource()
assertTrue(source.contains("private val viewModel: ChatMainViewModel by viewModel()"))
assertTrue(source.contains("ChatRoomListAdapter"))
assertTrue(source.contains("LoadingDialog(requireActivity(), layoutInflater)"))
assertTrue(source.contains("tvTitleBarTitle.setText(R.string.tab_chat)"))
assertTrue(source.contains("ivTitleBarMenu.setImageResource(R.drawable.ic_bar_cash)"))
assertTrue(source.contains("llTitleBarActions.addView"))
assertTrue(source.contains("setImageResource(R.drawable.ic_bar_search)"))
assertTrue(source.contains("setMenus("))
assertTrue(source.contains("R.string.screen_chat_filter_all"))
assertTrue(source.contains("R.string.screen_chat_filter_ai"))
assertTrue(source.contains("R.string.screen_chat_filter_dm"))
assertTrue(source.contains("viewModel.loadFirstPage()"))
}
@Test
fun `채팅 fragment source는 filter list pagination observe를 연결한다`() {
val source = chatMainFragmentSource()
assertTrue(source.contains("setOnTabSelectedListener"))
assertTrue(source.contains("ChatRoomFilter.fromTabIndex(index)"))
assertTrue(source.contains("LinearLayoutManager(requireContext())"))
assertTrue(source.contains("rvChatRooms.adapter = chatRoomListAdapter"))
assertTrue(source.contains("addOnScrollListener(object : RecyclerView.OnScrollListener()"))
assertTrue(source.contains("findLastVisibleItemPosition()"))
assertTrue(source.contains("viewModel.loadNextPage()"))
assertTrue(source.contains("chatRoomStateLiveData.observe(viewLifecycleOwner)"))
assertTrue(source.contains("ChatRoomListUiState.Content"))
assertTrue(source.contains("ChatRoomListUiState.Empty"))
assertTrue(source.contains("ChatRoomListUiState.Error"))
assertTrue(source.contains("viewModel.isLoading.observe(viewLifecycleOwner)"))
assertTrue(source.contains("viewModel.toastLiveData.observe(viewLifecycleOwner)"))
}
@Test
fun `채팅 fragment source는 AI 항목만 채팅방으로 이동한다`() {
val source = chatMainFragmentSource()
assertTrue(source.contains("private fun onChatRoomClick(item: ChatRoomListUiItem)"))
assertTrue(source.contains("if (item.chatType != ChatRoomType.AI) return"))
assertTrue(source.contains("ChatRoomActivity.newIntent(requireContext(), item.roomId)"))
assertTrue(source.contains("btnChatFloating.setOnClickListener { }"))
}
@Test
fun `채팅 fragment source는 탭 전환 및 첫 페이지 로딩 시 스크롤을 최상단으로 이동시킨다`() {
val source = chatMainFragmentSource()
assertTrue(source.contains("setOnTabSelectedListener"))
assertTrue(source.contains("rvChatRooms.scrollToPosition(0)"))
assertTrue(source.contains("!state.isAppending"))
}
private fun inflateView(layoutResId: Int): View {
val context = ApplicationProvider.getApplicationContext<Context>()
return LayoutInflater.from(context).inflate(layoutResId, null, false)
}
private fun chatMainFragmentSource(): String = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.kt"
).readText()
private fun projectFile(relativePath: String): File {
val candidates = listOf(
File(relativePath),
File("../$relativePath")
)
return candidates.firstOrNull { it.exists() }
?: error("Project file not found: $relativePath")
}
private fun View.containsViewIdContaining(idNamePart: String): Boolean {
if (id != View.NO_ID) {
val idName = runCatching { resources.getResourceEntryName(id) }.getOrNull()