Merge pull request #6040 from element-hq/feature/bma/updateBugReportScreenshot
Ensure screenshot is up to date
This commit is contained in:
commit
454415225f
19 changed files with 113 additions and 169 deletions
|
|
@ -15,9 +15,9 @@ 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.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
|
import io.element.android.features.rageshake.api.crash.CrashDetectionEvent
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionView
|
import io.element.android.features.rageshake.api.crash.CrashDetectionView
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvent
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionView
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionView
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
|
@ -39,8 +39,8 @@ fun RootView(
|
||||||
children()
|
children()
|
||||||
|
|
||||||
fun onOpenBugReport() {
|
fun onOpenBugReport() {
|
||||||
state.crashDetectionState.eventSink(CrashDetectionEvents.ResetAppHasCrashed)
|
state.crashDetectionState.eventSink(CrashDetectionEvent.ResetAppHasCrashed)
|
||||||
state.rageshakeDetectionState.eventSink(RageshakeDetectionEvents.Dismiss)
|
state.rageshakeDetectionState.eventSink(RageshakeDetectionEvent.Dismiss)
|
||||||
onOpenBugReport.invoke()
|
onOpenBugReport.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
package io.element.android.features.rageshake.api.crash
|
package io.element.android.features.rageshake.api.crash
|
||||||
|
|
||||||
sealed interface CrashDetectionEvents {
|
sealed interface CrashDetectionEvent {
|
||||||
data object ResetAllCrashData : CrashDetectionEvents
|
data object ResetAllCrashData : CrashDetectionEvent
|
||||||
data object ResetAppHasCrashed : CrashDetectionEvents
|
data object ResetAppHasCrashed : CrashDetectionEvent
|
||||||
}
|
}
|
||||||
|
|
@ -11,5 +11,5 @@ package io.element.android.features.rageshake.api.crash
|
||||||
data class CrashDetectionState(
|
data class CrashDetectionState(
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val crashDetected: Boolean,
|
val crashDetected: Boolean,
|
||||||
val eventSink: (CrashDetectionEvents) -> Unit
|
val eventSink: (CrashDetectionEvent) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ fun CrashDetectionView(
|
||||||
onOpenBugReport: () -> Unit = { },
|
onOpenBugReport: () -> Unit = { },
|
||||||
) {
|
) {
|
||||||
fun onPopupDismissed() {
|
fun onPopupDismissed() {
|
||||||
state.eventSink(CrashDetectionEvents.ResetAllCrashData)
|
state.eventSink(CrashDetectionEvent.ResetAllCrashData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.crashDetected) {
|
if (state.crashDetected) {
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ package io.element.android.features.rageshake.api.detection
|
||||||
|
|
||||||
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
||||||
|
|
||||||
sealed interface RageshakeDetectionEvents {
|
sealed interface RageshakeDetectionEvent {
|
||||||
data object Dismiss : RageshakeDetectionEvents
|
data object Dismiss : RageshakeDetectionEvent
|
||||||
data object Disable : RageshakeDetectionEvents
|
data object Disable : RageshakeDetectionEvent
|
||||||
data object StartDetection : RageshakeDetectionEvents
|
data object StartDetection : RageshakeDetectionEvent
|
||||||
data object StopDetection : RageshakeDetectionEvents
|
data object StopDetection : RageshakeDetectionEvent
|
||||||
data class ProcessScreenshot(val imageResult: ImageResult) : RageshakeDetectionEvents
|
data class ProcessScreenshot(val imageResult: ImageResult) : RageshakeDetectionEvent
|
||||||
}
|
}
|
||||||
|
|
@ -15,5 +15,5 @@ data class RageshakeDetectionState(
|
||||||
val showDialog: Boolean,
|
val showDialog: Boolean,
|
||||||
val isStarted: Boolean,
|
val isStarted: Boolean,
|
||||||
val preferenceState: RageshakePreferencesState,
|
val preferenceState: RageshakePreferencesState,
|
||||||
val eventSink: (RageshakeDetectionEvents) -> Unit
|
val eventSink: (RageshakeDetectionEvent) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -35,22 +35,22 @@ fun RageshakeDetectionView(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
OnLifecycleEvent { _, event ->
|
OnLifecycleEvent { _, event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
Lifecycle.Event.ON_RESUME -> eventSink(RageshakeDetectionEvents.StartDetection)
|
Lifecycle.Event.ON_RESUME -> eventSink(RageshakeDetectionEvent.StartDetection)
|
||||||
Lifecycle.Event.ON_PAUSE -> eventSink(RageshakeDetectionEvents.StopDetection)
|
Lifecycle.Event.ON_PAUSE -> eventSink(RageshakeDetectionEvent.StopDetection)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
state.takeScreenshot -> TakeScreenshot(
|
state.takeScreenshot -> TakeScreenshot(
|
||||||
onScreenshot = { eventSink(RageshakeDetectionEvents.ProcessScreenshot(it)) }
|
onScreenshot = { eventSink(RageshakeDetectionEvent.ProcessScreenshot(it)) }
|
||||||
)
|
)
|
||||||
state.showDialog -> {
|
state.showDialog -> {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
context.vibrate()
|
context.vibrate()
|
||||||
}
|
}
|
||||||
RageshakeDialogContent(
|
RageshakeDialogContent(
|
||||||
onNoClick = { eventSink(RageshakeDetectionEvents.Dismiss) },
|
onNoClick = { eventSink(RageshakeDetectionEvent.Dismiss) },
|
||||||
onDisableClick = { eventSink(RageshakeDetectionEvents.Disable) },
|
onDisableClick = { eventSink(RageshakeDetectionEvent.Disable) },
|
||||||
onYesClick = onOpenBugReport
|
onYesClick = onOpenBugReport
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
package io.element.android.features.rageshake.api.preferences
|
package io.element.android.features.rageshake.api.preferences
|
||||||
|
|
||||||
sealed interface RageshakePreferencesEvents {
|
sealed interface RageshakePreferencesEvent {
|
||||||
data class SetSensitivity(val sensitivity: Float) : RageshakePreferencesEvents
|
data class SetSensitivity(val sensitivity: Float) : RageshakePreferencesEvent
|
||||||
data class SetIsEnabled(val isEnabled: Boolean) : RageshakePreferencesEvents
|
data class SetIsEnabled(val isEnabled: Boolean) : RageshakePreferencesEvent
|
||||||
}
|
}
|
||||||
|
|
@ -13,5 +13,5 @@ data class RageshakePreferencesState(
|
||||||
val isEnabled: Boolean,
|
val isEnabled: Boolean,
|
||||||
val isSupported: Boolean,
|
val isSupported: Boolean,
|
||||||
val sensitivity: Float,
|
val sensitivity: Float,
|
||||||
val eventSink: (RageshakePreferencesEvents) -> Unit,
|
val eventSink: (RageshakePreferencesEvent) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ fun aRageshakePreferencesState(
|
||||||
isEnabled: Boolean = false,
|
isEnabled: Boolean = false,
|
||||||
isSupported: Boolean = true,
|
isSupported: Boolean = true,
|
||||||
sensitivity: Float = 0.3f,
|
sensitivity: Float = 0.3f,
|
||||||
eventSink: (RageshakePreferencesEvents) -> Unit = {}
|
eventSink: (RageshakePreferencesEvent) -> Unit = {}
|
||||||
) = RageshakePreferencesState(
|
) = RageshakePreferencesState(
|
||||||
isFeatureEnabled = isFeatureEnabled,
|
isFeatureEnabled = isFeatureEnabled,
|
||||||
isEnabled = isEnabled,
|
isEnabled = isEnabled,
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ fun RageshakePreferencesView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
fun onSensitivityChanged(sensitivity: Float) {
|
fun onSensitivityChanged(sensitivity: Float) {
|
||||||
state.eventSink(RageshakePreferencesEvents.SetSensitivity(sensitivity = sensitivity))
|
state.eventSink(RageshakePreferencesEvent.SetSensitivity(sensitivity = sensitivity))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEnabledChanged(isEnabled: Boolean) {
|
fun onEnabledChanged(isEnabled: Boolean) {
|
||||||
state.eventSink(RageshakePreferencesEvents.SetIsEnabled(isEnabled = isEnabled))
|
state.eventSink(RageshakePreferencesEvent.SetIsEnabled(isEnabled = isEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.CachePolicy
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import io.element.android.features.rageshake.impl.R
|
import io.element.android.features.rageshake.impl.R
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
|
@ -135,6 +136,9 @@ fun BugReportView(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val model = ImageRequest.Builder(context)
|
val model = ImageRequest.Builder(context)
|
||||||
.data(state.screenshotUri)
|
.data(state.screenshotUri)
|
||||||
|
// Since `screenshotUri` always has the same value, we need to disable memory cache to
|
||||||
|
// ensure the image is reloaded when the URI content changes
|
||||||
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.build()
|
.build()
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
|
import io.element.android.features.rageshake.api.crash.CrashDetectionEvent
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
|
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionState
|
import io.element.android.features.rageshake.api.crash.CrashDetectionState
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
@ -48,10 +48,10 @@ class DefaultCrashDetectionPresenter(
|
||||||
}
|
}
|
||||||
}.collectAsState(false)
|
}.collectAsState(false)
|
||||||
|
|
||||||
fun handleEvent(event: CrashDetectionEvents) {
|
fun handleEvent(event: CrashDetectionEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
CrashDetectionEvents.ResetAllCrashData -> localCoroutineScope.resetAll()
|
CrashDetectionEvent.ResetAllCrashData -> localCoroutineScope.resetAll()
|
||||||
CrashDetectionEvents.ResetAppHasCrashed -> localCoroutineScope.resetAppHasCrashed()
|
CrashDetectionEvent.ResetAppHasCrashed -> localCoroutineScope.resetAppHasCrashed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,14 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
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 dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvent
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvent
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||||
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
||||||
import io.element.android.features.rageshake.impl.rageshake.RageShake
|
import io.element.android.features.rageshake.impl.rageshake.RageShake
|
||||||
|
|
@ -49,29 +48,19 @@ class DefaultRageshakeDetectionPresenter(
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvent(event: RageshakeDetectionEvents) {
|
fun handleEvent(event: RageshakeDetectionEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
RageshakeDetectionEvents.Disable -> {
|
RageshakeDetectionEvent.Disable -> {
|
||||||
preferencesState.eventSink(RageshakePreferencesEvents.SetIsEnabled(false))
|
preferencesState.eventSink(RageshakePreferencesEvent.SetIsEnabled(false))
|
||||||
showDialog.value = false
|
showDialog.value = false
|
||||||
}
|
}
|
||||||
RageshakeDetectionEvents.StartDetection -> isStarted.value = true
|
RageshakeDetectionEvent.StartDetection -> isStarted.value = true
|
||||||
RageshakeDetectionEvents.StopDetection -> isStarted.value = false
|
RageshakeDetectionEvent.StopDetection -> isStarted.value = false
|
||||||
is RageshakeDetectionEvents.ProcessScreenshot -> localCoroutineScope.processScreenshot(takeScreenshot, showDialog, event.imageResult)
|
is RageshakeDetectionEvent.ProcessScreenshot -> localCoroutineScope.processScreenshot(takeScreenshot, showDialog, event.imageResult)
|
||||||
RageshakeDetectionEvents.Dismiss -> showDialog.value = false
|
RageshakeDetectionEvent.Dismiss -> showDialog.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val state = remember(preferencesState, isStarted.value, takeScreenshot.value, showDialog.value) {
|
|
||||||
RageshakeDetectionState(
|
|
||||||
isStarted = isStarted.value,
|
|
||||||
takeScreenshot = takeScreenshot.value,
|
|
||||||
showDialog = showDialog.value,
|
|
||||||
preferenceState = preferencesState,
|
|
||||||
eventSink = ::handleEvent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(preferencesState.sensitivity) {
|
LaunchedEffect(preferencesState.sensitivity) {
|
||||||
rageShake.setSensitivity(preferencesState.sensitivity)
|
rageShake.setSensitivity(preferencesState.sensitivity)
|
||||||
}
|
}
|
||||||
|
|
@ -83,14 +72,25 @@ class DefaultRageshakeDetectionPresenter(
|
||||||
!showDialog.value
|
!showDialog.value
|
||||||
|
|
||||||
LaunchedEffect(shouldStart) {
|
LaunchedEffect(shouldStart) {
|
||||||
handleRageShake(shouldStart, state, takeScreenshot)
|
handleRageShake(
|
||||||
|
start = shouldStart,
|
||||||
|
sensitivity = preferencesState.sensitivity,
|
||||||
|
takeScreenshot = takeScreenshot,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return state
|
|
||||||
|
return RageshakeDetectionState(
|
||||||
|
isStarted = isStarted.value,
|
||||||
|
takeScreenshot = takeScreenshot.value,
|
||||||
|
showDialog = showDialog.value,
|
||||||
|
preferenceState = preferencesState,
|
||||||
|
eventSink = ::handleEvent,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRageShake(start: Boolean, state: RageshakeDetectionState, takeScreenshot: MutableState<Boolean>) {
|
private fun handleRageShake(start: Boolean, sensitivity: Float, takeScreenshot: MutableState<Boolean>) {
|
||||||
if (start) {
|
if (start) {
|
||||||
rageShake.start(state.preferenceState.sensitivity)
|
rageShake.start(sensitivity)
|
||||||
rageShake.setInterceptor {
|
rageShake.setInterceptor {
|
||||||
takeScreenshot.value = true
|
takeScreenshot.value = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvent
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||||
import io.element.android.features.rageshake.impl.rageshake.RageShake
|
import io.element.android.features.rageshake.impl.rageshake.RageShake
|
||||||
|
|
@ -48,10 +48,10 @@ class DefaultRageshakePreferencesPresenter(
|
||||||
rageshakeDataStore.sensitivity()
|
rageshakeDataStore.sensitivity()
|
||||||
}.collectAsState(initial = 0f)
|
}.collectAsState(initial = 0f)
|
||||||
|
|
||||||
fun handleEvent(event: RageshakePreferencesEvents) {
|
fun handleEvent(event: RageshakePreferencesEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is RageshakePreferencesEvents.SetIsEnabled -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
is RageshakePreferencesEvent.SetIsEnabled -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
||||||
is RageshakePreferencesEvents.SetSensitivity -> localCoroutineScope.setSensitivity(event.sensitivity)
|
is RageshakePreferencesEvent.SetSensitivity -> localCoroutineScope.setSensitivity(event.sensitivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@
|
||||||
|
|
||||||
package io.element.android.features.rageshake.impl.bugreport
|
package io.element.android.features.rageshake.impl.bugreport
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
|
||||||
import app.cash.molecule.moleculeFlow
|
|
||||||
import app.cash.turbine.test
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||||
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
|
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
|
||||||
|
|
@ -22,6 +19,7 @@ import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -37,9 +35,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasCrashLogs).isFalse()
|
assertThat(initialState.hasCrashLogs).isFalse()
|
||||||
assertThat(initialState.formState).isEqualTo(BugReportFormState.Default)
|
assertThat(initialState.formState).isEqualTo(BugReportFormState.Default)
|
||||||
|
|
@ -53,9 +49,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - set description`() = runTest {
|
fun `present - set description`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION))
|
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION))
|
||||||
assertThat(awaitItem().submitEnabled).isTrue()
|
assertThat(awaitItem().submitEnabled).isTrue()
|
||||||
|
|
@ -67,9 +61,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - can contact`() = runTest {
|
fun `present - can contact`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetCanContact(true))
|
initialState.eventSink.invoke(BugReportEvents.SetCanContact(true))
|
||||||
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = true))
|
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = true))
|
||||||
|
|
@ -81,9 +73,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - send logs`() = runTest {
|
fun `present - send logs`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
// Since this is true by default, start by disabling
|
// Since this is true by default, start by disabling
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetSendLog(false))
|
initialState.eventSink.invoke(BugReportEvents.SetSendLog(false))
|
||||||
|
|
@ -96,9 +86,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - send screenshot`() = runTest {
|
fun `present - send screenshot`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(true))
|
initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(true))
|
||||||
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = true))
|
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = true))
|
||||||
|
|
@ -110,9 +98,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - send notification settings`() = runTest {
|
fun `present - send notification settings`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(true))
|
initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(true))
|
||||||
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = true))
|
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = true))
|
||||||
|
|
@ -127,9 +113,7 @@ class BugReportPresenterTest {
|
||||||
crashDataStore = FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
crashDataStore = FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
||||||
screenshotHolder = FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
screenshotHolder = FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasCrashLogs).isTrue()
|
assertThat(initialState.hasCrashLogs).isTrue()
|
||||||
|
|
@ -148,9 +132,7 @@ class BugReportPresenterTest {
|
||||||
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
||||||
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
@ -174,9 +156,7 @@ class BugReportPresenterTest {
|
||||||
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
||||||
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
@ -200,9 +180,7 @@ class BugReportPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - send failure description too short`() = runTest {
|
fun `present - send failure description too short`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION))
|
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
@ -223,9 +201,7 @@ class BugReportPresenterTest {
|
||||||
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true),
|
||||||
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,15 @@
|
||||||
|
|
||||||
package io.element.android.features.rageshake.impl.crash.ui
|
package io.element.android.features.rageshake.impl.crash.ui
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
|
||||||
import app.cash.molecule.moleculeFlow
|
|
||||||
import app.cash.turbine.test
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
|
import io.element.android.features.rageshake.api.crash.CrashDetectionEvent
|
||||||
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
|
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
|
||||||
import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter
|
import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter
|
||||||
import io.element.android.features.rageshake.impl.crash.FakeCrashDataStore
|
import io.element.android.features.rageshake.impl.crash.FakeCrashDataStore
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
@ -33,9 +31,7 @@ class CrashDetectionPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state no crash`() = runTest {
|
fun `present - initial state no crash`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isFalse()
|
assertThat(initialState.crashDetected).isFalse()
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +42,7 @@ class CrashDetectionPresenterTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
FakeCrashDataStore(appHasCrashed = true)
|
FakeCrashDataStore(appHasCrashed = true)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isTrue()
|
assertThat(initialState.crashDetected).isTrue()
|
||||||
|
|
@ -61,9 +55,7 @@ class CrashDetectionPresenterTest {
|
||||||
FakeCrashDataStore(appHasCrashed = true),
|
FakeCrashDataStore(appHasCrashed = true),
|
||||||
isFeatureAvailableFlow = flowOf(false),
|
isFeatureAvailableFlow = flowOf(false),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isFalse()
|
assertThat(initialState.crashDetected).isFalse()
|
||||||
}
|
}
|
||||||
|
|
@ -74,13 +66,11 @@ class CrashDetectionPresenterTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
FakeCrashDataStore(appHasCrashed = true)
|
FakeCrashDataStore(appHasCrashed = true)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isTrue()
|
assertThat(initialState.crashDetected).isTrue()
|
||||||
initialState.eventSink.invoke(CrashDetectionEvents.ResetAppHasCrashed)
|
initialState.eventSink.invoke(CrashDetectionEvent.ResetAppHasCrashed)
|
||||||
assertThat(awaitItem().crashDetected).isFalse()
|
assertThat(awaitItem().crashDetected).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,13 +80,11 @@ class CrashDetectionPresenterTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA)
|
FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isTrue()
|
assertThat(initialState.crashDetected).isTrue()
|
||||||
initialState.eventSink.invoke(CrashDetectionEvents.ResetAllCrashData)
|
initialState.eventSink.invoke(CrashDetectionEvent.ResetAllCrashData)
|
||||||
assertThat(awaitItem().crashDetected).isFalse()
|
assertThat(awaitItem().crashDetected).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,9 +97,7 @@ class CrashDetectionPresenterTest {
|
||||||
crashDataStore = crashDataStore,
|
crashDataStore = crashDataStore,
|
||||||
isFeatureAvailableFlow = isFeatureAvailableFlow,
|
isFeatureAvailableFlow = isFeatureAvailableFlow,
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.crashDetected).isFalse()
|
assertThat(initialState.crashDetected).isFalse()
|
||||||
crashDataStore.setCrashData("Some crash data")
|
crashDataStore.setCrashData("Some crash data")
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,8 @@
|
||||||
package io.element.android.features.rageshake.impl.detection
|
package io.element.android.features.rageshake.impl.detection
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import app.cash.molecule.RecompositionMode
|
|
||||||
import app.cash.molecule.moleculeFlow
|
|
||||||
import app.cash.turbine.test
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvent
|
||||||
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
import io.element.android.features.rageshake.api.screenshot.ImageResult
|
||||||
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
||||||
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
|
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
|
||||||
|
|
@ -21,6 +18,7 @@ import io.element.android.features.rageshake.impl.rageshake.FakeRageshakeDataSto
|
||||||
import io.element.android.features.rageshake.impl.screenshot.FakeScreenshotHolder
|
import io.element.android.features.rageshake.impl.screenshot.FakeScreenshotHolder
|
||||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
@ -57,9 +55,7 @@ class RageshakeDetectionPresenterTest {
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.takeScreenshot).isFalse()
|
assertThat(initialState.takeScreenshot).isFalse()
|
||||||
|
|
@ -82,14 +78,12 @@ class RageshakeDetectionPresenterTest {
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.StartDetection)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.StartDetection)
|
||||||
assertThat(awaitItem().isStarted).isTrue()
|
assertThat(awaitItem().isStarted).isTrue()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.StopDetection)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.StopDetection)
|
||||||
assertThat(awaitItem().isStarted).isFalse()
|
assertThat(awaitItem().isStarted).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,21 +102,19 @@ class RageshakeDetectionPresenterTest {
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isStarted).isFalse()
|
assertThat(initialState.isStarted).isFalse()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.StartDetection)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.StartDetection)
|
||||||
assertThat(awaitItem().isStarted).isTrue()
|
assertThat(awaitItem().isStarted).isTrue()
|
||||||
rageshake.triggerPhoneRageshake()
|
rageshake.triggerPhoneRageshake()
|
||||||
assertThat(awaitItem().takeScreenshot).isTrue()
|
assertThat(awaitItem().takeScreenshot).isTrue()
|
||||||
initialState.eventSink.invoke(
|
initialState.eventSink.invoke(
|
||||||
RageshakeDetectionEvents.ProcessScreenshot(ImageResult.Success(aBitmap))
|
RageshakeDetectionEvent.ProcessScreenshot(ImageResult.Success(aBitmap))
|
||||||
)
|
)
|
||||||
assertThat(awaitItem().showDialog).isTrue()
|
assertThat(awaitItem().showDialog).isTrue()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.Dismiss)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.Dismiss)
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.showDialog).isFalse()
|
assertThat(finalState.showDialog).isFalse()
|
||||||
assertThat(rageshakeDataStore.isEnabled().first()).isTrue()
|
assertThat(rageshakeDataStore.isEnabled().first()).isTrue()
|
||||||
|
|
@ -143,21 +135,19 @@ class RageshakeDetectionPresenterTest {
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isStarted).isFalse()
|
assertThat(initialState.isStarted).isFalse()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.StartDetection)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.StartDetection)
|
||||||
assertThat(awaitItem().isStarted).isTrue()
|
assertThat(awaitItem().isStarted).isTrue()
|
||||||
rageshake.triggerPhoneRageshake()
|
rageshake.triggerPhoneRageshake()
|
||||||
assertThat(awaitItem().takeScreenshot).isTrue()
|
assertThat(awaitItem().takeScreenshot).isTrue()
|
||||||
initialState.eventSink.invoke(
|
initialState.eventSink.invoke(
|
||||||
RageshakeDetectionEvents.ProcessScreenshot(ImageResult.Error(AN_EXCEPTION))
|
RageshakeDetectionEvent.ProcessScreenshot(ImageResult.Error(AN_EXCEPTION))
|
||||||
)
|
)
|
||||||
assertThat(awaitItem().showDialog).isTrue()
|
assertThat(awaitItem().showDialog).isTrue()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.Dismiss)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.Dismiss)
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.showDialog).isFalse()
|
assertThat(finalState.showDialog).isFalse()
|
||||||
assertThat(rageshakeDataStore.isEnabled().first()).isTrue()
|
assertThat(rageshakeDataStore.isEnabled().first()).isTrue()
|
||||||
|
|
@ -178,21 +168,19 @@ class RageshakeDetectionPresenterTest {
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isStarted).isFalse()
|
assertThat(initialState.isStarted).isFalse()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.StartDetection)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.StartDetection)
|
||||||
assertThat(awaitItem().isStarted).isTrue()
|
assertThat(awaitItem().isStarted).isTrue()
|
||||||
rageshake.triggerPhoneRageshake()
|
rageshake.triggerPhoneRageshake()
|
||||||
assertThat(awaitItem().takeScreenshot).isTrue()
|
assertThat(awaitItem().takeScreenshot).isTrue()
|
||||||
initialState.eventSink.invoke(
|
initialState.eventSink.invoke(
|
||||||
RageshakeDetectionEvents.ProcessScreenshot(ImageResult.Success(aBitmap))
|
RageshakeDetectionEvent.ProcessScreenshot(ImageResult.Success(aBitmap))
|
||||||
)
|
)
|
||||||
assertThat(awaitItem().showDialog).isTrue()
|
assertThat(awaitItem().showDialog).isTrue()
|
||||||
initialState.eventSink.invoke(RageshakeDetectionEvents.Disable)
|
initialState.eventSink.invoke(RageshakeDetectionEvent.Disable)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
assertThat(awaitItem().showDialog).isFalse()
|
assertThat(awaitItem().showDialog).isFalse()
|
||||||
assertThat(rageshakeDataStore.isEnabled().first()).isFalse()
|
assertThat(rageshakeDataStore.isEnabled().first()).isFalse()
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,13 @@
|
||||||
|
|
||||||
package io.element.android.features.rageshake.impl.preferences
|
package io.element.android.features.rageshake.impl.preferences
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
|
||||||
import app.cash.molecule.moleculeFlow
|
|
||||||
import app.cash.turbine.test
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvent
|
||||||
import io.element.android.features.rageshake.impl.rageshake.A_SENSITIVITY
|
import io.element.android.features.rageshake.impl.rageshake.A_SENSITIVITY
|
||||||
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
|
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
|
||||||
import io.element.android.features.rageshake.impl.rageshake.FakeRageshakeDataStore
|
import io.element.android.features.rageshake.impl.rageshake.FakeRageshakeDataStore
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -33,9 +31,7 @@ class RageshakePreferencesPresenterTest {
|
||||||
FakeRageshakeDataStore(isEnabled = true),
|
FakeRageshakeDataStore(isEnabled = true),
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isSupported).isTrue()
|
assertThat(initialState.isSupported).isTrue()
|
||||||
|
|
@ -50,9 +46,7 @@ class RageshakePreferencesPresenterTest {
|
||||||
FakeRageshakeDataStore(isEnabled = true),
|
FakeRageshakeDataStore(isEnabled = true),
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isSupported).isFalse()
|
assertThat(initialState.isSupported).isFalse()
|
||||||
|
|
@ -67,15 +61,13 @@ class RageshakePreferencesPresenterTest {
|
||||||
FakeRageshakeDataStore(isEnabled = true),
|
FakeRageshakeDataStore(isEnabled = true),
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isEnabled).isTrue()
|
assertThat(initialState.isEnabled).isTrue()
|
||||||
initialState.eventSink.invoke(RageshakePreferencesEvents.SetIsEnabled(false))
|
initialState.eventSink.invoke(RageshakePreferencesEvent.SetIsEnabled(false))
|
||||||
assertThat(awaitItem().isEnabled).isFalse()
|
assertThat(awaitItem().isEnabled).isFalse()
|
||||||
initialState.eventSink.invoke(RageshakePreferencesEvents.SetIsEnabled(true))
|
initialState.eventSink.invoke(RageshakePreferencesEvent.SetIsEnabled(true))
|
||||||
assertThat(awaitItem().isEnabled).isTrue()
|
assertThat(awaitItem().isEnabled).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,13 +79,11 @@ class RageshakePreferencesPresenterTest {
|
||||||
FakeRageshakeDataStore(isEnabled = true),
|
FakeRageshakeDataStore(isEnabled = true),
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sensitivity).isEqualTo(A_SENSITIVITY)
|
assertThat(initialState.sensitivity).isEqualTo(A_SENSITIVITY)
|
||||||
initialState.eventSink.invoke(RageshakePreferencesEvents.SetSensitivity(A_SENSITIVITY + 1f))
|
initialState.eventSink.invoke(RageshakePreferencesEvent.SetSensitivity(A_SENSITIVITY + 1f))
|
||||||
assertThat(awaitItem().sensitivity).isEqualTo(A_SENSITIVITY + 1f)
|
assertThat(awaitItem().sensitivity).isEqualTo(A_SENSITIVITY + 1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue