Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions feature/receiver/presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ plugins {
}

android {
testOptions.unitTests.isIncludeAndroidResources = true

namespace = "com.afternote.feature.receiver.presentation"
experimentalProperties["android.experimental.enableScreenshotTest"] = true
}
Expand Down Expand Up @@ -36,6 +38,10 @@ dependencies {
testImplementation(libs.coroutines.test)
testImplementation(libs.robolectric)

// 실패 표기가 «레이아웃을 유지하는지» 는 실제로 그려 봐야 확인된다 (#952).
testImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.test.manifest)

screenshotTestImplementation(libs.screenshot.validation.api)
screenshotTestImplementation(libs.androidx.compose.ui.tooling)
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,10 @@ fun AfternoteSection(
title = stringResource(R.string.receiver_home_afternote_section_title),
description = stringResource(R.string.receiver_home_afternote_section_desc),
countLine =
if (totalCount == null) {
unavailableCountLine()
} else {
rememberCountLine(
prefix = "${totalCount}개",
suffix = "의 애프터노트가 있습니다.",
)
},
rememberCountLine(
prefix = "${countText(totalCount)}개",
suffix = "의 애프터노트가 있습니다.",
),
buttonText = stringResource(R.string.receiver_home_afternote_section_button),
onButtonClick = onGoClick,
middleContent = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ fun rememberCountLine(
append(suffix)
}

/** 조회 실패를 정상 0건과 구분하는 중립 문구. */
/**
* 개수 자리에 넣을 문자열 — 값이 있으면 숫자, 없으면 대시 (#952).
*
* 갈래를 문장이 아니라 **개수 자리 하나로** 좁힌다. 문장을 통째로 갈아 끼우면 섹션 높이와
* 줄 구성이 바뀌고(시안 확정값은 레이아웃 유지), 호출부마다 같은 문장이 if/else 양쪽에
* 두 번 적혀 한쪽만 고쳐질 자리가 된다.
*/
@Composable
fun unavailableCountLine(): AnnotatedString = AnnotatedString(stringResource(R.string.receiver_home_section_count_unavailable))
fun countText(count: Int?): String = count?.toString() ?: stringResource(R.string.receiver_home_section_count_unavailable)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] 이름이 아주 일반적인데(countText) 패키지 레벨 public 입니다. 같은 패키지의 다른 화면에서 «개수를 문자열로» 정도의 뜻으로 끌어다 쓰면, 실제로는 «수신자 홈의 조회 실패 표기 규칙» 이 딸려 옵니다.

