Fix tests

This commit is contained in:
Benoit Marty
2024-09-24 10:01:34 +02:00
parent 0cea89edcc
commit 2ec6250e6f
5 changed files with 73 additions and 30 deletions

View File

@@ -17,7 +17,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -39,7 +38,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.analytics.api.ScreenTracker
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope
@@ -80,10 +78,10 @@ class CallScreenPresenter @AssistedInject constructor(
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
var isJoinedCall by rememberSaveable { mutableStateOf(false) }
var canRenderWebViewInCaseOfError by rememberSaveable { mutableStateOf(false) }
var ignoreWebViewError by rememberSaveable { mutableStateOf(false) }
var webViewError by remember { mutableStateOf<String?>(null) }
val languageTag = languageTagProvider.provideLanguageTag()
val theme = if (ElementTheme.isLightTheme) "light" else "dark"
val errorMessage = stringResource(id = CommonStrings.error_unknown)
DisposableEffect(Unit) {
coroutineScope.launch {
// Sets the call as joined
@@ -130,7 +128,7 @@ class CallScreenPresenter @AssistedInject constructor(
interceptor.interceptedMessages
.onEach {
// We are receiving messages from the WebView, consider that the application is loaded
canRenderWebViewInCaseOfError = true
ignoreWebViewError = true
// Relay message to Widget Driver
callWidgetDriver.value?.send(it)
@@ -170,15 +168,8 @@ class CallScreenPresenter @AssistedInject constructor(
messageInterceptor.value = event.widgetMessageInterceptor
}
is CallScreenEvents.OnWebViewError -> {
if (!canRenderWebViewInCaseOfError) {
urlState.value = AsyncData.Failure(
Exception(
buildString {
append(errorMessage)
event.description?.let { append("\n\n").append(it) }
}
)
)
if (!ignoreWebViewError) {
webViewError = event.description.orEmpty()
}
// Else ignore the error, give a chance the Element Call to recover by itself.
}
@@ -187,7 +178,7 @@ class CallScreenPresenter @AssistedInject constructor(
return CallScreenState(
urlState = urlState.value,
canRenderWebViewInCaseOfError = canRenderWebViewInCaseOfError,
webViewError = webViewError,
userAgent = userAgent,
isInWidgetMode = isInWidgetMode,
eventSink = { handleEvents(it) },

View File

@@ -11,7 +11,7 @@ import io.element.android.libraries.architecture.AsyncData
data class CallScreenState(
val urlState: AsyncData<String>,
val canRenderWebViewInCaseOfError: Boolean,
val webViewError: String?,
val userAgent: String,
val isInWidgetMode: Boolean,
val eventSink: (CallScreenEvents) -> Unit,

View File

@@ -16,19 +16,20 @@ open class CallScreenStateProvider : PreviewParameterProvider<CallScreenState> {
aCallScreenState(),
aCallScreenState(urlState = AsyncData.Loading()),
aCallScreenState(urlState = AsyncData.Failure(Exception("An error occurred"))),
aCallScreenState(webViewError = "Error details from WebView"),
)
}
internal fun aCallScreenState(
urlState: AsyncData<String> = AsyncData.Success("https://call.element.io/some-actual-call?with=parameters"),
canRenderWebViewInCaseOfError: Boolean = true,
webViewError: String? = null,
userAgent: String = "",
isInWidgetMode: Boolean = false,
eventSink: (CallScreenEvents) -> Unit = {},
): CallScreenState {
return CallScreenState(
urlState = urlState,
canRenderWebViewInCaseOfError = canRenderWebViewInCaseOfError,
webViewError = webViewError,
userAgent = userAgent,
isInWidgetMode = isInWidgetMode,
eventSink = eventSink,

View File

@@ -85,7 +85,15 @@ internal fun CallScreenView(
BackHandler {
handleBack()
}
if (state.urlState !is AsyncData.Failure || state.canRenderWebViewInCaseOfError) {
if (state.webViewError != null) {
ErrorDialog(
content = buildString {
append(stringResource(CommonStrings.error_unknown))
state.webViewError.takeIf { it.isNotEmpty() }?.let { append("\n\n").append(it) }
},
onSubmit = { state.eventSink(CallScreenEvents.Hangup) },
)
} else {
CallWebView(
modifier = Modifier
.padding(padding)
@@ -108,17 +116,17 @@ internal fun CallScreenView(
pipState.eventSink(PictureInPictureEvents.SetPipController(pipController))
}
)
}
when (state.urlState) {
AsyncData.Uninitialized,
is AsyncData.Loading ->
ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait))
is AsyncData.Failure ->
ErrorDialog(
content = state.urlState.error.message.orEmpty(),
onSubmit = { state.eventSink(CallScreenEvents.Hangup) },
)
is AsyncData.Success -> Unit
when (state.urlState) {
AsyncData.Uninitialized,
is AsyncData.Loading ->
ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait))
is AsyncData.Failure ->
ErrorDialog(
content = state.urlState.error.message.orEmpty(),
onSubmit = { state.eventSink(CallScreenEvents.Hangup) },
)
is AsyncData.Success -> Unit
}
}
}
}

View File

@@ -71,6 +71,7 @@ class CallScreenPresenterTest {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io"))
assertThat(initialState.webViewError).isNull()
assertThat(initialState.isInWidgetMode).isFalse()
analyticsLambda.assertions().isNeverCalled()
joinedCallLambda.assertions().isCalledOnce()
@@ -270,6 +271,48 @@ class CallScreenPresenterTest {
assert(stopSyncLambda).isCalledOnce()
}
@Test
fun `present - error from WebView are updating the state`() = runTest {
val presenter = createCallScreenPresenter(
callType = CallType.ExternalUrl("https://call.element.io"),
activeCallManager = FakeActiveCallManager(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Wait until the URL is loaded
skipItems(1)
val initialState = awaitItem()
initialState.eventSink(CallScreenEvents.OnWebViewError("A Webview error"))
val finalState = awaitItem()
assertThat(finalState.webViewError).isEqualTo("A Webview error")
}
}
@Test
fun `present - error from WebView are ignored if Element Call is loaded`() = runTest {
val presenter = createCallScreenPresenter(
callType = CallType.ExternalUrl("https://call.element.io"),
activeCallManager = FakeActiveCallManager(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Wait until the URL is loaded
skipItems(1)
val initialState = awaitItem()
val messageInterceptor = FakeWidgetMessageInterceptor()
initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor))
// Emit a message
messageInterceptor.givenInterceptedMessage("A message")
// WebView emits an error, but it will be ignored
initialState.eventSink(CallScreenEvents.OnWebViewError("A Webview error"))
val finalState = awaitItem()
assertThat(finalState.webViewError).isNull()
}
}
private fun TestScope.createCallScreenPresenter(
callType: CallType,
navigator: CallScreenNavigator = FakeCallScreenNavigator(),