diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/SettingImplementedCoverageAndroidTest.kt b/app/src/androidTest/java/com/afternote/afternote_fe/SettingImplementedCoverageAndroidTest.kt index e1234b7ca5..5d8dc29f72 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/SettingImplementedCoverageAndroidTest.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/SettingImplementedCoverageAndroidTest.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.hasScrollAction import androidx.compose.ui.test.hasText 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 @@ -144,6 +145,27 @@ class SettingImplementedCoverageAndroidTest { .assertIsDisplayed() } + @Test + fun actualSettingNavHost_faqRowNavigatesToFaqScreenAndBackReturnsHome() { + waitForSettingHomeContent() + composeRule + .onNode(hasScrollAction()) + .performScrollToNode(hasText("FAQ")) + composeRule + .onNodeWithText("FAQ") + .performClick() + + waitForRoute() + composeRule + .onNodeWithText("비밀번호를 잊어버렸어요.") + .assertIsDisplayed() + + composeRule + .onNodeWithContentDescription("뒤로가기") + .performClick() + waitForRoute() + } + private fun openWithdrawGuide() { waitForSettingHomeContent() composeRule diff --git a/app/src/main/java/com/afternote/afternote_fe/navigation/AppNavigationActions.kt b/app/src/main/java/com/afternote/afternote_fe/navigation/AppNavigationActions.kt index 29ade94ed6..35925f82e9 100644 --- a/app/src/main/java/com/afternote/afternote_fe/navigation/AppNavigationActions.kt +++ b/app/src/main/java/com/afternote/afternote_fe/navigation/AppNavigationActions.kt @@ -356,6 +356,14 @@ fun rememberSettingNavActions(appState: AppState): SettingNavActions = override fun onNoticeBack() { appState.navController.popBackStack() } + + override fun onNavigateToFaq() { + appState.navController.navigate(SettingRoute.FaqRoute) + } + + override fun onFaqBack() { + appState.navController.popBackStack() + } } } diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavActions.kt b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavActions.kt index 44bef11258..b2c2e27079 100644 --- a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavActions.kt +++ b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavActions.kt @@ -64,4 +64,8 @@ interface SettingNavActions { fun onNavigateToNotice() fun onNoticeBack() + + fun onNavigateToFaq() + + fun onFaqBack() } diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavGraph.kt b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavGraph.kt index c47d60b995..2c4781c962 100644 --- a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavGraph.kt +++ b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingNavGraph.kt @@ -14,6 +14,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.NoticeListScreen import com.afternote.feature.setting.presentation.screen.PassKeyListScreen import com.afternote.feature.setting.presentation.screen.PassKeyMakingScreen @@ -51,7 +52,7 @@ fun NavGraphBuilder.settingNavGraph( }, onPasskeyClick = actions::onNavigateToPasskey, onAppLockClick = actions::onNavigateToAppLock, - onFaqClick = {}, + onFaqClick = actions::onNavigateToFaq, onInquiryClick = {}, onNoticeClick = actions::onNavigateToNotice, onTermsClick = {}, @@ -187,5 +188,11 @@ fun NavGraphBuilder.settingNavGraph( onBackClick = actions::onNoticeBack, ) } + + composable { + FaqScreen( + onBackClick = actions::onFaqBack, + ) + } } } diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingRoute.kt b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingRoute.kt index 898cb15077..8abba3f9fa 100644 --- a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingRoute.kt +++ b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/navigation/SettingRoute.kt @@ -53,4 +53,7 @@ sealed interface SettingRoute { @Serializable data object NoticeRoute : SettingRoute + + @Serializable + data object FaqRoute : SettingRoute } diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreen.kt b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreen.kt new file mode 100644 index 0000000000..882a7b0fe5 --- /dev/null +++ b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreen.kt @@ -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 = {}) + } +} diff --git a/feature/setting/presentation/src/main/res/values/strings.xml b/feature/setting/presentation/src/main/res/values/strings.xml index 5bef489d95..f8295b975e 100644 --- a/feature/setting/presentation/src/main/res/values/strings.xml +++ b/feature/setting/presentation/src/main/res/values/strings.xml @@ -21,6 +21,8 @@ 앱 잠금 설정 FAQ + 비밀번호를 잊어버렸어요. + 로그인 화면의 \'비밀번호 찾기\'를 통해 재설정할 수 있습니다. 고객센터 공지사항 등록된 공지사항이 없어요. diff --git a/feature/setting/presentation/src/screenshotTest/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTest.kt b/feature/setting/presentation/src/screenshotTest/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTest.kt new file mode 100644 index 0000000000..826e6b3061 --- /dev/null +++ b/feature/setting/presentation/src/screenshotTest/kotlin/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTest.kt @@ -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 = {}) + } +} diff --git a/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenCompactScreenshot_c2abd7e6_0.png b/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenCompactScreenshot_c2abd7e6_0.png new file mode 100644 index 0000000000..d92c23b5f4 Binary files /dev/null and b/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenCompactScreenshot_c2abd7e6_0.png differ diff --git a/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenScreenshot_748aa731_0.png b/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenScreenshot_748aa731_0.png new file mode 100644 index 0000000000..0347f3c746 Binary files /dev/null and b/feature/setting/presentation/src/screenshotTestDebug/reference/com/afternote/feature/setting/presentation/screen/FaqScreenScreenshotTestKt/faqScreenScreenshot_748aa731_0.png differ diff --git a/feature/setting/presentation/src/test/kotlin/com/afternote/feature/setting/presentation/FaqScreenTest.kt b/feature/setting/presentation/src/test/kotlin/com/afternote/feature/setting/presentation/FaqScreenTest.kt new file mode 100644 index 0000000000..495f0a4a36 --- /dev/null +++ b/feature/setting/presentation/src/test/kotlin/com/afternote/feature/setting/presentation/FaqScreenTest.kt @@ -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) + } +}