From fab0d35269787a252901a274dad0739f71fea309 Mon Sep 17 00:00:00 2001 From: Su TT Date: Wed, 24 Sep 2025 16:47:21 -0400 Subject: [PATCH] Update ErrorPanel and retest --- .../common/CommentSectionErrorTest.kt | 61 ++++++++++ .../ui/components/common/ErrorPanel.kt | 110 +++++++----------- .../common/CommentSectionErrorTest.kt | 61 ---------- 3 files changed, 100 insertions(+), 132 deletions(-) create mode 100644 app/src/androidTest/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt delete mode 100644 app/src/test/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt diff --git a/app/src/androidTest/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt b/app/src/androidTest/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt new file mode 100644 index 000000000..010cab7a7 --- /dev/null +++ b/app/src/androidTest/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt @@ -0,0 +1,61 @@ +package org.schabi.newpipe.ui.components.common + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.schabi.newpipe.R +import org.schabi.newpipe.error.ErrorInfo +import org.schabi.newpipe.error.UserAction +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException +import java.io.IOException +import java.net.SocketTimeoutException + +@RunWith(AndroidJUnit4::class) +class CommentSectionErrorTest { + private val context: Context by lazy { ApplicationProvider.getApplicationContext() } + // Test 1: Network error on initial load (Resource.Error) + @Test + fun testInitialCommentNetworkError() { + val errorInfo = ErrorInfo( + throwable = SocketTimeoutException("Connection timeout"), + userAction = UserAction.REQUESTED_COMMENTS, + request = "comments" + ) + Assert.assertEquals(context.getString(R.string.network_error), errorInfo.getMessage(context)) + Assert.assertTrue(errorInfo.isReportable) + Assert.assertTrue(errorInfo.isRetryable) + Assert.assertNull(errorInfo.recaptchaUrl) + } + + // Test 2: Network error on paging (LoadState.Error) + @Test + fun testPagingNetworkError() { + val errorInfo = ErrorInfo( + throwable = IOException("Paging failed"), + userAction = UserAction.REQUESTED_COMMENTS, + request = "comments" + ) + Assert.assertEquals(context.getString(R.string.network_error), errorInfo.getMessage(context)) + Assert.assertTrue(errorInfo.isReportable) + Assert.assertTrue(errorInfo.isRetryable) + Assert.assertNull(errorInfo.recaptchaUrl) + } + + // Test 3: ReCaptcha during comments load + @Test + fun testReCaptchaDuringComments() { + val url = "https://www.google.com/recaptcha/api/fallback?k=test" + val errorInfo = ErrorInfo( + throwable = ReCaptchaException("ReCaptcha needed", url), + userAction = UserAction.REQUESTED_COMMENTS, + request = "comments" + ) + Assert.assertEquals(context.getString(R.string.recaptcha_request_toast), errorInfo.getMessage(context)) + Assert.assertEquals(url, errorInfo.recaptchaUrl) + Assert.assertTrue(errorInfo.isReportable) + Assert.assertTrue(errorInfo.isRetryable) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/ErrorPanel.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/ErrorPanel.kt index de6dd096e..3a2771a6f 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/common/ErrorPanel.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/ErrorPanel.kt @@ -1,7 +1,6 @@ package org.schabi.newpipe.ui.components.common import android.content.Intent -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -11,6 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -19,31 +19,10 @@ import org.schabi.newpipe.R import org.schabi.newpipe.error.ErrorInfo import org.schabi.newpipe.error.ErrorUtil import org.schabi.newpipe.error.ReCaptchaActivity -import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException import org.schabi.newpipe.ui.theme.AppTheme -import org.schabi.newpipe.ui.theme.SizeTokens.SpacingExtraLarge -import org.schabi.newpipe.ui.theme.SizeTokens.SpacingMedium -import org.schabi.newpipe.ui.theme.SizeTokens.SpacingSmall +import org.schabi.newpipe.ui.theme.SizeTokens import org.schabi.newpipe.util.external_communication.ShareUtils -enum class ErrorAction(@StringRes val actionStringId: Int) { - REPORT(R.string.error_snackbar_action), - SOLVE_CAPTCHA(R.string.recaptcha_solve) -} - -/** - * Determines the error action type based on the throwable in ErrorInfo - * - */ -fun determineErrorAction(errorInfo: ErrorInfo): ErrorAction { - return when (errorInfo.throwable) { - is ReCaptchaException -> ErrorAction.SOLVE_CAPTCHA - is AccountTerminatedException -> ErrorAction.REPORT - else -> ErrorAction.REPORT - } -} - @Composable fun ErrorPanel( errorInfo: ErrorInfo, @@ -51,11 +30,13 @@ fun ErrorPanel( onRetry: (() -> Unit)? = null, ) { - val explanation = errorInfo.getExplanation() - val canOpenInBrowser = errorInfo.openInBrowserUrl != null - val errorActionType = determineErrorAction(errorInfo) - val context = LocalContext.current + val isPreview = LocalInspectionMode.current + val messageText = if (isPreview) { + stringResource(R.string.error_snackbar_message) + } else { + errorInfo.getMessage(context) + } Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -63,61 +44,48 @@ fun ErrorPanel( ) { Text( - text = stringResource(errorInfo.messageStringId), + text = messageText, style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), textAlign = TextAlign.Center ) - if (explanation.isNotBlank()) { - Spacer(Modifier.height(SpacingSmall)) - Text( - text = explanation, - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center - ) - } - - Spacer(Modifier.height(SpacingMedium)) - when (errorActionType) { - ErrorAction.REPORT -> { - ServiceColoredButton(onClick = { - ErrorUtil.openActivity(context, errorInfo) - }) { - Text(stringResource(errorActionType.actionStringId).uppercase()) - } - } - ErrorAction.SOLVE_CAPTCHA -> { - ServiceColoredButton(onClick = { - // Starting ReCaptcha Challenge Activity - val intent = Intent(context, ReCaptchaActivity::class.java) - .putExtra( - ReCaptchaActivity.RECAPTCHA_URL_EXTRA, - (errorInfo.throwable as ReCaptchaException).url - ) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) - }) { - Text(stringResource(errorActionType.actionStringId).uppercase()) - } - } - } - - onRetry?.let { - ServiceColoredButton(onClick = it) { - Text(stringResource(R.string.retry).uppercase()) - } - } - if (canOpenInBrowser) { + Spacer(Modifier.height(SizeTokens.SpacingMedium)) + if (errorInfo.isReportable) { ServiceColoredButton(onClick = { - errorInfo.openInBrowserUrl?.let { url -> - ShareUtils.openUrlInBrowser(context, url) + ErrorUtil.openActivity(context, errorInfo) + }) { + Text(stringResource(R.string.error_snackbar_action).uppercase()) + } + } + + errorInfo.recaptchaUrl?.let { recaptchaUrl -> + ServiceColoredButton(onClick = { + val intent = Intent(context, ReCaptchaActivity::class.java) + .putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, recaptchaUrl) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + }) { + Text(stringResource(R.string.recaptcha_solve).uppercase()) + } + } + + if (errorInfo.isRetryable) { + onRetry?.let { + ServiceColoredButton(onClick = it) { + Text(stringResource(R.string.retry).uppercase()) } + } + } + + errorInfo.openInBrowserUrl?.let { url -> + ServiceColoredButton(onClick = { + ShareUtils.openUrlInBrowser(context, url) }) { Text(stringResource(R.string.open_in_browser).uppercase()) } } - Spacer(Modifier.height(SpacingExtraLarge)) + Spacer(Modifier.height(SizeTokens.SpacingExtraLarge)) } } diff --git a/app/src/test/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt b/app/src/test/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt deleted file mode 100644 index 42e45a3c5..000000000 --- a/app/src/test/java/org/schabi/newpipe/ui/components/common/CommentSectionErrorTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.schabi.newpipe.ui.components.common - -import androidx.paging.LoadState -import org.junit.Assert -import org.junit.Test -import org.schabi.newpipe.error.ErrorInfo -import org.schabi.newpipe.error.UserAction -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException -import java.io.IOException -import java.net.SocketTimeoutException - -class CommentSectionErrorTest { - - // Test 1: Network error on initial load (Resource.Error) - @Test - fun testInitialCommentNetworkError() { - val expectedMessage = "Connection timeout" - val networkError = SocketTimeoutException(expectedMessage) - - val errorInfo = ErrorInfo( - throwable = networkError, - userAction = UserAction.REQUESTED_COMMENTS, - request = "comments" - ) - Assert.assertEquals(networkError, errorInfo.throwable) - Assert.assertEquals(ErrorAction.REPORT, determineErrorAction(errorInfo)) - Assert.assertEquals(expectedMessage, errorInfo.getExplanation()) - } - - // Test 2: Network error on paging (LoadState.Error) - @Test - fun testPagingNetworkError() { - val expectedMessage = "Paging failed" - val pagingError = IOException(expectedMessage) - val loadStateError = LoadState.Error(pagingError) - - val errorInfo = ErrorInfo( - throwable = loadStateError.error, - userAction = UserAction.REQUESTED_COMMENTS, - request = "comments" - ) - Assert.assertEquals(pagingError, errorInfo.throwable) - Assert.assertEquals(ErrorAction.REPORT, determineErrorAction(errorInfo)) - Assert.assertEquals(expectedMessage, errorInfo.getExplanation()) - } - - // Test 3: ReCaptcha during comments load - @Test - fun testReCaptchaDuringComments() { - val url = "https://www.google.com/recaptcha/api/fallback?k=test" - val expectedMessage = "ReCaptcha needed" - val captcha = ReCaptchaException(expectedMessage, url) - val errorInfo = ErrorInfo( - throwable = captcha, - userAction = UserAction.REQUESTED_COMMENTS, - request = "comments" - ) - Assert.assertEquals(ErrorAction.SOLVE_CAPTCHA, determineErrorAction(errorInfo)) - Assert.assertEquals(expectedMessage, errorInfo.getExplanation()) - } -}