test(home): 팔로잉 최근 소식 nested payload 검증을 추가한다
This commit is contained in:
@@ -153,7 +153,7 @@ class HomeFollowingFragmentSourceTest {
|
|||||||
)
|
)
|
||||||
assertFalse(fragment.contains("binding.viewHomeFollowingRecentNewsTitle.ivSectionTitleChevron"))
|
assertFalse(fragment.contains("binding.viewHomeFollowingRecentNewsTitle.ivSectionTitleChevron"))
|
||||||
assertTrue(recentNewsSection.contains("view_home_following_recent_news_title"))
|
assertTrue(recentNewsSection.contains("view_home_following_recent_news_title"))
|
||||||
assertTrue(adapter.contains("FollowingNewsType.COMMUNITY_POST -> VIEW_TYPE_COMMUNITY"))
|
assertTrue(adapter.contains("is HomeFollowingNewsUiItem.Community -> VIEW_TYPE_COMMUNITY"))
|
||||||
assertTrue(adapter.contains("is HomeFollowingNewsUiItem.Ranking -> VIEW_TYPE_RANKING"))
|
assertTrue(adapter.contains("is HomeFollowingNewsUiItem.Ranking -> VIEW_TYPE_RANKING"))
|
||||||
assertTrue(adapter.contains("R.layout.view_feed_community"))
|
assertTrue(adapter.contains("R.layout.view_feed_community"))
|
||||||
assertTrue(adapter.contains("R.layout.view_feed_rank"))
|
assertTrue(adapter.contains("R.layout.view_feed_rank"))
|
||||||
@@ -174,7 +174,7 @@ class HomeFollowingFragmentSourceTest {
|
|||||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt"
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt"
|
||||||
).readText()
|
).readText()
|
||||||
|
|
||||||
assertTrue(adapter.contains("ranking.creatorNickname"))
|
assertTrue(adapter.contains("creatorNickname"))
|
||||||
assertTrue(adapter.contains("ranking.rank"))
|
assertTrue(adapter.contains("ranking.rank"))
|
||||||
assertTrue(adapter.contains("R.string.screen_home_following_creator_ranking_news_message"))
|
assertTrue(adapter.contains("R.string.screen_home_following_creator_ranking_news_message"))
|
||||||
assertTrue(adapter.contains("context.getString("))
|
assertTrue(adapter.contains("context.getString("))
|
||||||
@@ -186,6 +186,59 @@ class HomeFollowingFragmentSourceTest {
|
|||||||
assertFalse(adapter.contains("highlightRanges = emptyList()"))
|
assertFalse(adapter.contains("highlightRanges = emptyList()"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following recent news models use nested payload response contract`() {
|
||||||
|
val models = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt"
|
||||||
|
).readText()
|
||||||
|
val newsResponse = models.substringAfter("data class FollowingNewsResponse(")
|
||||||
|
.substringBefore(")\n\n@Keep")
|
||||||
|
|
||||||
|
assertTrue(newsResponse.contains("newsId"))
|
||||||
|
assertTrue(newsResponse.contains("type"))
|
||||||
|
assertTrue(newsResponse.contains("visibleFromAtUtc"))
|
||||||
|
assertTrue(newsResponse.contains("creatorRanking"))
|
||||||
|
assertTrue(newsResponse.contains("audioContent"))
|
||||||
|
assertTrue(newsResponse.contains("photoContent"))
|
||||||
|
assertTrue(newsResponse.contains("contentRanking"))
|
||||||
|
assertTrue(newsResponse.contains("communityPost"))
|
||||||
|
assertFalse(newsResponse.contains("creatorProfileImageUrl"))
|
||||||
|
assertFalse(newsResponse.contains("creatorNickname"))
|
||||||
|
assertFalse(newsResponse.contains("title"))
|
||||||
|
assertFalse(newsResponse.contains("body"))
|
||||||
|
assertFalse(newsResponse.contains("thumbnailImageUrl"))
|
||||||
|
assertFalse(newsResponse.contains("targetId"))
|
||||||
|
assertFalse(newsResponse.contains("occurredAtUtc"))
|
||||||
|
assertFalse(newsResponse.contains("rank"))
|
||||||
|
assertTrue(models.contains("data class FollowingCreatorRankingNewsResponse"))
|
||||||
|
assertTrue(models.contains("data class FollowingContentNewsResponse"))
|
||||||
|
assertTrue(models.contains("data class FollowingContentRankingNewsResponse"))
|
||||||
|
assertTrue(models.contains("data class FollowingCommunityPostNewsResponse"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following recent news adapter binds nested payload ui variants`() {
|
||||||
|
val adapter = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt"
|
||||||
|
).readText()
|
||||||
|
|
||||||
|
assertTrue(adapter.contains("FollowingNewsType.CONTENT_RANKING"))
|
||||||
|
assertTrue(adapter.contains("R.string.screen_home_following_content_ranking_news_message"))
|
||||||
|
assertTrue(adapter.contains("ranking.imageUrl"))
|
||||||
|
assertTrue(adapter.contains("community.postId.toString()"))
|
||||||
|
assertTrue(adapter.contains("community.likeCount"))
|
||||||
|
assertTrue(adapter.contains("community.commentCount"))
|
||||||
|
assertTrue(adapter.contains("community.content"))
|
||||||
|
assertTrue(adapter.contains("community.imageUrl"))
|
||||||
|
assertTrue(adapter.contains("content.contentId.toString()"))
|
||||||
|
assertTrue(adapter.contains("content.contentImageUrl"))
|
||||||
|
assertTrue(adapter.contains("content.creatorProfileImageUrl"))
|
||||||
|
assertFalse(adapter.contains("commentCount = 0"))
|
||||||
|
assertFalse(adapter.contains("likeCount = 0"))
|
||||||
|
assertFalse(adapter.contains("community.body.ifBlank { community.title }"))
|
||||||
|
assertFalse(adapter.contains("ranking.thumbnailImageUrl.orEmpty()"))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `following creators section has no header and uses figma simple profile size`() {
|
fun `following creators section has no header and uses figma simple profile size`() {
|
||||||
val layout = homeMainLayoutSource()
|
val layout = homeMainLayoutSource()
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import kr.co.vividnext.sodalive.R
|
|||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
||||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||||
import kr.co.vividnext.sodalive.v2.main.chat.data.ChatRoomListItemResponse
|
import kr.co.vividnext.sodalive.v2.main.chat.data.ChatRoomListItemResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingCommunityPostNewsResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingContentNewsResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingContentRankingNewsResponse
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingCreatorResponse
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingCreatorResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingCreatorRankingNewsResponse
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingLiveResponse
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingLiveResponse
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingNewsResponse
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingNewsResponse
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingNewsType
|
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingNewsType
|
||||||
@@ -14,6 +18,7 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingNewsUiItem
|
|||||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingUiState
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingUiState
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.model.toUiState
|
import kr.co.vividnext.sodalive.v2.main.home.model.toUiState
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@@ -42,7 +47,7 @@ class HomeFollowingMapperTest {
|
|||||||
onAirLives = listOf(live()),
|
onAirLives = listOf(live()),
|
||||||
recentChats = listOf(chat()),
|
recentChats = listOf(chat()),
|
||||||
monthlySchedules = listOf(schedule(scheduleId = "schedule-1")),
|
monthlySchedules = listOf(schedule(scheduleId = "schedule-1")),
|
||||||
recentNews = listOf(news(newsId = "news-1", type = FollowingNewsType.PHOTO_CONTENT))
|
recentNews = listOf(photoContentNews(newsId = "news-1"))
|
||||||
).toUiState(formatter) as HomeFollowingUiState.Content
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
|
|
||||||
assertEquals(1L, state.followingCreators.items.single().creatorId)
|
assertEquals(1L, state.followingCreators.items.single().creatorId)
|
||||||
@@ -73,7 +78,7 @@ class HomeFollowingMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `PHOTO_CONTENT는 photo content label로 매핑된다`() {
|
fun `PHOTO_CONTENT는 photo content label로 매핑된다`() {
|
||||||
val state = response(
|
val state = response(
|
||||||
recentNews = listOf(news(type = FollowingNewsType.PHOTO_CONTENT))
|
recentNews = listOf(photoContentNews())
|
||||||
).toUiState(formatter) as HomeFollowingUiState.Content
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
val item = state.recentNews.items.single() as HomeFollowingNewsUiItem.Content
|
val item = state.recentNews.items.single() as HomeFollowingNewsUiItem.Content
|
||||||
|
|
||||||
@@ -81,21 +86,96 @@ class HomeFollowingMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ranking news의 rank null 항목은 제외된다`() {
|
fun `recentNews nested payload는 type별 UI item으로 매핑된다`() {
|
||||||
val state = response(
|
val state = response(
|
||||||
recentNews = listOf(
|
recentNews = listOf(
|
||||||
news(newsId = "hidden", type = FollowingNewsType.CREATOR_RANKING, rank = null),
|
creatorRankingNews(newsId = "creator-ranking"),
|
||||||
news(newsId = "shown", type = FollowingNewsType.CONTENT_RANKING, rank = 1)
|
contentRankingNews(newsId = "content-ranking"),
|
||||||
|
audioContentNews(newsId = "audio-content"),
|
||||||
|
photoContentNews(newsId = "photo-content"),
|
||||||
|
communityPostNews(newsId = "community-post")
|
||||||
|
)
|
||||||
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
|
|
||||||
|
val creatorRanking = state.recentNews.items[0] as HomeFollowingNewsUiItem.Ranking
|
||||||
|
assertEquals(FollowingNewsType.CREATOR_RANKING, creatorRanking.type)
|
||||||
|
assertEquals(7, creatorRanking.rank)
|
||||||
|
assertEquals(1001L, creatorRanking.targetId)
|
||||||
|
assertEquals("creator nickname", creatorRanking.creatorNickname)
|
||||||
|
assertEquals("https://example.com/creator-ranking.png", creatorRanking.imageUrl)
|
||||||
|
|
||||||
|
val contentRanking = state.recentNews.items[1] as HomeFollowingNewsUiItem.Ranking
|
||||||
|
assertEquals(FollowingNewsType.CONTENT_RANKING, contentRanking.type)
|
||||||
|
assertEquals(3, contentRanking.rank)
|
||||||
|
assertEquals(2001L, contentRanking.targetId)
|
||||||
|
assertEquals("ranked content", contentRanking.title)
|
||||||
|
assertEquals("https://example.com/content-ranking.png", contentRanking.imageUrl)
|
||||||
|
|
||||||
|
val audioContent = state.recentNews.items[2] as HomeFollowingNewsUiItem.Content
|
||||||
|
assertEquals(FollowingNewsType.AUDIO_CONTENT, audioContent.type)
|
||||||
|
assertEquals(3001L, audioContent.contentId)
|
||||||
|
assertEquals("audio title", audioContent.title)
|
||||||
|
assertEquals("https://example.com/audio.png", audioContent.contentImageUrl)
|
||||||
|
assertEquals("audio creator", audioContent.creatorNickname)
|
||||||
|
|
||||||
|
val photoContent = state.recentNews.items[3] as HomeFollowingNewsUiItem.Content
|
||||||
|
assertEquals(FollowingNewsType.PHOTO_CONTENT, photoContent.type)
|
||||||
|
assertEquals(3002L, photoContent.contentId)
|
||||||
|
assertEquals(R.string.screen_home_following_photo_content, photoContent.labelResId)
|
||||||
|
|
||||||
|
val community = state.recentNews.items[4] as HomeFollowingNewsUiItem.Community
|
||||||
|
assertEquals(4001L, community.postId)
|
||||||
|
assertEquals("community creator", community.creatorNickname)
|
||||||
|
assertEquals("https://example.com/community-image.png", community.imageUrl)
|
||||||
|
assertEquals("community body", community.content)
|
||||||
|
assertEquals("2026-06-25T02:00:00Z", community.createdAt)
|
||||||
|
assertEquals(11, community.likeCount)
|
||||||
|
assertEquals(5, community.commentCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `type에 대응하는 nested payload가 null이면 recentNews에서 제외된다`() {
|
||||||
|
val state = response(
|
||||||
|
recentNews = listOf(
|
||||||
|
FollowingNewsResponse(
|
||||||
|
newsId = "missing-creator-ranking",
|
||||||
|
type = FollowingNewsType.CREATOR_RANKING,
|
||||||
|
visibleFromAtUtc = "2026-06-25T01:00:00Z",
|
||||||
|
creatorRanking = null
|
||||||
|
),
|
||||||
|
FollowingNewsResponse(
|
||||||
|
newsId = "missing-audio-content",
|
||||||
|
type = FollowingNewsType.AUDIO_CONTENT,
|
||||||
|
visibleFromAtUtc = "2026-06-25T01:00:00Z",
|
||||||
|
audioContent = null
|
||||||
|
),
|
||||||
|
contentRankingNews(newsId = "shown")
|
||||||
)
|
)
|
||||||
).toUiState(formatter) as HomeFollowingUiState.Content
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
|
|
||||||
assertEquals(listOf("shown"), state.recentNews.items.map { it.newsId })
|
assertEquals(listOf("shown"), state.recentNews.items.map { it.newsId })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `contentImageUrl이 null이어도 최근 소식 content와 ranking item은 유지된다`() {
|
||||||
|
val state = response(
|
||||||
|
recentNews = listOf(
|
||||||
|
contentRankingNews(newsId = "content-ranking", contentImageUrl = null),
|
||||||
|
audioContentNews(newsId = "audio-content", contentImageUrl = null)
|
||||||
|
)
|
||||||
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
|
|
||||||
|
val ranking = state.recentNews.items[0] as HomeFollowingNewsUiItem.Ranking
|
||||||
|
assertNull(ranking.imageUrl)
|
||||||
|
|
||||||
|
val content = state.recentNews.items[1] as HomeFollowingNewsUiItem.Content
|
||||||
|
assertNull(content.contentImageUrl)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `news 상대 시간은 visibleFromAtUtc 값을 formatter에 전달한다`() {
|
fun `news 상대 시간은 visibleFromAtUtc 값을 formatter에 전달한다`() {
|
||||||
val state = response(
|
val state = response(
|
||||||
recentNews = listOf(news(visibleFromAtUtc = "2026-06-25T00:00:00Z"))
|
recentNews = listOf(audioContentNews(visibleFromAtUtc = "2026-06-25T00:00:00Z"))
|
||||||
).toUiState(formatter) as HomeFollowingUiState.Content
|
).toUiState(formatter) as HomeFollowingUiState.Content
|
||||||
|
|
||||||
assertEquals("relative:2026-06-25T00:00:00Z", state.recentNews.items.single().visibleFromText)
|
assertEquals("relative:2026-06-25T00:00:00Z", state.recentNews.items.single().visibleFromText)
|
||||||
@@ -152,22 +232,86 @@ class HomeFollowingMapperTest {
|
|||||||
isOnAir = false
|
isOnAir = false
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun news(
|
private fun creatorRankingNews(
|
||||||
newsId: String = "news",
|
newsId: String = "news",
|
||||||
type: FollowingNewsType = FollowingNewsType.AUDIO_CONTENT,
|
visibleFromAtUtc: String = "2026-06-25T01:00:00Z"
|
||||||
visibleFromAtUtc: String = "2026-06-25T01:00:00Z",
|
|
||||||
rank: Int? = null
|
|
||||||
) = FollowingNewsResponse(
|
) = FollowingNewsResponse(
|
||||||
newsId = newsId,
|
newsId = newsId,
|
||||||
type = type,
|
type = FollowingNewsType.CREATOR_RANKING,
|
||||||
creatorProfileImageUrl = "https://example.com/news.png",
|
|
||||||
creatorNickname = "creator",
|
|
||||||
title = "title",
|
|
||||||
body = "body",
|
|
||||||
thumbnailImageUrl = null,
|
|
||||||
targetId = 6L,
|
|
||||||
occurredAtUtc = "2026-06-25T00:00:00Z",
|
|
||||||
visibleFromAtUtc = visibleFromAtUtc,
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
rank = rank
|
creatorRanking = FollowingCreatorRankingNewsResponse(
|
||||||
|
rank = 7,
|
||||||
|
creatorId = 1001L,
|
||||||
|
nickname = "creator nickname",
|
||||||
|
profileImageUrl = "https://example.com/creator-ranking.png"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun contentRankingNews(
|
||||||
|
newsId: String = "news",
|
||||||
|
visibleFromAtUtc: String = "2026-06-25T01:00:00Z",
|
||||||
|
contentImageUrl: String? = "https://example.com/content-ranking.png"
|
||||||
|
) = FollowingNewsResponse(
|
||||||
|
newsId = newsId,
|
||||||
|
type = FollowingNewsType.CONTENT_RANKING,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
|
contentRanking = FollowingContentRankingNewsResponse(
|
||||||
|
rank = 3,
|
||||||
|
contentId = 2001L,
|
||||||
|
contentImageUrl = contentImageUrl,
|
||||||
|
title = "ranked content"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun audioContentNews(
|
||||||
|
newsId: String = "news",
|
||||||
|
visibleFromAtUtc: String = "2026-06-25T01:00:00Z",
|
||||||
|
contentImageUrl: String? = "https://example.com/audio.png"
|
||||||
|
) = FollowingNewsResponse(
|
||||||
|
newsId = newsId,
|
||||||
|
type = FollowingNewsType.AUDIO_CONTENT,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
|
audioContent = FollowingContentNewsResponse(
|
||||||
|
contentId = 3001L,
|
||||||
|
contentImageUrl = contentImageUrl,
|
||||||
|
title = "audio title",
|
||||||
|
creatorProfileImageUrl = "https://example.com/audio-creator.png",
|
||||||
|
creatorNickname = "audio creator"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun photoContentNews(
|
||||||
|
newsId: String = "news",
|
||||||
|
visibleFromAtUtc: String = "2026-06-25T01:00:00Z"
|
||||||
|
) = FollowingNewsResponse(
|
||||||
|
newsId = newsId,
|
||||||
|
type = FollowingNewsType.PHOTO_CONTENT,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
|
photoContent = FollowingContentNewsResponse(
|
||||||
|
contentId = 3002L,
|
||||||
|
contentImageUrl = "https://example.com/photo.png",
|
||||||
|
title = "photo title",
|
||||||
|
creatorProfileImageUrl = "https://example.com/photo-creator.png",
|
||||||
|
creatorNickname = "photo creator"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun communityPostNews(
|
||||||
|
newsId: String = "news",
|
||||||
|
visibleFromAtUtc: String = "2026-06-25T01:00:00Z"
|
||||||
|
) = FollowingNewsResponse(
|
||||||
|
newsId = newsId,
|
||||||
|
type = FollowingNewsType.COMMUNITY_POST,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
|
communityPost = FollowingCommunityPostNewsResponse(
|
||||||
|
postId = 4001L,
|
||||||
|
creatorProfileImage = "https://example.com/community-creator.png",
|
||||||
|
creatorNickname = "community creator",
|
||||||
|
imageUrl = "https://example.com/community-image.png",
|
||||||
|
content = "community body",
|
||||||
|
createdAt = "2026-06-25T02:00:00Z",
|
||||||
|
likeCount = 11,
|
||||||
|
commentCount = 5
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user