feat(creator): 채널 홈 탭 전환을 연결한다
This commit is contained in:
@@ -35,17 +35,12 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("fun newIntent(context: Context, creatorId: Long): Intent"))
|
||||
assertTrue(source.contains("Intent(context, CreatorChannelActivity::class.java)"))
|
||||
assertTrue(source.contains("putExtra(EXTRA_CREATOR_ID, creatorId)"))
|
||||
assertTrue(source.contains("private val viewModel: CreatorChannelHomeViewModel by viewModel()"))
|
||||
assertTrue(source.contains("if (creatorId <= 0L)"))
|
||||
assertTrue(source.contains("finish()"))
|
||||
assertTrue(source.contains("viewModel.loadHome(creatorId)"))
|
||||
assertTrue(source.contains("viewModel.homeStateLiveData.observe(this)"))
|
||||
assertTrue(source.contains("viewModel.chatRoomIdLiveData.observe(this)"))
|
||||
assertFalse(source.contains("is CreatorChannelHomeUiState.Error -> showToast"))
|
||||
assertTrue(source.contains("event.consume()?.let"))
|
||||
assertTrue(source.contains("ChatRoomActivity.newIntent(this, chatRoomId)"))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByCreatorId(this, creatorId)"))
|
||||
assertTrue(source.contains("viewModel.createChatRoom(characterId)"))
|
||||
assertTrue(source.contains("homeActionDelegate?.createChatRoom(characterId)"))
|
||||
assertTrue(source.contains("updateActionButtonLayout"))
|
||||
assertTrue(source.contains("marginStart = if (isChatVisible && isDmVisible)"))
|
||||
}
|
||||
@@ -61,32 +56,90 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("binding.ivBell.setOnClickListener"))
|
||||
assertTrue(source.contains("private fun onFollowActionClicked"))
|
||||
assertTrue(source.contains("if (!header.isFollow)"))
|
||||
assertTrue(source.contains("viewModel.follow(follow = true, notify = true)"))
|
||||
assertTrue(source.contains("homeActionDelegate?.follow(follow = true, notify = true)"))
|
||||
assertTrue(source.contains("showFollowNotifyFragment"))
|
||||
assertTrue(source.contains("onClickNotifyAll = { viewModel.follow(follow = true, notify = true) }"))
|
||||
assertTrue(source.contains("onClickNotifyNone = { viewModel.follow(follow = true, notify = false) }"))
|
||||
assertTrue(source.contains("onClickUnFollow = { viewModel.follow(follow = false, notify = false) }"))
|
||||
assertTrue(source.contains("viewModel.isFollowInProgressLiveData.observe(this)"))
|
||||
assertTrue(source.contains("onClickNotifyAll = { homeActionDelegate?.follow(follow = true, notify = true) }"))
|
||||
assertTrue(source.contains("onClickNotifyNone = { homeActionDelegate?.follow(follow = true, notify = false) }"))
|
||||
assertTrue(source.contains("onClickUnFollow = { homeActionDelegate?.follow(follow = false, notify = false) }"))
|
||||
assertTrue(source.contains("onCreatorChannelFollowProgressChanged"))
|
||||
assertTrue(source.contains("binding.layoutFollowCapsule.isEnabled = titleBarState.isActionEnabled"))
|
||||
assertTrue(source.contains("binding.ivBell.isEnabled = titleBarState.isActionEnabled"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `layout source는 HorizontalScrollView 기반 7개 탭 컨테이너와 RecyclerView를 가진다`() {
|
||||
fun `Phase 10 컨테이너 layout은 TabLayout과 ViewPager2를 가진다`() {
|
||||
val layout = projectFile("app/src/main/res/layout/activity_creator_channel.xml").readText()
|
||||
|
||||
assertTrue(layout.contains("<com.google.android.material.tabs.TabLayout"))
|
||||
assertTrue(layout.contains("@+id/tab_layout"))
|
||||
assertTrue(layout.contains("<androidx.viewpager2.widget.ViewPager2"))
|
||||
assertTrue(layout.contains("@+id/view_pager"))
|
||||
assertFalse(layout.contains("<HorizontalScrollView"))
|
||||
assertFalse(layout.contains("@+id/horizontal_tab_scroll_view"))
|
||||
assertFalse(layout.contains("@+id/tab_container"))
|
||||
assertFalse(layout.contains("@+id/rv_home_sections"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Phase 10 Activity는 pager adapter를 사용하고 홈 탭 구현을 Fragment로 위임한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(layout.contains("<HorizontalScrollView"))
|
||||
assertTrue(layout.contains("@+id/horizontal_tab_scroll_view"))
|
||||
assertTrue(layout.contains("android:elevation=\"1dp\""))
|
||||
assertTrue(layout.contains("@+id/tab_container"))
|
||||
assertTrue(source.contains("CreatorChannelPagerAdapter"))
|
||||
assertTrue(source.contains("binding.viewPager.adapter"))
|
||||
assertTrue(source.contains("CreatorChannelHomeFragment"))
|
||||
assertFalse(source.contains("private val viewModel: CreatorChannelHomeViewModel by viewModel()"))
|
||||
assertFalse(source.contains("CreatorChannelHomeSectionAdapter"))
|
||||
assertFalse(source.contains("sectionAdapter"))
|
||||
assertFalse(source.contains("viewModel.loadHome(creatorId)"))
|
||||
assertFalse(source.contains("bindTabs(content.tabs)"))
|
||||
assertFalse(source.contains("private fun createTabView"))
|
||||
assertFalse(source.contains("private fun onTabClicked"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home Fragment source는 creatorId argument와 home ViewModel RecyclerView를 소유한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
|
||||
).readText()
|
||||
val layout = projectFile("app/src/main/res/layout/fragment_creator_channel_home.xml").readText()
|
||||
|
||||
assertTrue(source.contains("BaseFragment<FragmentCreatorChannelHomeBinding>"))
|
||||
assertTrue(source.contains("private const val ARG_CREATOR_ID"))
|
||||
assertTrue(source.contains("fun newInstance(creatorId: Long): CreatorChannelHomeFragment"))
|
||||
assertTrue(source.contains("arguments = Bundle().apply"))
|
||||
assertTrue(source.contains("putLong(ARG_CREATOR_ID, creatorId)"))
|
||||
assertTrue(source.contains("private val viewModel: CreatorChannelHomeViewModel by viewModel()"))
|
||||
assertTrue(source.contains("CreatorChannelHomeSectionAdapter(::onScheduleClicked, ::onAudioContentClicked)"))
|
||||
assertTrue(source.contains("binding.rvHomeSections.layoutManager = LinearLayoutManager(requireContext())"))
|
||||
assertTrue(source.contains("binding.rvHomeSections.adapter = sectionAdapter"))
|
||||
assertTrue(source.contains("viewModel.homeStateLiveData.observe(viewLifecycleOwner)"))
|
||||
assertTrue(source.contains("viewModel.chatRoomIdLiveData.observe(viewLifecycleOwner)"))
|
||||
assertTrue(source.contains("viewModel.toastLiveData.observe(viewLifecycleOwner)"))
|
||||
assertTrue(source.contains("viewModel.isFollowInProgressLiveData.observe(viewLifecycleOwner)"))
|
||||
assertTrue(source.contains("host.onCreatorChannelHomeContentChanged()"))
|
||||
assertTrue(source.contains("binding.rvHomeSections.adapter = null"))
|
||||
assertTrue(source.contains("if (creatorId > 0L)"))
|
||||
assertTrue(source.contains("viewModel.loadHome(creatorId)"))
|
||||
assertTrue(layout.contains("@+id/rv_home_sections"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `layout source는 Activity 컨테이너와 홈 Fragment RecyclerView를 분리한다`() {
|
||||
val layout = projectFile("app/src/main/res/layout/activity_creator_channel.xml").readText()
|
||||
val fragmentLayout = projectFile("app/src/main/res/layout/fragment_creator_channel_home.xml").readText()
|
||||
|
||||
assertTrue(layout.contains("@+id/tab_layout"))
|
||||
assertTrue(layout.contains("@+id/view_pager"))
|
||||
assertTrue(layout.contains("android:layout_height=\"1dp\""))
|
||||
assertFalse(layout.contains("@+id/horizontal_tab_scroll_view"))
|
||||
assertFalse(layout.contains("@+id/tab_container"))
|
||||
assertFalse(layout.contains("@+id/rv_home_sections"))
|
||||
assertTrue(fragmentLayout.contains("@+id/rv_home_sections"))
|
||||
assertTrue(fragmentLayout.contains("tools:listitem=\"@layout/item_creator_channel_home_audio\""))
|
||||
assertTrue(layout.contains("android:drawableStart=\"@drawable/ic_new_talk\""))
|
||||
assertTrue(layout.contains("android:drawableStart=\"@drawable/ic_new_dm\""))
|
||||
assertFalse(layout.contains("TextTabBarView"))
|
||||
assertTrue(source.contains("getString(tab.labelResId)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +174,7 @@ class CreatorChannelActivitySourceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `scroll source는 tab sticky와 title bar black 전환을 연결한다`() {
|
||||
fun `scroll source는 tab sticky와 title bar black 전환을 유지한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
@@ -129,7 +182,8 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("setupScrollListener"))
|
||||
assertTrue(source.contains("binding.nestedScrollView.setOnScrollChangeListener"))
|
||||
assertTrue(source.contains("CreatorChannelScrollState.calculateStickyTop"))
|
||||
assertTrue(source.contains("binding.horizontalTabScrollView.translationY"))
|
||||
assertTrue(source.contains("binding.tabLayout.translationY = tabTranslationY.toFloat()"))
|
||||
assertTrue(source.contains("val tabBarTop = headerHeight - scrollY + tabTranslationY"))
|
||||
assertTrue(source.contains("CreatorChannelScrollState.shouldUseBlackTitleBar"))
|
||||
assertTrue(source.contains("binding.titleBarContainer.setBackgroundColor"))
|
||||
assertTrue(source.contains("Color.BLACK"))
|
||||
@@ -147,29 +201,67 @@ class CreatorChannelActivitySourceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tab source는 Figma 기준 selected indicator와 16sp 고정 폭 탭을 사용한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
fun `pager adapter source는 7개 탭 순서와 홈 placeholder Fragment를 연결한다`() {
|
||||
val adapter = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt"
|
||||
).readText()
|
||||
val placeholder = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPlaceholderFragment.kt"
|
||||
).readText()
|
||||
val placeholderLayout = projectFile(
|
||||
"app/src/main/res/layout/fragment_creator_channel_placeholder.xml"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("createTabView(tab, isSelected = tab == selectedTab)"))
|
||||
assertTrue(source.contains("tabText.textSize = 16f"))
|
||||
assertTrue(source.contains("width = 110.dpToPx().toInt()"))
|
||||
assertTrue(source.contains("indicator.setBackgroundColor(getColor(R.color.soda_400))"))
|
||||
assertTrue(source.contains("indicator.isVisible = isSelected"))
|
||||
assertTrue(adapter.contains("class CreatorChannelPagerAdapter"))
|
||||
assertTrue(adapter.contains("FragmentStateAdapter"))
|
||||
assertTrue(adapter.contains("private val tabs: List<CreatorChannelTab> = CreatorChannelTab.entries"))
|
||||
assertTrue(adapter.contains("override fun getItemCount(): Int = tabs.size"))
|
||||
assertTrue(adapter.contains("CreatorChannelTab.Home -> CreatorChannelHomeFragment.newInstance(creatorId)"))
|
||||
assertTrue(adapter.contains("else -> CreatorChannelPlaceholderFragment.newInstance(tab)"))
|
||||
assertTrue(placeholder.contains("private const val ARG_TAB_NAME"))
|
||||
assertTrue(placeholder.contains("fun newInstance(tab: CreatorChannelTab): CreatorChannelPlaceholderFragment"))
|
||||
assertTrue(placeholderLayout.contains("@+id/tv_placeholder"))
|
||||
assertTrue(placeholderLayout.contains("android:layout_height=\"160dp\""))
|
||||
assertFalse(placeholderLayout.contains("android:visibility=\"gone\""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tab source는 홈 기본 선택과 홈 외 탭 no op 정책을 명시한다`() {
|
||||
fun `tab source는 TabLayoutMediator와 ViewPager2를 연결한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("private var selectedTab: CreatorChannelTab = CreatorChannelTab.Home"))
|
||||
assertTrue(source.contains("createTabView(tab, isSelected = tab == selectedTab)"))
|
||||
assertTrue(source.contains("setOnClickListener { onTabClicked(tab) }"))
|
||||
assertTrue(source.contains("private fun onTabClicked(tab: CreatorChannelTab)"))
|
||||
assertTrue(source.contains("if (tab != CreatorChannelTab.Home) return"))
|
||||
assertTrue(source.contains("TabLayoutMediator"))
|
||||
assertTrue(source.contains("private var tabLayoutMediator: TabLayoutMediator? = null"))
|
||||
assertTrue(source.contains("private var pageChangeCallback: ViewPager2.OnPageChangeCallback? = null"))
|
||||
assertTrue(source.contains("binding.viewPager.adapter = CreatorChannelPagerAdapter(this, creatorId)"))
|
||||
assertTrue(source.contains("tabLayoutMediator = TabLayoutMediator(binding.tabLayout, binding.viewPager)"))
|
||||
assertTrue(source.contains("tab.text = getString(CreatorChannelTab.entries[position].labelResId)"))
|
||||
assertTrue(source.contains(".attach()"))
|
||||
assertTrue(source.contains("binding.viewPager.isUserInputEnabled = true"))
|
||||
assertTrue(source.contains("binding.viewPager.offscreenPageLimit = CreatorChannelTab.entries.size - 1"))
|
||||
assertTrue(source.contains("binding.viewPager.registerOnPageChangeCallback(callback)"))
|
||||
assertTrue(source.contains("override fun onDestroy()"))
|
||||
assertTrue(source.contains("tabLayoutMediator?.detach()"))
|
||||
assertTrue(source.contains("binding.viewPager.unregisterOnPageChangeCallback(callback)"))
|
||||
assertTrue(source.contains("private fun updateViewPagerHeight()"))
|
||||
assertTrue(source.contains("findViewByPosition(binding.viewPager.currentItem)"))
|
||||
assertTrue(source.contains("currentPage.measure(widthSpec, heightSpec)"))
|
||||
assertTrue(source.contains("binding.viewPager.updateLayoutParams"))
|
||||
assertFalse(source.contains("binding.tabLayout.addTab"))
|
||||
assertFalse(source.contains("private fun createTabView"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tab source는 기존 custom tab no op 정책을 제거한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertFalse(source.contains("private var selectedTab: CreatorChannelTab = CreatorChannelTab.Home"))
|
||||
assertFalse(source.contains("setOnClickListener { onTabClicked(tab) }"))
|
||||
assertFalse(source.contains("private fun onTabClicked(tab: CreatorChannelTab)"))
|
||||
assertFalse(source.contains("if (tab != CreatorChannelTab.Home) return"))
|
||||
assertFalse(source.contains("CreatorChannelTab.Live ->"))
|
||||
assertFalse(source.contains("CreatorChannelTab.Audio ->"))
|
||||
assertFalse(source.contains("CreatorChannelTab.Series ->"))
|
||||
@@ -589,8 +681,13 @@ class CreatorChannelActivitySourceTest {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val fragment = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("CreatorChannelHomeSectionAdapter(::onScheduleClicked"))
|
||||
assertTrue(fragment.contains("CreatorChannelHomeSectionAdapter(::onScheduleClicked"))
|
||||
assertTrue(fragment.contains("private fun onScheduleClicked(schedule: CreatorChannelScheduleResponse)"))
|
||||
assertTrue(fragment.contains("host.onCreatorChannelScheduleClicked(schedule)"))
|
||||
assertTrue(source.contains("private fun onScheduleClicked(schedule: CreatorChannelScheduleResponse)"))
|
||||
assertTrue(source.contains("CreatorActivityType.Audio"))
|
||||
assertTrue(source.contains("CreatorActivityType.LiveReplay"))
|
||||
@@ -663,8 +760,13 @@ class CreatorChannelActivitySourceTest {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val fragment = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("CreatorChannelHomeSectionAdapter(::onScheduleClicked, ::onAudioContentClicked)"))
|
||||
assertTrue(fragment.contains("CreatorChannelHomeSectionAdapter(::onScheduleClicked, ::onAudioContentClicked)"))
|
||||
assertTrue(fragment.contains("private fun onAudioContentClicked(audioContent: CreatorChannelAudioContentResponse)"))
|
||||
assertTrue(fragment.contains("host.onCreatorChannelAudioContentClicked(audioContent)"))
|
||||
assertTrue(source.contains("private fun onAudioContentClicked(audioContent: CreatorChannelAudioContentResponse)"))
|
||||
assertTrue(source.contains("AudioContentDetailActivity::class.java"))
|
||||
assertTrue(source.contains("putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContent.audioContentId)"))
|
||||
|
||||
Reference in New Issue
Block a user