Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
cec7bc3
feat(setting): FAQ 화면 및 펼침 항목 구현
koongmai Aug 22, 2026
ee9b5ea
Merge remote-tracking branch 'origin/develop' into feat/785
koongmai Sep 5, 2026
9f0e314
fix(setting): FAQ 화면을 실제 내비게이션에 연결하고 문자열 이스케이프를 고친다
koongmai Sep 5, 2026
870ec90
test(setting): FaqScreen에 상호작용·스크린샷 테스트를 추가한다
koongmai Sep 5, 2026
d25788a
test(setting): FaqScreen 스크린샷 레퍼런스 이미지를 추가한다
koongmai Sep 5, 2026
661b7f0
test(app): FAQ 네비게이션 배선을 실제 NavHost로 검증한다
koongmai Sep 5, 2026
018c76a
Merge remote-tracking branch 'origin/develop' into handoff/785-settin…
OnlyReadOneBook Sep 6, 2026
d00e114
merge(setting): 문의 기반 위에 FAQ 탐색 경로를 통합한다 (#785)
OnlyReadOneBook Sep 6, 2026
8ee0315
Merge branch 'handoff/784-setting-1hyok' into handoff/785-setting-1hyok
OnlyReadOneBook Sep 6, 2026
2ff2637
refactor(setting): adapt FAQ to local Nav3 stack (#785)
OnlyReadOneBook Sep 8, 2026
79725d2
Merge branch 'codex/784-setting-nav3' into codex/785-setting-nav3
OnlyReadOneBook Sep 8, 2026
7a521bc
test: update screenshot baselines in CI
github-actions[bot] Sep 8, 2026
6837912
Merge remote-tracking branch 'origin/handoff/784-setting-1hyok' into …
OnlyReadOneBook Sep 8, 2026
0c627bb
chore: 옛 guard check-run 을 걷어내려 HEAD 를 갱신한다 (#1983)
OnlyReadOneBook Sep 11, 2026
70916e8
Merge remote-tracking branch 'origin/handoff/784-setting-1hyok' into …
OnlyReadOneBook Sep 11, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.ui.test.hasScrollAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
Expand Down Expand Up @@ -174,6 +175,27 @@ class SettingImplementedCoverageAndroidTest {
waitForRootHost()
}

@Test
fun actualSettingNavHost_faqRowNavigatesToFaqScreenAndBackReturnsHome() {
waitForSettingHomeContent()
composeRule
.onNode(hasScrollAction())
.performScrollToNode(hasText("FAQ"))
composeRule
.onNodeWithText("FAQ")
.performClick()

waitForText("비밀번호를 잊어버렸어요.")
composeRule
.onNodeWithText("비밀번호를 잊어버렸어요.")
.assertIsDisplayed()

composeRule
.onNodeWithContentDescription("뒤로가기")
.performClick()
waitForSettingHomeContent()
}

private fun openWithdrawGuide() {
waitForSettingHomeContent()
composeRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,8 @@ internal class SettingLocalNavActions(
override fun onNavigateToInquiryWrite() {
backStack.add(SettingRoute.InquiryWriteRoute)
}

override fun onNavigateToFaq() {
backStack.add(SettingRoute.FaqRoute)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ internal interface SettingNavActions {
fun onNavigateToInquiryDetail(inquiryId: Long)

fun onNavigateToInquiryWrite()

fun onNavigateToFaq()
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.afternote.feature.setting.presentation.component.PinSetupStep
import com.afternote.feature.setting.presentation.screen.AppLockSetupScreen
import com.afternote.feature.setting.presentation.screen.ConnectedAccountsScreen
import com.afternote.feature.setting.presentation.screen.DeliveryConditionScreen
import com.afternote.feature.setting.presentation.screen.FaqScreen
import com.afternote.feature.setting.presentation.screen.InquiryDetailScreen
import com.afternote.feature.setting.presentation.screen.InquiryListScreen
import com.afternote.feature.setting.presentation.screen.InquiryWriteScreen
Expand Down Expand Up @@ -85,7 +86,7 @@ public fun SettingNavHost(
},
onPasskeyClick = actions::onNavigateToPasskey,
onAppLockClick = actions::onNavigateToAppLock,
onFaqClick = {},
onFaqClick = actions::onNavigateToFaq,
onInquiryClick = actions::onNavigateToInquiry,
onNoticeClick = actions::onNavigateToNotice,
onTermsClick = {},
Expand Down Expand Up @@ -255,6 +256,10 @@ public fun SettingNavHost(
entry<SettingRoute.InquiryWriteRoute> {
InquiryWriteScreen(onBackClick = actions::popBack)
}

entry<SettingRoute.FaqRoute> {
FaqScreen(onBackClick = actions::popBack)
}
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ internal sealed interface SettingRoute : NavKey {
@Serializable
data object NoticeRoute : SettingRoute

@Serializable
data object FaqRoute : SettingRoute

@Serializable
data object InquiryListRoute : SettingRoute

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.afternote.feature.setting.presentation.screen

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
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.core.ui.topbar.DetailTopBar
import com.afternote.feature.setting.presentation.R
import com.afternote.core.ui.R as CoreUiR

@Composable
fun FaqScreen(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by rememberSaveable { mutableStateOf(false) }

Scaffold(
modifier = modifier,
topBar = {
DetailTopBar(
title = stringResource(R.string.settings_support_faq),
onBackClick = onBackClick,
)
},
containerColor = AfternoteDesign.colors.gray1,
) { innerPadding ->
LazyColumn(
modifier =
Modifier
.fillMaxSize()
.padding(innerPadding),
) {
item {
FaqItem(
question = stringResource(R.string.faq_default_question),
answer = stringResource(R.string.faq_default_answer),
expanded = expanded,
onClick = { expanded = !expanded },
)
}
}
}
}

@Composable
private fun FaqItem(
question: String,
answer: String,
expanded: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier =
modifier
.fillMaxWidth()
.background(AfternoteDesign.colors.white),
) {
Row(
modifier =
Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 20.dp, vertical = 18.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = question,
modifier = Modifier.weight(1f),
style = AfternoteDesign.typography.bodyBase,
color = AfternoteDesign.colors.gray9,
)
Icon(
painter = painterResource(CoreUiR.drawable.core_ui_arrowdown),
contentDescription = null,
modifier =
Modifier
.size(24.dp)
.rotate(if (expanded) 180f else 0f),
tint = AfternoteDesign.colors.gray7,
)
}
if (expanded) {
Text(
text = answer,
modifier =
Modifier
.fillMaxWidth()
.background(AfternoteDesign.colors.gray1)
.padding(horizontal = 20.dp, vertical = 18.dp),
style = AfternoteDesign.typography.bodySmallR,
color = AfternoteDesign.colors.gray7,
)
}
HorizontalDivider(color = AfternoteDesign.colors.gray2)
}
}

@Preview(showBackground = true)
@Composable
private fun FaqScreenPreview() {
AfternoteTheme {
FaqScreen(onBackClick = {})
}
}
2 changes: 2 additions & 0 deletions feature/setting/presentation/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<string name="settings_security_app_lock">앱 잠금 설정</string>
<!-- 고객지원 -->
<string name="settings_support_faq">FAQ</string>
<string name="faq_default_question">비밀번호를 잊어버렸어요.</string>
<string name="faq_default_answer">로그인 화면의 \'비밀번호 찾기\'를 통해 재설정할 수 있습니다.</string>
<string name="settings_support_inquiry">고객센터</string>
<string name="setting_inquiry_title_bar">1:1 문의</string>
<string name="setting_inquiry_new">새 문의 접수하기</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.afternote.feature.setting.presentation.screen

import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.feature.setting.presentation.COMPACT_DEVICE_SPEC
import com.android.tools.screenshot.PreviewTest

@PreviewTest
@Preview(showBackground = true)
@Composable
internal fun faqScreenScreenshot() {
FaqScreenScreenshotContent()
}

@PreviewTest
@Preview(showBackground = true, device = COMPACT_DEVICE_SPEC)
@Composable
internal fun faqScreenCompactScreenshot() {
FaqScreenScreenshotContent()
}

@Composable
private fun FaqScreenScreenshotContent() {
AfternoteTheme {
FaqScreen(onBackClick = {})
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,60 @@
package com.afternote.feature.setting.presentation

import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.feature.setting.presentation.screen.FaqScreen
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode

private const val QUESTION = "비밀번호를 잊어버렸어요."
private const val ANSWER = "로그인 화면의 '비밀번호 찾기'를 통해 재설정할 수 있습니다."

@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [35])
class FaqScreenTest {
@get:Rule
val composeRule = createComposeRule()

@Test
fun answer_isHiddenUntilQuestionRowIsClicked_thenToggleHidesItAgain() {
composeRule.setContent {
AfternoteTheme {
FaqScreen(onBackClick = {})
}
}

composeRule.onAllNodesWithText(ANSWER).assertCountEquals(0)

composeRule.onNodeWithText(QUESTION).performClick()
composeRule.onNodeWithText(ANSWER).assertIsDisplayed()

composeRule.onNodeWithText(QUESTION).performClick()
composeRule.onAllNodesWithText(ANSWER).assertCountEquals(0)
}

@Test
fun backClick_invokesOnBackClick() {
var backClicked = false
composeRule.setContent {
AfternoteTheme {
FaqScreen(onBackClick = { backClicked = true })
}
}

composeRule.onNodeWithContentDescription("뒤로가기").performClick()

assertTrue(backClicked)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ class SettingLocalNavActionsTest {
assertEquals(listOf(SettingRoute.SettingHomeRoute), stack.toList())
}

@Test
fun `faq returns to the screen that opened it`() {
actions.onNavigateToFaq()
assertEquals(SettingRoute.FaqRoute, stack.last())
actions.popBack()
assertEquals(listOf(SettingRoute.SettingHomeRoute), stack.toList())
}

@Test
fun `all destination keys and route arguments survive serialization`() {
val routes =
Expand All @@ -126,6 +134,7 @@ class SettingLocalNavActionsTest {
SettingRoute.InquiryListRoute,
SettingRoute.InquiryDetailRoute(51L),
SettingRoute.InquiryWriteRoute,
SettingRoute.FaqRoute,
)
assertEquals(routes, Json.decodeFromString<List<SettingRoute>>(Json.encodeToString(routes)))
}
Expand Down
Loading