feat(home): 팔로잉 탭 화면을 연결한다

This commit is contained in:
2026-06-25 23:34:24 +09:00
parent f009a2edb8
commit 01d0367f50
3 changed files with 467 additions and 1 deletions

View File

@@ -0,0 +1,144 @@
package kr.co.vividnext.sodalive.v2.main.home
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
class HomeFollowingFragmentSourceTest {
@Test
fun `home layout exposes following content and section recycler ids`() {
val layout = homeMainLayoutSource()
assertTrue(layout.contains("@+id/nsv_home_following_content"))
assertTrue(layout.contains("@+id/rv_home_following_creators"))
assertTrue(layout.contains("@+id/rv_home_following_on_air_lives"))
assertTrue(layout.contains("@+id/rv_home_following_recent_chats"))
assertTrue(layout.contains("@+id/rv_home_following_monthly_schedules"))
assertTrue(layout.contains("@+id/rv_home_following_recent_news"))
}
@Test
fun `home main fragment injects following viewmodel and wires adapters`() {
val source = homeMainFragmentSource()
assertTrue(source.contains("HomeFollowingViewModel"))
assertTrue(source.contains("private val homeFollowingViewModel: HomeFollowingViewModel by viewModel()"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingCreatorAdapter"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingLiveAdapter"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingChatAdapter"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingScheduleAdapter"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingNewsAdapter"))
assertTrue(source.contains("HomeFollowingCreatorAdapter"))
assertTrue(source.contains("HomeFollowingLiveAdapter"))
assertTrue(source.contains("HomeFollowingChatAdapter"))
assertTrue(source.contains("HomeFollowingScheduleAdapter"))
assertTrue(source.contains("HomeFollowingNewsAdapter"))
}
@Test
fun `following tab branch shows following content and hides other home surfaces`() {
val source = homeMainFragmentSource()
val branch = source.substringAfter("HOME_TAB_FOLLOWING ->")
.substringBefore("}\n }")
assertTrue(branch.contains("binding.nsvHomeFollowingContent.visibility = View.VISIBLE"))
assertTrue(branch.contains("binding.nsvHomeRecommendationContent.visibility = View.GONE"))
assertTrue(branch.contains("binding.rvHomeCreatorRankings.visibility = View.GONE"))
}
@Test
fun `following tab loads following content only once`() {
val source = homeMainFragmentSource()
assertTrue(source.contains("private var hasLoadedFollowing = false"))
assertTrue(source.contains("if (!hasLoadedFollowing)"))
assertTrue(source.contains("hasLoadedFollowing = true"))
assertTrue(source.contains("homeFollowingViewModel.loadFollowing()"))
}
@Test
fun `following section chevrons call callback without starting activity`() {
val source = homeMainFragmentSource()
val callback = source.substringAfter("private fun onFollowingSectionMoreClick")
.substringBefore("\n private fun")
assertTrue(source.contains("onFollowingSectionMoreClick("))
assertTrue(source.contains("binding.viewHomeFollowingCreatorsTitle"))
assertTrue(source.contains("binding.viewHomeFollowingOnAirTitle"))
assertTrue(source.contains("binding.viewHomeFollowingRecentChatsTitle"))
assertTrue(source.contains("binding.viewHomeFollowingMonthlySchedulesTitle"))
assertTrue(source.contains("binding.viewHomeFollowingRecentNewsTitle"))
assertTrue(source.contains("setOnClickListener"))
assertTrue(source.contains("onFollowingSectionMoreClick(HomeFollowingSection."))
assertFalse(callback.contains("startActivity"))
}
@Test
fun `following state binding clears or binds each following section`() {
val source = homeMainFragmentSource()
assertTrue(source.contains("followingStateLiveData.observe(viewLifecycleOwner)"))
assertTrue(source.contains("is HomeFollowingUiState.Content -> bindHomeFollowingContent(state)"))
assertTrue(source.contains("HomeFollowingUiState.LoginRequired,"))
assertTrue(source.contains("HomeFollowingUiState.Empty,"))
assertTrue(source.contains("is HomeFollowingUiState.Error -> bindHomeFollowingEmpty()"))
assertTrue(source.contains("followingCreatorAdapter.submitItems(emptyList())"))
assertTrue(source.contains("followingLiveAdapter.submitItems(emptyList())"))
assertTrue(source.contains("followingChatAdapter.submitItems(emptyList())"))
assertTrue(source.contains("followingScheduleAdapter.submitItems(emptyList())"))
assertTrue(source.contains("followingNewsAdapter.submitItems(emptyList())"))
assertTrue(source.contains("bindFollowingCreatorSection(content.followingCreators)"))
assertTrue(source.contains("bindFollowingLiveSection(content.onAirLives)"))
assertTrue(source.contains("bindFollowingChatSection(content.recentChats)"))
assertTrue(source.contains("bindFollowingScheduleSection(content.monthlySchedules)"))
assertTrue(source.contains("bindFollowingNewsSection(content.recentNews)"))
}
@Test
fun `following adapters bind figma required item fields`() {
val liveAdapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt"
).readText()
val liveLayout = projectFile("app/src/main/res/layout/item_home_following_live.xml").readText()
val scheduleAdapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.kt"
).readText()
val scheduleLayout = projectFile("app/src/main/res/layout/item_home_following_schedule.xml").readText()
val newsAdapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt"
).readText()
val newsLayout = projectFile("app/src/main/res/layout/item_home_following_news_content.xml").readText()
assertTrue(liveLayout.contains("@+id/tv_home_following_live_started_at"))
assertTrue(liveAdapter.contains("startedAtText.text"))
assertTrue(liveAdapter.contains("item.startedAtUtc"))
assertTrue(scheduleLayout.contains("@+id/iv_home_following_schedule_creator_profile"))
assertTrue(scheduleLayout.contains("@+id/tv_home_following_schedule_type"))
assertTrue(scheduleAdapter.contains("profileImage.loadHomeCreatorProfileImage(item.creatorProfileImageUrl)"))
assertTrue(scheduleAdapter.contains("typeText.setText(item.typeLabelResId)"))
assertTrue(scheduleAdapter.contains("item.isOnAir"))
assertTrue(scheduleAdapter.contains("R.string.screen_home_following_on_air"))
assertTrue(newsLayout.contains("@+id/tv_home_following_news_label"))
assertTrue(newsLayout.contains("@+id/tv_home_following_news_title"))
assertTrue(newsAdapter.contains("labelText.setText(content.labelResId)"))
assertTrue(newsAdapter.contains("titleText.text = content.title"))
assertTrue(newsAdapter.contains("createdAtText.text = content.visibleFromText"))
}
private fun homeMainFragmentSource(): String {
return projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt").readText()
}
private fun homeMainLayoutSource(): String {
return projectFile("app/src/main/res/layout/fragment_v2_main_home.xml").readText()
}
private fun projectFile(relativePath: String): File {
val candidates = listOf(File(relativePath), File("../$relativePath"))
return candidates.first { it.exists() }.canonicalFile
}
}