Add notification troubleshoot test about blocked users.
This commit is contained in:
@@ -208,6 +208,10 @@ class PreferencesFlowNode(
|
||||
navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openIgnoredUsers() {
|
||||
backstack.push(NavTarget.BlockedUsers)
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -43,5 +43,6 @@ dependencies {
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.troubleshoot.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
||||
import io.element.android.libraries.permissions.impl.R
|
||||
import io.element.android.libraries.permissions.impl.action.PermissionActions
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
@@ -54,7 +55,10 @@ class NotificationTroubleshootCheckPermissionTest(
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
// Do not bother about asking the permission inline, just lead the user to the settings
|
||||
permissionActions.openSettings()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.permissions.impl.action.FakePermissionActions
|
||||
import io.element.android.libraries.permissions.test.FakePermissionStateProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
|
||||
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -84,7 +85,7 @@ class NotificationTroubleshootCheckPermissionTestTest {
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
launch {
|
||||
sut.quickFix(this)
|
||||
sut.quickFix(this, FakeNotificationTroubleshootNavigator())
|
||||
// Run the test again (IRL it will be done thanks to the resuming of the application)
|
||||
sut.run(this)
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ dependencies {
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.libraries.pushproviders.test)
|
||||
testImplementation(projects.libraries.pushstore.test)
|
||||
testImplementation(projects.libraries.troubleshoot.test)
|
||||
testImplementation(projects.features.call.test)
|
||||
testImplementation(projects.features.lockscreen.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import dev.zacsweers.metro.ContributesIntoSet
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
@ContributesIntoSet(SessionScope::class)
|
||||
@Inject
|
||||
class IgnoredUsersTest(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 80
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_description),
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val ignorerUsers = matrixClient.ignoredUsersFlow.value
|
||||
if (ignorerUsers.isEmpty()) {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_result_none),
|
||||
status = NotificationTroubleshootTestState.Status.Success,
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getQuantityString(
|
||||
R.plurals.troubleshoot_notifications_test_blocked_users_result_some,
|
||||
ignorerUsers.size,
|
||||
ignorerUsers.size
|
||||
),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(
|
||||
hasQuickFix = true,
|
||||
isCritical = false,
|
||||
quickFixButtonString = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_quick_fix),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
navigator.openIgnoredUsers()
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.push.api.gateway.PushGatewayFailure
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
@@ -99,7 +100,10 @@ class PushLoopbackTest(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
delegate.start()
|
||||
pushService.getCurrentPushProvider()?.rotateToken()
|
||||
run(coroutineScope)
|
||||
|
||||
@@ -54,6 +54,14 @@
|
||||
<string name="push_distributor_background_sync_android">"Background synchronization"</string>
|
||||
<string name="push_distributor_firebase_android">"Google Services"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"No valid Google Play Services found. Notifications may not work properly."</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_description">"Checking blocked users"</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_quick_fix">"View blocked users"</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_result_none">"No users are blocked."</string>
|
||||
<plurals name="troubleshoot_notifications_test_blocked_users_result_some">
|
||||
<item quantity="one">"You blocked %1$d user. You will not receive notifications for this user."</item>
|
||||
<item quantity="other">"You blocked %1$d users. You will not receive notifications for these users."</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_title">"Blocked users"</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Get the name of the current provider."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"No push providers selected."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Current push provider: %1$s."</string>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class IgnoredUsersTestTest {
|
||||
@Test
|
||||
fun `test IgnoredUsersTest order`() = runTest {
|
||||
val sut = IgnoredUsersTest(
|
||||
matrixClient = FakeMatrixClient(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
assertThat(sut.order).isEqualTo(80)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test IgnoredUsersTest quick fix`() = runTest {
|
||||
val sut = IgnoredUsersTest(
|
||||
matrixClient = FakeMatrixClient(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
val openIgnoredUsersResult = lambdaRecorder<Unit> {}
|
||||
val navigator = object : NotificationTroubleshootNavigator {
|
||||
override fun openIgnoredUsers() = openIgnoredUsersResult()
|
||||
}
|
||||
sut.quickFix(
|
||||
coroutineScope = backgroundScope,
|
||||
navigator = navigator,
|
||||
)
|
||||
openIgnoredUsersResult.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test IgnoredUsersTest with no blocked users`() = runTest {
|
||||
val sut = IgnoredUsersTest(
|
||||
matrixClient = FakeMatrixClient(
|
||||
ignoredUsersFlow = MutableStateFlow(persistentListOf())
|
||||
),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
backgroundScope.launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test IgnoredUsersTest with blocked users`() = runTest {
|
||||
val sut = IgnoredUsersTest(
|
||||
matrixClient = FakeMatrixClient(
|
||||
ignoredUsersFlow = MutableStateFlow(persistentListOf(A_USER_ID, A_USER_ID_2))
|
||||
),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
backgroundScope.launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
val lastStatus = lastItem.status as NotificationTroubleshootTestState.Status.Failure
|
||||
assertThat(lastStatus.hasQuickFix).isTrue()
|
||||
assertThat(lastStatus.isCritical).isFalse()
|
||||
assertThat(lastStatus.quickFixButtonString).isNotNull()
|
||||
assertThat(lastItem.description).contains("2")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import io.element.android.libraries.push.api.gateway.PushGatewayFailure
|
||||
import io.element.android.libraries.push.test.FakePushService
|
||||
import io.element.android.libraries.pushproviders.test.FakePushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
@@ -97,7 +98,7 @@ class PushLoopbackTestTest {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
sut.quickFix(this)
|
||||
sut.quickFix(this, FakeNotificationTroubleshootNavigator())
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
rotateTokenLambda.assertions().isCalledOnce()
|
||||
|
||||
@@ -76,5 +76,6 @@ dependencies {
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.libraries.pushstore.test)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.libraries.troubleshoot.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.element.android.libraries.pushproviders.firebase.FirebaseConfig
|
||||
import io.element.android.libraries.pushproviders.firebase.FirebaseStore
|
||||
import io.element.android.libraries.pushproviders.firebase.FirebaseTroubleshooter
|
||||
import io.element.android.libraries.pushproviders.firebase.R
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
@@ -71,7 +72,10 @@ class FirebaseTokenTest(
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
delegate.start()
|
||||
firebaseTroubleshooter.troubleshoot()
|
||||
run(coroutineScope)
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.element.android.libraries.pushproviders.firebase.FirebaseConfig
|
||||
import io.element.android.libraries.pushproviders.firebase.InMemoryFirebaseStore
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
|
||||
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -62,7 +63,7 @@ class FirebaseTokenTestTest {
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
sut.quickFix(this)
|
||||
sut.quickFix(this, FakeNotificationTroubleshootNavigator())
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ dependencies {
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.libraries.pushproviders.test)
|
||||
testImplementation(projects.libraries.pushstore.test)
|
||||
testImplementation(projects.libraries.troubleshoot.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.R
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushDistributorProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
@@ -64,7 +65,10 @@ class UnifiedPushTest(
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
openDistributorWebPageAction.execute()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
|
||||
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -67,7 +68,7 @@ class UnifiedPushTestTest {
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
launch {
|
||||
sut.quickFix(this)
|
||||
sut.quickFix(this, FakeNotificationTroubleshootNavigator())
|
||||
sut.run(this)
|
||||
}
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
|
||||
@@ -22,5 +22,6 @@ interface NotificationTroubleShootEntryPoint : FeatureEntryPoint {
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onDone()
|
||||
fun openIgnoredUsers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
interface NotificationTroubleshootNavigator {
|
||||
fun openIgnoredUsers()
|
||||
}
|
||||
@@ -16,7 +16,10 @@ interface NotificationTroubleshootTest {
|
||||
fun isRelevant(data: TestFilterData): Boolean = true
|
||||
suspend fun run(coroutineScope: CoroutineScope)
|
||||
suspend fun reset()
|
||||
suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
error("Quick fix not implemented, you need to override this method in your test")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ data class NotificationTroubleshootTestState(
|
||||
data object InProgress : Status
|
||||
data object WaitingForUser : Status
|
||||
data object Success : Status
|
||||
data class Failure(val hasQuickFix: Boolean) : Status
|
||||
data class Failure(
|
||||
val hasQuickFix: Boolean,
|
||||
val isCritical: Boolean = true,
|
||||
val quickFixButtonString: String? = null,
|
||||
) : Status
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.services.analytics.api.ScreenTracker
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@@ -26,15 +27,26 @@ import io.element.android.services.analytics.api.ScreenTracker
|
||||
class TroubleshootNotificationsNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: TroubleshootNotificationsPresenter,
|
||||
private val screenTracker: ScreenTracker,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
factory: TroubleshootNotificationsPresenter.Factory,
|
||||
) : Node(buildContext, plugins = plugins),
|
||||
NotificationTroubleshootNavigator {
|
||||
private val presenter = factory.create(
|
||||
navigator = this,
|
||||
)
|
||||
|
||||
private fun onDone() {
|
||||
plugins<NotificationTroubleShootEntryPoint.Callback>().forEach {
|
||||
it.onDone()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openIgnoredUsers() {
|
||||
plugins<NotificationTroubleShootEntryPoint.Callback>().forEach {
|
||||
it.openIgnoredUsers()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
screenTracker.TrackScreen(MobileScreen.ScreenName.NotificationTroubleshoot)
|
||||
|
||||
@@ -12,14 +12,23 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class TroubleshootNotificationsPresenter(
|
||||
@Assisted private val navigator: NotificationTroubleshootNavigator,
|
||||
private val troubleshootTestSuite: TroubleshootTestSuite,
|
||||
) : Presenter<TroubleshootNotificationsState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(navigator: NotificationTroubleshootNavigator): TroubleshootNotificationsPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): TroubleshootNotificationsState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@@ -34,7 +43,11 @@ class TroubleshootNotificationsPresenter(
|
||||
troubleshootTestSuite.runTestSuite(this)
|
||||
}
|
||||
is TroubleshootNotificationsEvents.QuickFix -> coroutineScope.launch {
|
||||
troubleshootTestSuite.quickFix(event.testIndex, this)
|
||||
troubleshootTestSuite.quickFix(
|
||||
testIndex = event.testIndex,
|
||||
coroutineScope = this,
|
||||
navigator = navigator,
|
||||
)
|
||||
}
|
||||
TroubleshootNotificationsEvents.RetryFailedTests -> coroutineScope.launch {
|
||||
troubleshootTestSuite.retryFailedTest(this)
|
||||
|
||||
@@ -31,8 +31,12 @@ open class TroubleshootNotificationsStateProvider : PreviewParameterProvider<Tro
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateFailure(
|
||||
isCritical = false,
|
||||
hasQuickFix = true,
|
||||
quickFixButtonString = "Custom quick fix",
|
||||
),
|
||||
aTroubleshootTestStateInProgress(),
|
||||
aTroubleshootTestStateIdle(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
@@ -106,5 +110,14 @@ fun aTroubleshootTestStateWaitingForUser() =
|
||||
fun aTroubleshootTestStateSuccess() =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.Success)
|
||||
|
||||
fun aTroubleshootTestStateFailure(hasQuickFix: Boolean) =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.Failure(hasQuickFix = hasQuickFix))
|
||||
fun aTroubleshootTestStateFailure(
|
||||
hasQuickFix: Boolean = false,
|
||||
isCritical: Boolean = true,
|
||||
quickFixButtonString: String? = null,
|
||||
) = aTroubleshootTestState(
|
||||
status = NotificationTroubleshootTestState.Status.Failure(
|
||||
hasQuickFix = hasQuickFix,
|
||||
isCritical = isCritical,
|
||||
quickFixButtonString = quickFixButtonString,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -64,11 +64,12 @@ private fun ColumnScope.TroubleshootTestView(
|
||||
testState: NotificationTroubleshootTestState,
|
||||
onQuickFixClick: () -> Unit,
|
||||
) {
|
||||
if ((testState.status as? Status.Idle)?.visible == false) return
|
||||
val status = testState.status
|
||||
if ((status as? Status.Idle)?.visible == false) return
|
||||
ListItem(
|
||||
headlineContent = { Text(text = testState.name) },
|
||||
supportingContent = { Text(text = testState.description) },
|
||||
trailingContent = when (testState.status) {
|
||||
trailingContent = when (status) {
|
||||
is Status.Idle -> null
|
||||
Status.InProgress -> ListItemContent.Custom {
|
||||
CircularProgressIndicator(
|
||||
@@ -98,20 +99,19 @@ private fun ColumnScope.TroubleshootTestView(
|
||||
Icon(
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = CompoundIcons.ErrorSolid(),
|
||||
tint = ElementTheme.colors.textCriticalPrimary
|
||||
imageVector = if (status.isCritical) CompoundIcons.ErrorSolid() else CompoundIcons.Warning(),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
if ((testState.status as? Status.Failure)?.hasQuickFix == true) {
|
||||
if (status is Status.Failure && status.hasQuickFix) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
},
|
||||
headlineContent = { },
|
||||
trailingContent = ListItemContent.Custom {
|
||||
Button(
|
||||
text = stringResource(id = R.string.troubleshoot_notifications_screen_quick_fix_action),
|
||||
onClick = onQuickFixClick
|
||||
text = status.quickFixButtonString ?: stringResource(id = R.string.troubleshoot_notifications_screen_quick_fix_action),
|
||||
onClick = onQuickFixClick,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ import dev.zacsweers.metro.Inject
|
||||
import im.vector.app.features.analytics.plan.NotificationTroubleshoot
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
|
||||
@@ -90,8 +91,12 @@ class TroubleshootTestSuite(
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun quickFix(testIndex: Int, coroutineScope: CoroutineScope) {
|
||||
tests[testIndex].quickFix(coroutineScope)
|
||||
suspend fun quickFix(
|
||||
testIndex: Int,
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
tests[testIndex].quickFix(coroutineScope, navigator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +109,7 @@ fun List<NotificationTroubleshootTestState>.computeMainState(): AsyncAction<Unit
|
||||
else -> {
|
||||
if (any { it.status is NotificationTroubleshootTestState.Status.WaitingForUser }) {
|
||||
AsyncAction.ConfirmingNoParams
|
||||
} else if (any { it.status is NotificationTroubleshootTestState.Status.Failure }) {
|
||||
} else if (any { it.status.let { status -> status is NotificationTroubleshootTestState.Status.Failure && status.isCritical } }) {
|
||||
AsyncAction.Failure(Exception("Some tests failed"))
|
||||
} else {
|
||||
AsyncAction.Success(Unit)
|
||||
|
||||
@@ -28,12 +28,13 @@ class DefaultNotificationTroubleShootEntryPointTest {
|
||||
TroubleshootNotificationsNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenter = createTroubleshootNotificationsPresenter(),
|
||||
factory = { createTroubleshootNotificationsPresenter() },
|
||||
screenTracker = FakeScreenTracker(),
|
||||
)
|
||||
}
|
||||
val callback = object : NotificationTroubleShootEntryPoint.Callback {
|
||||
override fun onDone() = lambdaError()
|
||||
override fun openIgnoredUsers() = lambdaError()
|
||||
}
|
||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
||||
.callback(callback)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -50,7 +51,10 @@ class FakeNotificationTroubleshootTest(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
updateState(NotificationTroubleshootTestState.Status.InProgress)
|
||||
quickFixAction()?.let {
|
||||
_state.emit(it)
|
||||
|
||||
@@ -13,9 +13,11 @@ import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.push.test.FakeGetCurrentPushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@@ -111,9 +113,13 @@ private fun createTroubleshootTestSuite(
|
||||
}
|
||||
|
||||
internal fun createTroubleshootNotificationsPresenter(
|
||||
navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator {
|
||||
override fun openIgnoredUsers() = lambdaError()
|
||||
},
|
||||
troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(),
|
||||
): TroubleshootNotificationsPresenter {
|
||||
return TroubleshootNotificationsPresenter(
|
||||
navigator = navigator,
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
}
|
||||
|
||||
18
libraries/troubleshoot/test/build.gradle.kts
Normal file
18
libraries/troubleshoot/test/build.gradle.kts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.troubleshoot.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.tests.testutils)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.test
|
||||
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeNotificationTroubleshootNavigator(
|
||||
private val openIgnoredUsersResult: () -> Unit = { lambdaError() },
|
||||
) : NotificationTroubleshootNavigator {
|
||||
override fun openIgnoredUsers() = openIgnoredUsersResult()
|
||||
}
|
||||
@@ -123,6 +123,7 @@
|
||||
"includeRegex" : [
|
||||
"push_.*",
|
||||
"notification_.*",
|
||||
"troubleshoot_notifications\\.test_blocked_users\\..*",
|
||||
"troubleshoot_notifications_test_current_push_provider.*",
|
||||
"troubleshoot_notifications_test_detect_push_provider.*",
|
||||
"troubleshoot_notifications_test_display_notification_.*",
|
||||
|
||||
Reference in New Issue
Block a user