Fix tests
This commit is contained in:
parent
0cea89edcc
commit
2ec6250e6f
5 changed files with 73 additions and 30 deletions
|
|
@ -17,7 +17,6 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
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.sync.SyncState
|
||||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
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.analytics.api.ScreenTracker
|
||||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
@ -80,10 +78,10 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
||||||
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
||||||
var isJoinedCall by rememberSaveable { mutableStateOf(false) }
|
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 languageTag = languageTagProvider.provideLanguageTag()
|
||||||
val theme = if (ElementTheme.isLightTheme) "light" else "dark"
|
val theme = if (ElementTheme.isLightTheme) "light" else "dark"
|
||||||
val errorMessage = stringResource(id = CommonStrings.error_unknown)
|
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
// Sets the call as joined
|
// Sets the call as joined
|
||||||
|
|
@ -130,7 +128,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
interceptor.interceptedMessages
|
interceptor.interceptedMessages
|
||||||
.onEach {
|
.onEach {
|
||||||
// We are receiving messages from the WebView, consider that the application is loaded
|
// We are receiving messages from the WebView, consider that the application is loaded
|
||||||
canRenderWebViewInCaseOfError = true
|
ignoreWebViewError = true
|
||||||
// Relay message to Widget Driver
|
// Relay message to Widget Driver
|
||||||
callWidgetDriver.value?.send(it)
|
callWidgetDriver.value?.send(it)
|
||||||
|
|
||||||
|
|
@ -170,15 +168,8 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
messageInterceptor.value = event.widgetMessageInterceptor
|
messageInterceptor.value = event.widgetMessageInterceptor
|
||||||
}
|
}
|
||||||
is CallScreenEvents.OnWebViewError -> {
|
is CallScreenEvents.OnWebViewError -> {
|
||||||
if (!canRenderWebViewInCaseOfError) {
|
if (!ignoreWebViewError) {
|
||||||
urlState.value = AsyncData.Failure(
|
webViewError = event.description.orEmpty()
|
||||||
Exception(
|
|
||||||
buildString {
|
|
||||||
append(errorMessage)
|
|
||||||
event.description?.let { append("\n\n").append(it) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// Else ignore the error, give a chance the Element Call to recover by itself.
|
// Else ignore the error, give a chance the Element Call to recover by itself.
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +178,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
return CallScreenState(
|
return CallScreenState(
|
||||||
urlState = urlState.value,
|
urlState = urlState.value,
|
||||||
canRenderWebViewInCaseOfError = canRenderWebViewInCaseOfError,
|
webViewError = webViewError,
|
||||||
userAgent = userAgent,
|
userAgent = userAgent,
|
||||||
isInWidgetMode = isInWidgetMode,
|
isInWidgetMode = isInWidgetMode,
|
||||||
eventSink = { handleEvents(it) },
|
eventSink = { handleEvents(it) },
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class CallScreenState(
|
data class CallScreenState(
|
||||||
val urlState: AsyncData<String>,
|
val urlState: AsyncData<String>,
|
||||||
val canRenderWebViewInCaseOfError: Boolean,
|
val webViewError: String?,
|
||||||
val userAgent: String,
|
val userAgent: String,
|
||||||
val isInWidgetMode: Boolean,
|
val isInWidgetMode: Boolean,
|
||||||
val eventSink: (CallScreenEvents) -> Unit,
|
val eventSink: (CallScreenEvents) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,20 @@ open class CallScreenStateProvider : PreviewParameterProvider<CallScreenState> {
|
||||||
aCallScreenState(),
|
aCallScreenState(),
|
||||||
aCallScreenState(urlState = AsyncData.Loading()),
|
aCallScreenState(urlState = AsyncData.Loading()),
|
||||||
aCallScreenState(urlState = AsyncData.Failure(Exception("An error occurred"))),
|
aCallScreenState(urlState = AsyncData.Failure(Exception("An error occurred"))),
|
||||||
|
aCallScreenState(webViewError = "Error details from WebView"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aCallScreenState(
|
internal fun aCallScreenState(
|
||||||
urlState: AsyncData<String> = AsyncData.Success("https://call.element.io/some-actual-call?with=parameters"),
|
urlState: AsyncData<String> = AsyncData.Success("https://call.element.io/some-actual-call?with=parameters"),
|
||||||
canRenderWebViewInCaseOfError: Boolean = true,
|
webViewError: String? = null,
|
||||||
userAgent: String = "",
|
userAgent: String = "",
|
||||||
isInWidgetMode: Boolean = false,
|
isInWidgetMode: Boolean = false,
|
||||||
eventSink: (CallScreenEvents) -> Unit = {},
|
eventSink: (CallScreenEvents) -> Unit = {},
|
||||||
): CallScreenState {
|
): CallScreenState {
|
||||||
return CallScreenState(
|
return CallScreenState(
|
||||||
urlState = urlState,
|
urlState = urlState,
|
||||||
canRenderWebViewInCaseOfError = canRenderWebViewInCaseOfError,
|
webViewError = webViewError,
|
||||||
userAgent = userAgent,
|
userAgent = userAgent,
|
||||||
isInWidgetMode = isInWidgetMode,
|
isInWidgetMode = isInWidgetMode,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,15 @@ internal fun CallScreenView(
|
||||||
BackHandler {
|
BackHandler {
|
||||||
handleBack()
|
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(
|
CallWebView(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
|
|
@ -108,17 +116,17 @@ internal fun CallScreenView(
|
||||||
pipState.eventSink(PictureInPictureEvents.SetPipController(pipController))
|
pipState.eventSink(PictureInPictureEvents.SetPipController(pipController))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
when (state.urlState) {
|
||||||
when (state.urlState) {
|
AsyncData.Uninitialized,
|
||||||
AsyncData.Uninitialized,
|
is AsyncData.Loading ->
|
||||||
is AsyncData.Loading ->
|
ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait))
|
||||||
ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait))
|
is AsyncData.Failure ->
|
||||||
is AsyncData.Failure ->
|
ErrorDialog(
|
||||||
ErrorDialog(
|
content = state.urlState.error.message.orEmpty(),
|
||||||
content = state.urlState.error.message.orEmpty(),
|
onSubmit = { state.eventSink(CallScreenEvents.Hangup) },
|
||||||
onSubmit = { state.eventSink(CallScreenEvents.Hangup) },
|
)
|
||||||
)
|
is AsyncData.Success -> Unit
|
||||||
is AsyncData.Success -> Unit
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ class CallScreenPresenterTest {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io"))
|
assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io"))
|
||||||
|
assertThat(initialState.webViewError).isNull()
|
||||||
assertThat(initialState.isInWidgetMode).isFalse()
|
assertThat(initialState.isInWidgetMode).isFalse()
|
||||||
analyticsLambda.assertions().isNeverCalled()
|
analyticsLambda.assertions().isNeverCalled()
|
||||||
joinedCallLambda.assertions().isCalledOnce()
|
joinedCallLambda.assertions().isCalledOnce()
|
||||||
|
|
@ -270,6 +271,48 @@ class CallScreenPresenterTest {
|
||||||
assert(stopSyncLambda).isCalledOnce()
|
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(
|
private fun TestScope.createCallScreenPresenter(
|
||||||
callType: CallType,
|
callType: CallType,
|
||||||
navigator: CallScreenNavigator = FakeCallScreenNavigator(),
|
navigator: CallScreenNavigator = FakeCallScreenNavigator(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue