feat(content): 전체 탭 화면 연결을 추가한다
This commit is contained in:
@@ -71,6 +71,154 @@ class ContentMainFragmentSourceTest {
|
||||
assertTrue(source.contains("screen_content_ranking_type_like_count"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 layout과 source는 Phase 5 요구를 포함한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
val fragmentLayout = projectFile("app/src/main/res/layout/fragment_v2_main_content.xml").readText()
|
||||
val dayFilterBackground = projectFile("app/src/main/res/drawable/bg_content_all_day_filter.xml").readText()
|
||||
val selectedDayBackground = projectFile("app/src/main/res/drawable/bg_content_all_day_selected.xml").readText()
|
||||
|
||||
assertTrue(fragmentLayout.contains("@+id/layout_content_all_surface"))
|
||||
assertTrue(fragmentLayout.contains("@+id/view_content_all_type_tabs"))
|
||||
assertTrue(fragmentLayout.contains("@+id/layout_content_all_day_filter"))
|
||||
assertTrue(fragmentLayout.contains("android:layout_width=\"wrap_content\""))
|
||||
assertTrue(fragmentLayout.contains("android:layout_height=\"36dp\""))
|
||||
assertTrue(fragmentLayout.contains("android:layout_marginTop=\"@dimen/spacing_8\""))
|
||||
assertTrue(fragmentLayout.contains("android:background=\"@drawable/bg_content_all_day_filter\""))
|
||||
assertTrue(fragmentLayout.contains("android:padding=\"@dimen/spacing_4\""))
|
||||
assertTrue(fragmentLayout.contains("app:layout_constraintEnd_toEndOf=\"parent\""))
|
||||
assertTrue(fragmentLayout.contains("app:layout_constraintHorizontal_bias=\"0.5\""))
|
||||
assertTrue(dayFilterBackground.contains("@color/gray_900"))
|
||||
assertTrue(dayFilterBackground.contains("@dimen/radius_8"))
|
||||
assertTrue(selectedDayBackground.contains("@color/white"))
|
||||
assertTrue(selectedDayBackground.contains("@dimen/radius_8"))
|
||||
assertTrue(fragmentLayout.contains("@+id/layout_content_all_sort_bar"))
|
||||
assertTrue(fragmentLayout.contains("android:layout_height=\"52dp\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/tv_content_all_total_label"))
|
||||
assertTrue(fragmentLayout.contains("android:text=\"@string/screen_content_tab_all\""))
|
||||
assertTrue(fragmentLayout.contains("android:textColor=\"@color/white\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/tv_content_all_total_count"))
|
||||
assertTrue(fragmentLayout.contains("style=\"@style/Typography.Body2\""))
|
||||
assertTrue(fragmentLayout.contains("android:textColor=\"@color/gray_500\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/layout_content_all_sort_button"))
|
||||
assertTrue(fragmentLayout.contains("android:layout_height=\"match_parent\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/tv_content_all_sort_label"))
|
||||
assertTrue(fragmentLayout.contains("style=\"@style/Typography.Body3\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/iv_content_all_sort"))
|
||||
assertTrue(fragmentLayout.contains("android:src=\"@drawable/ic_new_sort\""))
|
||||
assertTrue(fragmentLayout.contains("@+id/rv_content_all_items"))
|
||||
assertTrue(fragmentLayout.contains("@+id/layout_content_all_empty_error"))
|
||||
assertTrue(fragmentLayout.contains("@+id/tv_content_all_empty_error"))
|
||||
|
||||
assertTrue(source.contains("private val contentAllTabViewModel: ContentAllTabViewModel by viewModel()"))
|
||||
assertTrue(source.contains("ContentAllAudioCardAdapter"))
|
||||
assertTrue(source.contains("ContentAllSeriesCardAdapter"))
|
||||
assertTrue(source.contains("CONTENT_ALL_GRID_SPAN_COUNT"))
|
||||
assertTrue(source.contains("GridLayoutManager(requireContext(), CONTENT_ALL_GRID_SPAN_COUNT)"))
|
||||
assertTrue(source.contains("addContentGridItemSpacing(CONTENT_ALL_GRID_SPAN_COUNT)"))
|
||||
assertTrue(source.contains("MainContentAllType.AUDIO"))
|
||||
assertTrue(source.contains("MainContentAllType.SERIES"))
|
||||
assertTrue(source.contains("MainContentAllType.ORIGINAL"))
|
||||
assertTrue(source.contains("MainContentAllType.FREE"))
|
||||
assertTrue(source.contains("MainContentAllType.POINT"))
|
||||
assertFalse(source.contains("screen_content_all_type_all"))
|
||||
assertFalse(source.contains("screen_content_all_type_serialized"))
|
||||
assertTrue(source.contains("CreatorChannelSortPopup"))
|
||||
assertTrue(source.contains("ContentSort.entries"))
|
||||
assertTrue(source.contains("toLabelResId()"))
|
||||
assertTrue(source.contains("contentAllDayOfWeekOptions"))
|
||||
assertTrue(source.contains("toContentAllDayLabelResId()"))
|
||||
assertTrue(source.contains("R.drawable.bg_content_all_day_selected"))
|
||||
assertTrue(source.contains("if (isSelected) R.color.black else R.color.white"))
|
||||
assertTrue(source.contains("loadMore()"))
|
||||
assertTrue(source.contains("consumePaginationErrorMessage()"))
|
||||
assertTrue(source.contains("openAudioContentDetail(audioContentId)"))
|
||||
assertTrue(source.contains("openSeriesDetail(seriesId)"))
|
||||
assertTrue(source.contains("override fun onDestroyView()"))
|
||||
assertTrue(source.contains("sortPopup?.dismiss()"))
|
||||
assertTrue(source.contains("sortPopup = null"))
|
||||
assertTrue(source.contains("bannerBinder = null"))
|
||||
assertTrue(source.contains("layoutContentAllEmptyError"))
|
||||
assertTrue(source.contains("tvContentAllEmptyError.setText"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 source는 최신 전체 상태를 보관한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertSourceContains(
|
||||
source,
|
||||
"private var currentAllTabState: MainContentAllTabUiState? = null",
|
||||
"전체 탭은 Content만 보관하지 않고 Empty Error Loading까지 포함하는 최신 상태를 캐시해야 한다."
|
||||
)
|
||||
assertFalse(
|
||||
"전체 탭 캐시는 MainContentAllTabUiState.Content 전용 currentAllTabContent에 머물면 안 된다.",
|
||||
source.contains("currentAllTabContent: MainContentAllTabUiState.Content?")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 observer는 guard 전에 최신 상태를 저장한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertSourceContains(source, "contentAllTabViewModel.allTabStateLiveData.observe(viewLifecycleOwner) { state ->")
|
||||
assertSourceContains(source, "currentAllTabState = state")
|
||||
assertSourceContains(source, "renderAllTabState(state)")
|
||||
assertTrue(
|
||||
"allTabStateLiveData observer는 render/guard 전에 currentAllTabState = state를 먼저 실행해야 한다.",
|
||||
source.indexOf("currentAllTabState = state") < source.indexOf("renderAllTabState(state)")
|
||||
)
|
||||
assertFalse(
|
||||
"inactive ALL 탭 emission을 버리는 observer/render guard는 제거되어야 한다.",
|
||||
source.contains("if (currentContentTab != CONTENT_TAB_ALL) return")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 진입은 캐시 상태를 우선 렌더링하고 필요할 때만 초기 로드한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertSourceContains(source, "private fun showAllContent()")
|
||||
assertSourceContains(source, "currentAllTabState?.let(::renderAllTabState)")
|
||||
assertSourceContains(source, "if (currentAllTabState == null && !hasSelectedAllTab)")
|
||||
assertSourceContains(source, "contentAllTabViewModel.loadInitial()")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 source는 상태별 dispatcher를 둔다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertSourceContains(source, "private fun renderAllTabState(state: MainContentAllTabUiState)")
|
||||
assertSourceContains(source, "is MainContentAllTabUiState.Content ->")
|
||||
assertSourceContains(source, "is MainContentAllTabUiState.Empty ->")
|
||||
assertSourceContains(source, "is MainContentAllTabUiState.Error ->")
|
||||
assertSourceContains(source, "is MainContentAllTabUiState.Loading ->")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 source는 비 content 상태에도 공통 control을 바인딩한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertSourceContains(source, "private fun bindAllTabControls(state: MainContentAllTabUiState)")
|
||||
assertSourceContains(source, "bindAllTabControls(state)")
|
||||
assertSourceContains(source, "binding.viewContentAllTypeTabs.root.setMenus")
|
||||
assertSourceContains(source, "binding.layoutContentAllDayFilter.visibility")
|
||||
assertSourceContains(source, "binding.tvContentAllSortLabel.setText")
|
||||
assertSourceContains(source, "binding.tvContentAllTotalCount.text")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 추천 source는 오디오와 시리즈 routing extra를 사용한다`() {
|
||||
val source = projectFile(
|
||||
@@ -207,6 +355,10 @@ class ContentMainFragmentSourceTest {
|
||||
link = link
|
||||
)
|
||||
|
||||
private fun assertSourceContains(source: String, expected: String, message: String? = null) {
|
||||
assertTrue(message ?: "Expected source to contain: $expected", source.contains(expected))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
|
||||
Reference in New Issue
Block a user