feat(creator): 채널 홈 시리즈, 커뮤니티, 팬 Talk 섹션을 재구성한다

This commit is contained in:
2026-06-15 17:19:59 +09:00
parent c45c5a2a5c
commit 2d58a876da
25 changed files with 791 additions and 242 deletions

View File

@@ -5,6 +5,10 @@ import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelDon
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelDonationHeaderColorRes
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelNoticeCardWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelAudioItemWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelCommunityCardWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelFanTalkCardWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSeriesCardHeightDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSeriesCardWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelScheduleTimelineLineCount
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDate
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDayOfWeek
@@ -156,11 +160,11 @@ class CreatorChannelHomeActivitySourceTest {
assertTrue(adapter.contains("ll_section_items"))
assertTrue(adapter.contains("sectionItems?.addView"))
assertTrue(adapter.contains("addActivityRow"))
assertTrue(adapter.contains("createContentTile"))
assertTrue(adapter.contains("addFeedCard"))
assertFalse(adapter.contains("createContentTile"))
assertFalse(adapter.contains("addFeedCard"))
assertFalse(adapter.contains("addScheduleRow"))
assertFalse(adapter.contains("addDonationCard"))
assertTrue(adapter.contains("addCommentCard"))
assertFalse(adapter.contains("addCommentCard"))
assertTrue(adapter.contains("createSnsButton"))
assertTrue(adapter.contains("activity.debutDateUtc"))
assertTrue(adapter.contains("activity.liveDurationHours"))
@@ -382,14 +386,11 @@ class CreatorChannelHomeActivitySourceTest {
}
@Test
fun `section adapter source는 가로 시리즈와 SNS 링크와 일정 타입 label을 보존한다`() {
fun `section adapter source는 SNS 링크와 일정 타입 label을 보존한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
assertTrue(adapter.contains("HorizontalScrollView"))
assertTrue(adapter.contains("createHorizontalScrollRow"))
assertTrue(adapter.contains("createHorizontalScrollRow(row)"))
assertTrue(adapter.contains("iconResId = sns.iconResId"))
assertTrue(adapter.contains("url = sns.url"))
assertTrue(adapter.contains("schedule.type.labelResId"))
@@ -542,6 +543,217 @@ class CreatorChannelHomeActivitySourceTest {
assertTrue(source.contains("putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContent.audioContentId)"))
}
@Test
fun `시리즈 섹션은 Figma Contents series size m 카드 row로 렌더링한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val seriesLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_series.xml").readText()
val seriesItemLayout = projectFile(
"app/src/main/res/layout/item_creator_channel_home_series_content.xml"
).readText()
val seriesCardView = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSeriesCardView.kt"
).readText()
assertTrue(seriesLayout.contains("@layout/view_section_title"))
assertTrue(seriesLayout.contains("@+id/hsv_series_items"))
assertTrue(seriesLayout.contains("@+id/ll_series_items"))
assertFalse(seriesLayout.contains("@+id/ll_section_items"))
assertTrue(seriesItemLayout.contains("CreatorChannelHomeSeriesCardView"))
assertTrue(seriesItemLayout.contains("@+id/layout_series_content_card"))
assertTrue(seriesItemLayout.contains("@+id/layout_series_thumbnail"))
assertTrue(seriesItemLayout.contains("@+id/iv_series_thumbnail"))
assertTrue(seriesItemLayout.contains("@+id/layout_series_original_tag"))
assertTrue(seriesItemLayout.contains("@+id/iv_series_original_icon"))
assertTrue(seriesItemLayout.contains("@+id/tv_series_original_text"))
assertTrue(seriesItemLayout.contains("android:text=\"Only\""))
assertTrue(seriesItemLayout.contains("@font/phosphate_solid"))
assertTrue(seriesItemLayout.contains("android:layout_width=\"70dp\""))
assertTrue(seriesItemLayout.contains("@drawable/bg_series_original_tag"))
assertTrue(seriesItemLayout.contains("@drawable/ic_series_original"))
assertTrue(seriesItemLayout.contains("android:layout_width=\"163dp\""))
assertTrue(seriesItemLayout.contains("android:layout_height=\"230dp\""))
assertTrue(seriesItemLayout.contains("android:layout_width=\"14dp\""))
assertFalse(seriesItemLayout.contains("android:clipToOutline"))
assertTrue(seriesCardView.contains("clipToOutline = true"))
assertTrue(seriesCardView.contains("ViewOutlineProvider"))
assertTrue(seriesCardView.contains("outline.setRoundRect"))
assertTrue(seriesCardView.contains("R.dimen.radius_14"))
assertTrue(seriesCardView.contains("originalTag.isVisible = series.isOriginal"))
assertTrue(seriesCardView.contains("thumbnail.loadUrl(series.coverImageUrl)"))
assertTrue(seriesCardView.contains("fun setThumbnailSize(widthDp: Int, heightDp: Int)"))
assertTrue(adapter.contains("private val seriesItems: LinearLayout?"))
assertTrue(adapter.contains("val visibleSeries = item.series.take(MAX_SERIES_ITEM_COUNT)"))
assertTrue(adapter.contains("R.layout.item_creator_channel_home_series_content"))
assertTrue(adapter.contains("(row as CreatorChannelHomeSeriesCardView).apply"))
assertTrue(adapter.contains("bind(series)"))
assertTrue(adapter.contains("setThumbnailSize(seriesWidthDp, calculateCreatorChannelSeriesCardHeightDp(seriesWidthDp))"))
assertTrue(adapter.contains("row.setOnClickListener { onSeriesClick(series) }"))
assertTrue(adapter.contains("calculateCreatorChannelSeriesCardWidthDp"))
assertTrue(adapter.contains("private const val MAX_SERIES_ITEM_COUNT = 10"))
assertFalse(adapter.contains("createContentTile("))
assertFalse(adapter.contains("creator_channel_series_summary"))
}
@Test
fun `시리즈 DTO와 bind source는 현재 백엔드 필드만 화면에 사용한다`() {
val model = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.kt"
).readText()
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val seriesCardView = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSeriesCardView.kt"
).readText()
assertTrue(model.contains("@SerializedName(\"seriesId\") val seriesId: Long"))
assertTrue(model.contains("@SerializedName(\"coverImageUrl\") val coverImageUrl: String"))
assertTrue(model.contains("@SerializedName(\"isOriginal\") val isOriginal: Boolean"))
assertFalse(model.contains("publishedDaysOfWeek"))
assertFalse(model.contains("isPopular"))
assertFalse(model.contains("isComplete"))
assertTrue(adapter.contains("onSeriesClick(series)"))
assertTrue(seriesCardView.contains("series.coverImageUrl"))
assertTrue(seriesCardView.contains("series.isOriginal"))
assertFalse(seriesCardView.contains("series.numberOfContent"))
assertFalse(seriesCardView.contains("series.isNew"))
}
@Test
fun `시리즈 카드 width는 402dp 기준 최대 163dp이고 작은 화면에서는 비율 축소한다`() {
assertEquals(163, calculateCreatorChannelSeriesCardWidthDp(402))
assertEquals(163, calculateCreatorChannelSeriesCardWidthDp(430))
assertEquals(146, calculateCreatorChannelSeriesCardWidthDp(360))
assertEquals(230, calculateCreatorChannelSeriesCardHeightDp(163))
assertEquals(206, calculateCreatorChannelSeriesCardHeightDp(146))
}
@Test
fun `커뮤니티 섹션은 Figma feed card 3개와 전체보기 capsule로 렌더링한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val communityLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_community.xml").readText()
val communityViewLayout = projectFile("app/src/main/res/layout/view_feed_community.xml").readText()
val feedCommunityView = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt"
).readText()
assertTrue(communityLayout.contains("@layout/view_section_title"))
assertTrue(communityLayout.contains("@+id/ll_community_items"))
assertTrue(communityLayout.contains("@+id/layout_community_more_button"))
assertTrue(communityLayout.contains("@string/view_all"))
assertFalse(communityLayout.contains("@+id/ll_section_items"))
assertTrue(communityViewLayout.contains("FeedCommunityView"))
assertTrue(communityViewLayout.contains("android:layout_width=\"match_parent\""))
assertTrue(communityViewLayout.contains("@+id/fl_feed_community_image_container"))
assertTrue(communityViewLayout.contains("@+id/iv_feed_community_image"))
assertTrue(communityViewLayout.contains("@+id/ll_feed_community_paid_overlay"))
assertTrue(communityViewLayout.contains("@+id/tv_feed_community_price"))
assertTrue(communityViewLayout.contains("@drawable/bg_creator_channel_community_price"))
assertTrue(communityViewLayout.contains("@drawable/ic_bar_cash"))
assertFalse(communityViewLayout.contains("android:drawableStart=\"@drawable/ic_bar_cash\""))
assertTrue(communityViewLayout.contains("android:layout_marginStart=\"@dimen/spacing_6\""))
assertFalse(communityViewLayout.contains("android:clipToOutline"))
assertFalse(communityViewLayout.contains("android:layout_height=\"0dp\""))
assertTrue(feedCommunityView.contains("clipToOutline = true"))
assertTrue(feedCommunityView.contains("ViewOutlineProvider"))
assertTrue(feedCommunityView.contains("outline.setRoundRect"))
assertTrue(adapter.contains("private val communityItems: LinearLayout?"))
assertTrue(adapter.contains("val visibleCommunities = item.communities.take(MAX_COMMUNITY_ITEM_COUNT)"))
assertTrue(adapter.contains("R.layout.view_feed_community"))
assertTrue(adapter.contains("communityItems?.addView(row)"))
assertTrue(adapter.contains("(row as FeedCommunityView).apply"))
assertTrue(adapter.contains("setFeedSize("))
assertTrue(adapter.contains("FeedSize("))
assertTrue(adapter.contains("val communityWidthDp = calculateCreatorChannelCommunityCardWidthDp("))
assertTrue(adapter.contains("rootWidthDp = communityWidthDp"))
assertTrue(adapter.contains("setHideEmptyTextRows(true)"))
assertTrue(adapter.contains("bind(community.toFeedCommunityItem())"))
assertTrue(adapter.contains("bindCommunityImages(row, community)"))
assertTrue(adapter.contains("BlurTransformation(itemView.context, 25f, 2.5f)"))
assertTrue(adapter.contains("row.layoutParams = LinearLayout.LayoutParams("))
assertTrue(adapter.contains("communityWidthDp.dp()"))
assertTrue(adapter.contains("LinearLayout.LayoutParams.WRAP_CONTENT"))
assertTrue(adapter.contains("communityImageUrl = community.imageUrl.takeIf { !it.isNullOrBlank() }"))
assertTrue(adapter.contains("communityImageView().setImageDrawable(null)"))
assertTrue(adapter.contains("private const val MAX_COMMUNITY_ITEM_COUNT = 3"))
assertFalse(adapter.contains("CreatorChannelCommunityCardView"))
assertFalse(adapter.contains("CreatorChannelCommunityThumbnailView"))
assertFalse(adapter.contains("private fun addFeedCard"))
assertFalse(adapter.contains("addFeedCard("))
}
@Test
fun `커뮤니티 컴포넌트 width는 402dp 기준 최대 374dp이고 작은 화면에서는 비율 축소한다`() {
assertEquals(374, calculateCreatorChannelCommunityCardWidthDp(402))
assertEquals(374, calculateCreatorChannelCommunityCardWidthDp(430))
assertEquals(335, calculateCreatorChannelCommunityCardWidthDp(360))
}
@Test
fun `팬Talk 섹션은 Figma ListComment layout과 bind로 렌더링한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val fanTalkLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_fantalk.xml").readText()
val fanTalkCardView = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelFanTalkCardView.kt"
).readText()
assertTrue(fanTalkLayout.contains("@layout/view_section_title"))
assertTrue(fanTalkLayout.contains("CreatorChannelFanTalkCardView"))
assertTrue(fanTalkLayout.contains("@+id/layout_fantalk_card"))
assertTrue(fanTalkLayout.contains("@+id/layout_fantalk_total_row"))
assertTrue(fanTalkLayout.contains("@+id/tv_fantalk_total_label"))
assertTrue(fanTalkLayout.contains("@+id/tv_fantalk_total_count"))
assertTrue(fanTalkLayout.contains("@+id/layout_fantalk_latest_row"))
assertTrue(fanTalkLayout.contains("@+id/iv_fantalk_profile"))
assertTrue(fanTalkLayout.contains("@+id/tv_fantalk_content"))
assertTrue(fanTalkLayout.contains("@+id/layout_fantalk_empty"))
assertTrue(fanTalkLayout.contains("@+id/tv_fantalk_empty_title"))
assertTrue(fanTalkLayout.contains("@+id/layout_fantalk_support_button"))
assertTrue(fanTalkLayout.contains("@string/creator_channel_fantalk_empty_title"))
assertTrue(fanTalkLayout.contains("@string/creator_channel_fantalk_support_action"))
assertTrue(fanTalkLayout.contains("@drawable/ic_new_fantalk_plus"))
assertTrue(fanTalkLayout.contains("android:layout_width=\"match_parent\""))
assertTrue(fanTalkLayout.contains("android:layout_height=\"169dp\""))
assertTrue(fanTalkLayout.contains("android:layout_marginBottom=\"@dimen/spacing_6\""))
assertTrue(fanTalkLayout.contains("@drawable/ic_chevron_down_white"))
assertFalse(fanTalkLayout.contains("@+id/ll_section_items"))
assertFalse(fanTalkLayout.contains("android:clipToOutline"))
assertTrue(fanTalkCardView.contains("clipToOutline = true"))
assertTrue(fanTalkCardView.contains("ViewOutlineProvider"))
assertTrue(fanTalkCardView.contains("outline.setRoundRect"))
assertTrue(adapter.contains("private val fanTalkTotalCount: TextView?"))
assertTrue(adapter.contains("private val fanTalkCard: View?"))
assertTrue(adapter.contains("calculateCreatorChannelFanTalkCardWidthDp"))
assertTrue(adapter.contains("private val fanTalkProfile: ImageView?"))
assertTrue(adapter.contains("private val fanTalkContent: TextView?"))
assertTrue(adapter.contains("private val fanTalkTotalRow: View?"))
assertTrue(adapter.contains("private val fanTalkLatestRow: View?"))
assertTrue(adapter.contains("private val fanTalkEmpty: View?"))
assertTrue(adapter.contains("fanTalkTotalCount?.text = item.fanTalk.totalCount.toString()"))
assertTrue(adapter.contains("fanTalkTotalRow?.isVisible = fanTalk != null"))
assertTrue(adapter.contains("fanTalkLatestRow?.isVisible = fanTalk != null"))
assertTrue(adapter.contains("fanTalkEmpty?.isVisible = fanTalk == null"))
assertTrue(adapter.contains("fanTalkContent?.text = fanTalk.content"))
assertTrue(adapter.contains("fanTalkProfile?.loadUrl(fanTalk.profileImageUrl)"))
assertTrue(adapter.contains("fanTalkContent?.text = \"\""))
assertTrue(adapter.contains("fanTalkProfile?.setImageResource(R.drawable.ic_placeholder_profile)"))
assertFalse(adapter.contains("private fun addCommentCard"))
assertFalse(adapter.contains("addCommentCard("))
}
@Test
fun `팬Talk 카드 width는 402dp 기준 최대 374dp이고 작은 화면에서는 비율 축소한다`() {
assertEquals(374, calculateCreatorChannelFanTalkCardWidthDp(402))
assertEquals(374, calculateCreatorChannelFanTalkCardWidthDp(430))
assertEquals(335, calculateCreatorChannelFanTalkCardWidthDp(360))
}
@Test
fun `SNS source는 ic_sns 아이콘을 ImageView로 표시한다`() {
val uiModel = projectFile(
@@ -606,11 +818,8 @@ class CreatorChannelHomeActivitySourceTest {
}
@Test
fun `section item layouts는 legacy generic card id를 제거하고 동적 컨테이너만 둔다`() {
fun `남은 section item layouts는 legacy generic card id를 제거하고 동적 컨테이너만 둔다`() {
val layoutNames = listOf(
"series",
"community",
"fantalk",
"introduce",
"activity",
"sns"

View File

@@ -52,7 +52,7 @@ class CreatorChannelHomeMapperTest {
}
@Test
fun `null 단건 콘텐츠와 빈 리스트와 blank SNS는 section 생성하지 않는다`() {
fun `null 단건 콘텐츠와 빈 리스트와 blank SNS는 팬Talk empty section 생성다`() {
val content = response(
currentLive = null,
latestAudioContent = null,
@@ -81,7 +81,7 @@ class CreatorChannelHomeMapperTest {
assertFalse(content.sections.any { it is CreatorChannelHomeSection.AudioContents })
assertFalse(content.sections.any { it is CreatorChannelHomeSection.Series })
assertFalse(content.sections.any { it is CreatorChannelHomeSection.Communities })
assertFalse(content.sections.any { it is CreatorChannelHomeSection.FanTalk })
assertTrue(content.sections.any { it is CreatorChannelHomeSection.FanTalk })
assertFalse(content.sections.any { it is CreatorChannelHomeSection.Introduce })
assertFalse(content.sections.any { it is CreatorChannelHomeSection.Sns })
}
@@ -239,11 +239,8 @@ class CreatorChannelHomeMapperTest {
seriesId = 1L,
title = "시리즈",
coverImageUrl = "https://example.com/series.png",
publishedDaysOfWeek = "MON",
isComplete = false,
numberOfContent = 3,
isNew = true,
isPopular = false,
isOriginal = true
)

View File

@@ -1088,28 +1088,15 @@ class HomeMainFragmentLayoutTest {
}
@Test
fun `home popular community adapter does not load original image for locked paid post`() {
val context = ApplicationProvider.getApplicationContext<Context>()
val parent = RecyclerView(context)
parent.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
val adapter = HomePopularCommunityAdapter(onClickItem = {})
val post = popularCommunityPost(1L).copy(
item = popularCommunityPost(1L).item.copy(
imageUrl = "https://example.com/paid.png",
price = 30,
existOrdered = false
),
paidStatus = HomeRecommendationPaidStatus.Paid(30)
)
adapter.submitSection(HomeRecommendationPopularCommunityPostSection(listOf(post)))
val viewHolder = adapter.onCreateViewHolder(parent, 0)
val communityImage = viewHolder.itemView.findViewById<ImageView>(R.id.iv_feed_community_image)
communityImage.setImageResource(R.drawable.ic_launcher_background)
fun `home popular community adapter applies blur when locked paid post image is loaded`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomePopularCommunityAdapter.kt"
).readText()
adapter.onBindViewHolder(viewHolder, 0)
assertEquals(null, communityImage.drawable)
assertEquals(View.VISIBLE, viewHolder.itemView.findViewById<View>(R.id.ll_feed_community_paid_overlay).visibility)
assertTrue(source.contains("imageUrl = item.item.imageUrl"))
assertTrue(source.contains("shouldBlur = item.item.price > 0 && !item.item.existOrdered"))
assertTrue(source.contains("BlurTransformation(imageView.context, 25f, 2.5f)"))
assertFalse(source.contains("item.item.imageUrl.takeIf { item.item.price <= 0 || item.item.existOrdered }"))
}
@Test
@@ -1550,6 +1537,12 @@ class HomeMainFragmentLayoutTest {
)
}
private fun projectFile(relativePath: String): java.io.File {
val candidates = listOf(java.io.File(relativePath), java.io.File("../$relativePath"))
return candidates.firstOrNull { it.exists() }
?: error("Project file not found: $relativePath")
}
private fun genreGroup(
genre: String,
creators: List<HomeRecommendationCreatorUiModel>

View File

@@ -18,8 +18,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.math.roundToInt
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [28], application = Application::class)
class FeedViewTest {
@@ -45,23 +43,31 @@ class FeedViewTest {
}
@Test
fun `community can hide empty body and keyword rows by caller policy`() {
fun `community can hide empty body row by caller policy`() {
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
view.setHideEmptyTextRows(true)
view.bind(sampleCommunityItem(bodyText = "", keywordText = ""))
assertEquals(View.GONE, view.findViewById<TextView>(R.id.tv_feed_community_body).visibility)
assertEquals(View.GONE, view.findViewById<TextView>(R.id.tv_feed_community_keyword).visibility)
}
@Test
fun `community keeps keyword visible by default for existing feed usage`() {
fun `community layout matches creator channel feed row structure`() {
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
val layout = projectFile("app/src/main/res/layout/view_feed_community.xml").readText()
view.bind(sampleCommunityItem(bodyText = "본문", keywordText = "#키워드"))
assertEquals(View.VISIBLE, view.findViewById<TextView>(R.id.tv_feed_community_keyword).visibility)
assertFalse(layout.contains("@+id/tv_feed_community_keyword"))
assertTrue(layout.contains("android:layout_width=\"match_parent\""))
assertTrue(layout.contains("android:maxLines=\"5\""))
assertTrue(layout.contains("android:layout_height=\"236dp\""))
assertTrue(layout.contains("@drawable/bg_creator_channel_community_price"))
assertTrue(layout.contains("@drawable/ic_bar_cash"))
assertFalse(layout.contains("android:drawableStart=\"@drawable/ic_bar_cash\""))
assertTrue(layout.contains("android:layout_marginStart=\"@dimen/spacing_6\""))
assertTrue(layout.contains("@color/gray_400"))
assertFalse(layout.contains("android:layout_height=\"0dp\""))
assertEquals(5, view.findViewById<TextView>(R.id.tv_feed_community_body).maxLines)
}
@Test
@@ -88,8 +94,8 @@ class FeedViewTest {
)
)
assertEquals(View.GONE, view.findViewById<TextView>(R.id.tv_feed_community_keyword).visibility)
assertEquals(View.VISIBLE, view.findViewById<FrameLayout>(R.id.fl_feed_community_image_container).visibility)
assertEquals(View.VISIBLE, view.findViewById<ImageView>(R.id.iv_feed_community_image).visibility)
assertEquals(View.VISIBLE, view.findViewById<View>(R.id.ll_feed_community_paid_overlay).visibility)
assertEquals("30", view.findViewById<TextView>(R.id.tv_feed_community_price).text.toString())
}
@@ -114,7 +120,7 @@ class FeedViewTest {
}
@Test
fun `community image container follows measured card width ratio`() {
fun `community image container keeps creator channel fixed height`() {
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
val imageContainer = view.findViewById<FrameLayout>(R.id.fl_feed_community_image_container)
@@ -124,7 +130,7 @@ class FeedViewTest {
val expectedImageWidth = 402.dpToPx() - view.paddingLeft - view.paddingRight
assertEquals(expectedImageWidth, imageContainer.measuredWidth)
assertEquals((expectedImageWidth * 236 / 346f).roundToInt(), imageContainer.measuredHeight)
assertEquals(236.dpToPx(), imageContainer.measuredHeight)
assertEquals(true, imageContainer.clipToOutline)
assertNotNull(imageContainer.outlineProvider)
}
@@ -205,6 +211,12 @@ class FeedViewTest {
private fun exactly(size: Int): Int = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY)
private fun projectFile(relativePath: String): java.io.File {
val candidates = listOf(java.io.File(relativePath), java.io.File("../$relativePath"))
return candidates.firstOrNull { it.exists() }
?: error("Project file not found: $relativePath")
}
private fun assertNotEmptyContentDescription(imageView: ImageView) {
assertTrue(!imageView.contentDescription.isNullOrEmpty())
}