diff --git a/feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/OnboardingProfileScreen.kt b/feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/OnboardingProfileScreen.kt index 8700922e57..55cae8ab41 100644 --- a/feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/OnboardingProfileScreen.kt +++ b/feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/OnboardingProfileScreen.kt @@ -146,7 +146,7 @@ fun OnboardingProfileScreen( } /** 포토 피커 취소 결과(null)는 선택 변경이 아니므로 기존 프로필 이미지를 그대로 둔다. */ -internal fun handleProfileImagePickerResult( +private fun handleProfileImagePickerResult( uri: Uri?, onProfileImagePick: (Uri) -> Unit, ) { diff --git a/feature/onboarding/presentation/src/test/java/com/afternote/feature/onboarding/presentation/OnboardingProfilePickerResultTest.kt b/feature/onboarding/presentation/src/test/java/com/afternote/feature/onboarding/presentation/OnboardingProfilePickerResultTest.kt index 7a538a85b9..7cc292e097 100644 --- a/feature/onboarding/presentation/src/test/java/com/afternote/feature/onboarding/presentation/OnboardingProfilePickerResultTest.kt +++ b/feature/onboarding/presentation/src/test/java/com/afternote/feature/onboarding/presentation/OnboardingProfilePickerResultTest.kt @@ -1,18 +1,103 @@ package com.afternote.feature.onboarding.presentation +import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalActivityResultRegistryOwner +import androidx.activity.result.ActivityResultRegistry +import androidx.activity.result.ActivityResultRegistryOwner +import androidx.activity.result.contract.ActivityResultContract +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.performClick +import androidx.core.app.ActivityOptionsCompat +import com.afternote.core.ui.theme.AfternoteTheme import org.junit.Assert.assertEquals +import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import com.afternote.core.ui.R as CoreUiR +/** + * 프로필 화면이 포토 피커 결과를 어떻게 다루는지 고정한다 (#1675). + * + * 결과 처리 helper 는 화면 파일 안에서만 쓰이는 `private` 구현이라 테스트가 직접 부르지 않는다. + * 대신 화면의 공개 계약 — 「프로필 수정 버튼」을 눌러 피커를 띄우고 그 결과가 돌아왔을 때 + * `onProfileImagePick` 이 불리는가 — 으로 판정한다. 피커 자체는 실행하지 않고 + * [LocalActivityResultRegistryOwner] 를 결과를 즉시 돌려주는 레지스트리로 갈아 끼운다. + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [35]) class OnboardingProfilePickerResultTest { + @get:Rule + val composeRule = createAndroidComposeRule() + + private val pickedUris = mutableListOf() + + /** 피커를 띄우면 [pickerResult] 를 그대로 돌려주는 화면을 세운다. */ + private fun setProfileContent(pickerResult: Uri?) { + val registryOwner = + object : ActivityResultRegistryOwner { + override val activityResultRegistry = ImmediateResultRegistry(pickerResult) + } + composeRule.setContent { + AfternoteTheme { + CompositionLocalProvider(LocalActivityResultRegistryOwner provides registryOwner) { + OnboardingProfileScreen( + initialName = "김노을", + displayImageUri = null, + snackbarHostState = remember { SnackbarHostState() }, + onNameChange = {}, + onProfileImagePick = pickedUris::add, + onBackClick = {}, + onCompleteClick = {}, + ) + } + } + } + } + + private fun clickProfileImagePicker() { + val editDescription = + composeRule.activity.getString(CoreUiR.string.core_ui_content_description_profile_edit) + composeRule.onNodeWithContentDescription(editDescription).performClick() + } + @Test fun `갤러리 취소는 기존 프로필 이미지를 변경하지 않는다`() { - val previouslySelectedUri = "content://profile/selected" - var profileImageUri = previouslySelectedUri + setProfileContent(pickerResult = null) - handleProfileImagePickerResult(uri = null) { pickedUri -> - profileImageUri = pickedUri.toString() - } + clickProfileImagePicker() + + assertEquals(emptyList(), pickedUris) + } + + @Test + fun `갤러리에서 고른 이미지는 프로필 이미지로 올라간다`() { + val selectedUri = Uri.parse("content://profile/selected") + setProfileContent(pickerResult = selectedUri) + + clickProfileImagePicker() + + assertEquals(listOf(selectedUri), pickedUris) + } +} - assertEquals(previouslySelectedUri, profileImageUri) +/** `launch` 즉시 [result] 를 돌려주는 레지스트리. 실제 갤러리 Activity 를 띄우지 않는다. */ +private class ImmediateResultRegistry( + private val result: Uri?, +) : ActivityResultRegistry() { + override fun onLaunch( + requestCode: Int, + contract: ActivityResultContract, + input: I, + options: ActivityOptionsCompat?, + ) { + @Suppress("UNCHECKED_CAST") + dispatchResult(requestCode, result as O) } } diff --git a/konsist/src/test/kotlin/com/afternote/konsist/ProductionVisibilityKonsistTest.kt b/konsist/src/test/kotlin/com/afternote/konsist/ProductionVisibilityKonsistTest.kt index b51b77e408..387583fe31 100644 --- a/konsist/src/test/kotlin/com/afternote/konsist/ProductionVisibilityKonsistTest.kt +++ b/konsist/src/test/kotlin/com/afternote/konsist/ProductionVisibilityKonsistTest.kt @@ -341,10 +341,8 @@ class ProductionVisibilityKonsistTest { "feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/viewmodel/WeeklyReportRecordedDays.kt::function resolveDateInWeekOrNull [internal]", ) - val ISSUE_1675_ONBOARDING = - setOf( - "feature/onboarding/presentation/src/main/java/com/afternote/feature/onboarding/presentation/OnboardingProfileScreen.kt::function handleProfileImagePickerResult [internal]", - ) + /** #1675 해소. 프로필 이미지 선택 결과 helper 는 private 이고 테스트는 화면 동작으로 본다. */ + val ISSUE_1675_ONBOARDING = emptySet() val ISSUE_1676_RECEIVER = setOf(