mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-22 18:14:49 +00:00
Update ErrorPanel and retest
This commit is contained in:
@@ -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<Context>() }
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.schabi.newpipe.ui.components.common
|
package org.schabi.newpipe.ui.components.common
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -11,6 +10,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
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.ErrorInfo
|
||||||
import org.schabi.newpipe.error.ErrorUtil
|
import org.schabi.newpipe.error.ErrorUtil
|
||||||
import org.schabi.newpipe.error.ReCaptchaActivity
|
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.AppTheme
|
||||||
import org.schabi.newpipe.ui.theme.SizeTokens.SpacingExtraLarge
|
import org.schabi.newpipe.ui.theme.SizeTokens
|
||||||
import org.schabi.newpipe.ui.theme.SizeTokens.SpacingMedium
|
|
||||||
import org.schabi.newpipe.ui.theme.SizeTokens.SpacingSmall
|
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
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
|
@Composable
|
||||||
fun ErrorPanel(
|
fun ErrorPanel(
|
||||||
errorInfo: ErrorInfo,
|
errorInfo: ErrorInfo,
|
||||||
@@ -51,11 +30,13 @@ fun ErrorPanel(
|
|||||||
onRetry: (() -> Unit)? = null,
|
onRetry: (() -> Unit)? = null,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
val explanation = errorInfo.getExplanation()
|
|
||||||
val canOpenInBrowser = errorInfo.openInBrowserUrl != null
|
|
||||||
val errorActionType = determineErrorAction(errorInfo)
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val isPreview = LocalInspectionMode.current
|
||||||
|
val messageText = if (isPreview) {
|
||||||
|
stringResource(R.string.error_snackbar_message)
|
||||||
|
} else {
|
||||||
|
errorInfo.getMessage(context)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@@ -63,61 +44,48 @@ fun ErrorPanel(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(errorInfo.messageStringId),
|
text = messageText,
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|
||||||
if (explanation.isNotBlank()) {
|
Spacer(Modifier.height(SizeTokens.SpacingMedium))
|
||||||
Spacer(Modifier.height(SpacingSmall))
|
if (errorInfo.isReportable) {
|
||||||
Text(
|
|
||||||
text = explanation,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(SpacingMedium))
|
|
||||||
when (errorActionType) {
|
|
||||||
ErrorAction.REPORT -> {
|
|
||||||
ServiceColoredButton(onClick = {
|
ServiceColoredButton(onClick = {
|
||||||
ErrorUtil.openActivity(context, errorInfo)
|
ErrorUtil.openActivity(context, errorInfo)
|
||||||
}) {
|
}) {
|
||||||
Text(stringResource(errorActionType.actionStringId).uppercase())
|
Text(stringResource(R.string.error_snackbar_action).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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
onRetry?.let {
|
||||||
ServiceColoredButton(onClick = it) {
|
ServiceColoredButton(onClick = it) {
|
||||||
Text(stringResource(R.string.retry).uppercase())
|
Text(stringResource(R.string.retry).uppercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (canOpenInBrowser) {
|
|
||||||
ServiceColoredButton(onClick = {
|
|
||||||
errorInfo.openInBrowserUrl?.let { url ->
|
|
||||||
ShareUtils.openUrlInBrowser(context, url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorInfo.openInBrowserUrl?.let { url ->
|
||||||
|
ServiceColoredButton(onClick = {
|
||||||
|
ShareUtils.openUrlInBrowser(context, url)
|
||||||
}) {
|
}) {
|
||||||
Text(stringResource(R.string.open_in_browser).uppercase())
|
Text(stringResource(R.string.open_in_browser).uppercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.height(SpacingExtraLarge))
|
Spacer(Modifier.height(SizeTokens.SpacingExtraLarge))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user