internal 로 좁히거나 이름에 맥락을 넣으면(countOrDash 류) 오용 여지가 줄어듭니다. 지금 범위에서 문제가 생기는 건 아니니 후속으로 보셔도 됩니다.

Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,15 @@ fun MindRecordSection(
title = stringResource(R.string.receiver_home_mindrecord_section_title),
description = stringResource(R.string.receiver_home_mindrecord_section_desc),
countLine =
if (summary == null) {
unavailableCountLine()
} else {
rememberCountLine(
prefix = "${summary.totalCount}개 ",
suffix = "마음의 기록이 있습니다.",
)
},
rememberCountLine(
prefix = "${countText(summary?.totalCount)}개 ",
suffix = "마음의 기록이 있습니다.",
),
buttonText = stringResource(R.string.receiver_home_mindrecord_section_button),
onButtonClick = onGoClick,
middleContent = {
if (summary == null) return@HomeSectionCard
// 조회 실패에도 카드를 숨기지 않는다 — 섹션 레이아웃 유지가 시안 확정값이고,
// 통째로 사라지면 «기능이 없는 것» 과 «못 불러온 것» 이 구분되지 않는다 (#952).
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
Expand All @@ -52,14 +49,14 @@ fun MindRecordSection(
iconResId = CoreUiR.drawable.core_ui_ic_mindrecord,
label = stringResource(R.string.receiver_home_mindrecord_daily_question),
totalLabel = totalLabel,
count = summary.dailyQuestionCount,
count = summary?.dailyQuestionCount,
modifier = Modifier.weight(1f),
)
MindRecordStatCard(
iconResId = CoreUiR.drawable.core_ui_ic_diary,
label = stringResource(R.string.receiver_home_mindrecord_diary),
totalLabel = totalLabel,
count = summary.diaryCount,
count = summary?.diaryCount,
modifier = Modifier.weight(1f),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.afternote.core.ui.theme.AfternoteDesign
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.feature.receiver.presentation.R
import com.afternote.core.ui.R as CoreUiR

/**
Expand All @@ -35,7 +37,8 @@ fun MindRecordStatCard(
iconResId: Int,
label: String,
totalLabel: String,
count: Int,
/** 조회 실패·미상이면 null — 카드를 숨기지 않고 개수 자리만 대시로 둔다 (#952). */
count: Int?,
modifier: Modifier = Modifier,
) {
Column(
Expand Down Expand Up @@ -81,7 +84,7 @@ fun MindRecordStatCard(
)
Spacer(modifier = Modifier.height(2.dp))
Text(
text = count.toString(),
text = count?.toString() ?: stringResource(R.string.receiver_home_section_count_unavailable),
style = AfternoteDesign.typography.h3,
color = AfternoteDesign.colors.gray9,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,10 @@ fun TimeLetterSection(
title = stringResource(R.string.receiver_home_timeletter_section_title),
description = stringResource(R.string.receiver_home_timeletter_section_desc),
countLine =
if (totalCount == null) {
unavailableCountLine()
} else {
rememberCountLine(
prefix = "${totalCount}개 ",
suffix = "라이프 이벤트 레터가 있습니다.",
)
},
rememberCountLine(
prefix = "${countText(totalCount)}개 ",
suffix = "라이프 이벤트 레터가 있습니다.",
),
buttonText = stringResource(R.string.receiver_home_timeletter_section_button),
onButtonClick = onGoClick,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
<string name="receiver_home_afternote_section_title">애프터노트</string>
<string name="receiver_home_afternote_section_desc">고인이 사후 정리하고자 하는 데이터입니다.</string>
<string name="receiver_home_afternote_section_button">애프터노트 확인하러 가기</string>
<string name="receiver_home_section_count_unavailable">기록 수를 불러오지 못했습니다.</string>
<!--
조회 실패·미상일 때 개수 자리에 넣는 값. 문장을 통째로 갈아 끼우지 않고 숫자 자리만
바꾼다 — 섹션 레이아웃이 유지돼야 한다는 것이 시안 확정값이다 (#952, 4309:19394).
en dash(–) 다. hyphen 과 다르니 바꾸지 말 것.
-->
<string name="receiver_home_section_count_unavailable">–</string>
<string name="receiver_home_download_all_button">모든 기록 내려받기</string>
<string name="receiver_home_download_all_dialog_message">모든 기록을 내려받으시겠습니까?</string>
<string name="receiver_home_download_all_failed">모든 기록 내려받기에 실패했습니다.</string>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.afternote.feature.receiver.presentation.home.component

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.feature.receiver.presentation.home.model.MindRecordSummary
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

/**
* 기록 수 조회 실패 표기 (#952, 시안 4309:19394).
*
* 확정값은 «개수 자리만 대시, 레이아웃은 유지» 다. 종전에는 문장을 통째로 "기록 수를
* 불러오지 못했습니다." 로 갈아 끼우고 통계 카드 행을 숨겨, 실패한 화면과 정상 화면의
* 구성이 달랐다 — 카드가 통째로 사라지면 «기능이 없는 것» 과 «못 불러온 것» 도
* 구분되지 않는다.
*/
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [35])
class ReceiverHomeCountFallbackTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()

@Test
fun `조회에 실패해도 문장 구조가 그대로다`() {
composeRule.setContent {
AfternoteTheme { MindRecordSection(summary = null, onGoClick = {}) }
}

// 종전 문구가 아니라, 개수 자리만 바뀐 같은 문장이어야 한다.
composeRule.onNodeWithText("–개 마음의 기록이 있습니다.").assertIsDisplayed()
}

@Test
fun `조회에 실패해도 통계 카드가 남는다`() {
composeRule.setContent {
AfternoteTheme { MindRecordSection(summary = null, onGoClick = {}) }
}

composeRule.onNodeWithText("데일리 질문").assertIsDisplayed()
composeRule.onNodeWithText("일기").assertIsDisplayed()
// 두 카드의 개수 자리가 «모두» 대시다 — 인덱스 하나만 보면 한 장만 대시로 남는
// 회귀를 놓치고, 노드 순서가 바뀌면 조용히 다른 것을 본다.
composeRule.onAllNodesWithText("–").assertCountEquals(2)
}

@Test
fun `정상 조회는 숫자를 그대로 보여준다`() {
composeRule.setContent {
AfternoteTheme {
MindRecordSection(
summary = MindRecordSummary(dailyQuestionCount = 18, diaryCount = 4),
onGoClick = {},
)
}
}

composeRule.onNodeWithText("22개 마음의 기록이 있습니다.").assertIsDisplayed()
composeRule.onNodeWithText("18").assertIsDisplayed()
composeRule.onNodeWithText("4").assertIsDisplayed()
}
}
Loading