Troubleshoot notifications screen
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.api
|
||||
|
||||
data class CurrentUserPushConfig(
|
||||
val url: String,
|
||||
val pushKey: String,
|
||||
)
|
||||
@@ -49,8 +49,5 @@ interface PushProvider {
|
||||
*/
|
||||
suspend fun unregister(matrixClient: MatrixClient)
|
||||
|
||||
/**
|
||||
* Attempt to troubleshoot the push provider.
|
||||
*/
|
||||
suspend fun troubleshoot(): Result<Unit>
|
||||
suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig?
|
||||
}
|
||||
|
||||
@@ -51,8 +51,10 @@ dependencies {
|
||||
exclude(group = "com.google.firebase", module = "firebase-measurement-connector")
|
||||
}
|
||||
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
}
|
||||
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.libraries.pushproviders.api.PusherSubscriber
|
||||
@@ -34,25 +31,15 @@ private val loggerTag = LoggerTag("FirebasePushProvider", LoggerTag.PushLoggerTa
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class FirebasePushProvider @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val firebaseStore: FirebaseStore,
|
||||
private val firebaseTroubleshooter: FirebaseTroubleshooter,
|
||||
private val pusherSubscriber: PusherSubscriber,
|
||||
private val isPlayServiceAvailable: IsPlayServiceAvailable,
|
||||
) : PushProvider {
|
||||
override val index = FirebaseConfig.INDEX
|
||||
override val name = FirebaseConfig.NAME
|
||||
|
||||
override fun isAvailable(): Boolean {
|
||||
// The PlayServices has to be available
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||
return if (resultCode == ConnectionResult.SUCCESS) {
|
||||
Timber.tag(loggerTag.value).d("Google Play Services is available")
|
||||
true
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).w("Google Play Services is not available")
|
||||
false
|
||||
}
|
||||
return isPlayServiceAvailable.isAvailable()
|
||||
}
|
||||
|
||||
override fun getDistributors(): List<Distributor> {
|
||||
@@ -73,7 +60,12 @@ class FirebasePushProvider @Inject constructor(
|
||||
pusherSubscriber.unregisterPusher(matrixClient, pushKey, FirebaseConfig.PUSHER_HTTP_URL)
|
||||
}
|
||||
|
||||
override suspend fun troubleshoot(): Result<Unit> {
|
||||
return firebaseTroubleshooter.troubleshoot()
|
||||
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? {
|
||||
return firebaseStore.getFcmToken()?.let { fcmToken ->
|
||||
CurrentUserPushConfig(
|
||||
url = FirebaseConfig.PUSHER_HTTP_URL,
|
||||
pushKey = fcmToken
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,28 @@ package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.DefaultPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* This class store the Firebase token in SharedPrefs.
|
||||
*/
|
||||
class FirebaseStore @Inject constructor(
|
||||
interface FirebaseStore {
|
||||
fun getFcmToken(): String?
|
||||
fun storeFcmToken(token: String?)
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultFirebaseStore @Inject constructor(
|
||||
@DefaultPreferences private val sharedPrefs: SharedPreferences,
|
||||
) {
|
||||
fun getFcmToken(): String? {
|
||||
) : FirebaseStore {
|
||||
override fun getFcmToken(): String? {
|
||||
return sharedPrefs.getString(PREFS_KEY_FCM_TOKEN, null)
|
||||
}
|
||||
|
||||
fun storeFcmToken(token: String?) {
|
||||
override fun storeFcmToken(token: String?) {
|
||||
sharedPrefs.edit {
|
||||
putString(PREFS_KEY_FCM_TOKEN, token)
|
||||
}
|
||||
|
||||
@@ -16,25 +16,28 @@
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
interface FirebaseTroubleshooter {
|
||||
suspend fun troubleshoot(): Result<Unit>
|
||||
}
|
||||
|
||||
/**
|
||||
* This class force retrieving and storage of the Firebase token.
|
||||
*/
|
||||
class FirebaseTroubleshooter @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultFirebaseTroubleshooter @Inject constructor(
|
||||
private val newTokenHandler: FirebaseNewTokenHandler,
|
||||
) {
|
||||
suspend fun troubleshoot(): Result<Unit> {
|
||||
private val isPlayServiceAvailable: IsPlayServiceAvailable,
|
||||
) : FirebaseTroubleshooter {
|
||||
override suspend fun troubleshoot(): Result<Unit> {
|
||||
return runCatching {
|
||||
val token = retrievedFirebaseToken()
|
||||
newTokenHandler.handle(token)
|
||||
@@ -44,7 +47,7 @@ class FirebaseTroubleshooter @Inject constructor(
|
||||
private suspend fun retrievedFirebaseToken(): String {
|
||||
return suspendCoroutine { continuation ->
|
||||
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
|
||||
if (checkPlayServices(context)) {
|
||||
if (isPlayServiceAvailable.isAvailable()) {
|
||||
try {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnSuccessListener { token ->
|
||||
@@ -65,15 +68,4 @@ class FirebaseTroubleshooter @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the device to make sure it has the Google Play Services APK. If
|
||||
* it doesn't, display a dialog that allows users to download the APK from
|
||||
* the Google Play Store or enable it in the device's system settings.
|
||||
*/
|
||||
private fun checkPlayServices(context: Context): Boolean {
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||
return resultCode == ConnectionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
interface IsPlayServiceAvailable {
|
||||
fun isAvailable(): Boolean
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultIsPlayServiceAvailable @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : IsPlayServiceAvailable {
|
||||
override fun isAvailable(): Boolean {
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||
return if (resultCode == ConnectionResult.SUCCESS) {
|
||||
Timber.d("Google Play Services is available")
|
||||
true
|
||||
} else {
|
||||
Timber.w("Google Play Services is not available")
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.core.notifications.TestFilterData
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.pushproviders.firebase.FirebaseConfig
|
||||
import io.element.android.libraries.pushproviders.firebase.IsPlayServiceAvailable
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class FirebaseAvailabilityTest @Inject constructor(
|
||||
private val isPlayServiceAvailable: IsPlayServiceAvailable,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 300
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = "Check Firebase",
|
||||
defaultDescription = "Ensure that Firebase is available.",
|
||||
visibleWhenIdle = false,
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.LONG_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override fun isRelevant(data: TestFilterData): Boolean {
|
||||
return data.currentPushProviderName == FirebaseConfig.NAME
|
||||
}
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val result = isPlayServiceAvailable.isAvailable()
|
||||
if (result) {
|
||||
delegate.updateState(
|
||||
description = "Firebase is available",
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = "Firebase is not available",
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() = delegate.reset()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.core.notifications.TestFilterData
|
||||
import io.element.android.libraries.di.AppScope
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class FirebaseTokenTest @Inject constructor(
|
||||
private val firebaseStore: FirebaseStore,
|
||||
private val firebaseTroubleshooter: FirebaseTroubleshooter,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 310
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = "Check Firebase token",
|
||||
defaultDescription = "Ensure that Firebase token is available.",
|
||||
visibleWhenIdle = false,
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.LONG_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override fun isRelevant(data: TestFilterData): Boolean {
|
||||
return data.currentPushProviderName == FirebaseConfig.NAME
|
||||
}
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val token = firebaseStore.getFcmToken()
|
||||
if (token != null) {
|
||||
delegate.updateState(
|
||||
description = "Firebase token: ${token.take(8)}*****",
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = "Firebase token is not known",
|
||||
status = NotificationTroubleshootTestState.Status.Failure(true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
firebaseTroubleshooter.troubleshoot()
|
||||
run(coroutineScope)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeFirebaseTroubleshooter(
|
||||
private val troubleShootResult: () -> Result<Unit> = { Result.success(Unit) }
|
||||
) : FirebaseTroubleshooter {
|
||||
override suspend fun troubleshoot(): Result<Unit> = simulateLongTask {
|
||||
troubleShootResult()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase
|
||||
|
||||
class InMemoryFirebaseStore(
|
||||
private var token: String? = null
|
||||
) : FirebaseStore {
|
||||
override fun getFcmToken(): String? = token
|
||||
|
||||
override fun storeFcmToken(token: String?) {
|
||||
this.token = token
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.pushproviders.firebase.IsPlayServiceAvailable
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class FirebaseAvailabilityTestTest {
|
||||
@Test
|
||||
fun `test FirebaseAvailabilityTest success`() = runTest {
|
||||
val sut = FirebaseAvailabilityTest(
|
||||
isPlayServiceAvailable = object : IsPlayServiceAvailable {
|
||||
override fun isAvailable(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test FirebaseAvailabilityTest failure`() = runTest {
|
||||
val sut = FirebaseAvailabilityTest(
|
||||
isPlayServiceAvailable = object : IsPlayServiceAvailable {
|
||||
override fun isAvailable(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.firebase.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.pushproviders.firebase.FakeFirebaseTroubleshooter
|
||||
import io.element.android.libraries.pushproviders.firebase.InMemoryFirebaseStore
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class FirebaseTokenTestTest {
|
||||
@Test
|
||||
fun `test FirebaseTokenTest success`() = runTest {
|
||||
val sut = FirebaseTokenTest(
|
||||
firebaseStore = InMemoryFirebaseStore(FAKE_TOKEN),
|
||||
firebaseTroubleshooter = FakeFirebaseTroubleshooter(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
assertThat(lastItem.description).contains(FAKE_TOKEN.take(8))
|
||||
assertThat(lastItem.description).doesNotContain(FAKE_TOKEN)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test FirebaseTokenTest error`() = runTest {
|
||||
val firebaseStore = InMemoryFirebaseStore(null)
|
||||
val sut = FirebaseTokenTest(
|
||||
firebaseStore = firebaseStore,
|
||||
firebaseTroubleshooter = FakeFirebaseTroubleshooter(
|
||||
troubleShootResult = {
|
||||
firebaseStore.storeFcmToken(FAKE_TOKEN)
|
||||
Result.success(Unit)
|
||||
}
|
||||
),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
sut.quickFix(this)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FAKE_TOKEN = "abcdefghijk"
|
||||
}
|
||||
}
|
||||
27
libraries/pushproviders/test/build.gradle.kts
Normal file
27
libraries/pushproviders/test/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.pushproviders.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.pushproviders.api)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
|
||||
class FakePushProvider(
|
||||
override val index: Int = 0,
|
||||
override val name: String = "aFakePushProvider",
|
||||
private val isAvailable: Boolean = true,
|
||||
private val distributors: List<Distributor> = emptyList()
|
||||
) : PushProvider {
|
||||
override fun isAvailable(): Boolean = isAvailable
|
||||
|
||||
override fun getDistributors(): List<Distributor> = distributors
|
||||
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
override suspend fun unregister(matrixClient: MatrixClient) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ dependencies {
|
||||
implementation(projects.libraries.pushproviders.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.services.appnavstate.api)
|
||||
implementation(projects.services.toolbox.api)
|
||||
|
||||
implementation(projects.libraries.network)
|
||||
@@ -50,8 +51,10 @@ dependencies {
|
||||
// UnifiedPush library
|
||||
api(libs.unifiedpush)
|
||||
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.system.getApplicationLabel
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import org.unifiedpush.android.connector.UnifiedPush
|
||||
import javax.inject.Inject
|
||||
|
||||
interface UnifiedPushDistributorProvider {
|
||||
fun getDistributors(): List<Distributor>
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultUnifiedPushDistributorProvider @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : UnifiedPushDistributorProvider {
|
||||
override fun getDistributors(): List<Distributor> {
|
||||
val distributors = UnifiedPush.getDistributors(context)
|
||||
return distributors.mapNotNull {
|
||||
if (it == context.packageName) {
|
||||
// Exclude self
|
||||
null
|
||||
} else {
|
||||
Distributor(it, context.getApplicationLabel(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.androidutils.system.getApplicationLabel
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
import org.unifiedpush.android.connector.UnifiedPush
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.currentSessionId
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -34,10 +33,12 @@ private val loggerTag = LoggerTag("UnifiedPushProvider", LoggerTag.PushLoggerTag
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class UnifiedPushProvider @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val unifiedPushDistributorProvider: UnifiedPushDistributorProvider,
|
||||
private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase,
|
||||
private val unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
) : PushProvider {
|
||||
override val index = UnifiedPushConfig.INDEX
|
||||
override val name = UnifiedPushConfig.NAME
|
||||
@@ -54,15 +55,7 @@ class UnifiedPushProvider @Inject constructor(
|
||||
}
|
||||
|
||||
override fun getDistributors(): List<Distributor> {
|
||||
val distributors = UnifiedPush.getDistributors(context)
|
||||
return distributors.mapNotNull {
|
||||
if (it == context.packageName) {
|
||||
// Exclude self
|
||||
null
|
||||
} else {
|
||||
Distributor(it, context.getApplicationLabel(it))
|
||||
}
|
||||
}
|
||||
return unifiedPushDistributorProvider.getDistributors()
|
||||
}
|
||||
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor) {
|
||||
@@ -75,7 +68,14 @@ class UnifiedPushProvider @Inject constructor(
|
||||
unRegisterUnifiedPushUseCase.execute(clientSecret)
|
||||
}
|
||||
|
||||
override suspend fun troubleshoot(): Result<Unit> {
|
||||
TODO("Not yet implemented")
|
||||
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? {
|
||||
val currentSession = appNavigationStateService.appNavigationState.value.navigationState.currentSessionId() ?: return null
|
||||
val clientSecret = pushClientSecret.getSecretForUser(currentSession)
|
||||
val url = unifiedPushStore.getPushGateway(clientSecret) ?: return null
|
||||
val pushKey = unifiedPushStore.getEndpoint(clientSecret) ?: return null
|
||||
return CurrentUserPushConfig(
|
||||
url = url,
|
||||
pushKey = pushKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
|
||||
interface OpenDistributorWebPageAction {
|
||||
fun execute()
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultOpenDistributorWebPageAction @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : OpenDistributorWebPageAction {
|
||||
override fun execute() {
|
||||
// Open the distributor download page
|
||||
context.openUrlInExternalApp(
|
||||
url = "https://unifiedpush.org/users/distributors/",
|
||||
inNewTask = true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.core.notifications.TestFilterData
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushDistributorProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class UnifiedPushTest @Inject constructor(
|
||||
private val unifiedPushDistributorProvider: UnifiedPushDistributorProvider,
|
||||
private val openDistributorWebPageAction: OpenDistributorWebPageAction,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 400
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = "Check UnifiedPush",
|
||||
defaultDescription = "Ensure that UnifiedPush distributors are available.",
|
||||
visibleWhenIdle = false,
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override fun isRelevant(data: TestFilterData): Boolean {
|
||||
return data.currentPushProviderName == UnifiedPushConfig.NAME
|
||||
}
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val distributors = unifiedPushDistributorProvider.getDistributors()
|
||||
if (distributors.isNotEmpty()) {
|
||||
delegate.updateState(
|
||||
description = "Distributors found: ${distributors.joinToString { it.name }}",
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = "No push distributors found",
|
||||
status = NotificationTroubleshootTestState.Status.Failure(true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
openDistributorWebPageAction.execute()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
class FakeOpenDistributorWebPageAction(
|
||||
private val executeAction: () -> Unit = {}
|
||||
) : OpenDistributorWebPageAction {
|
||||
override fun execute() = executeAction()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushDistributorProvider
|
||||
|
||||
class FakeUnifiedPushDistributorProvider(
|
||||
private var getDistributorsResult: List<Distributor> = emptyList()
|
||||
) : UnifiedPushDistributorProvider {
|
||||
override fun getDistributors(): List<Distributor> {
|
||||
return getDistributorsResult
|
||||
}
|
||||
|
||||
fun setDistributorsResult(list: List<Distributor>) {
|
||||
getDistributorsResult = list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class UnifiedPushTestTest {
|
||||
@Test
|
||||
fun `test UnifiedPushTest success`() = runTest {
|
||||
val sut = UnifiedPushTest(
|
||||
unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider(
|
||||
getDistributorsResult = listOf(
|
||||
Distributor("value", "Name"),
|
||||
)
|
||||
),
|
||||
openDistributorWebPageAction = FakeOpenDistributorWebPageAction(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test UnifiedPushTest error`() = runTest {
|
||||
val providers = FakeUnifiedPushDistributorProvider()
|
||||
val sut = UnifiedPushTest(
|
||||
unifiedPushDistributorProvider = providers,
|
||||
openDistributorWebPageAction = FakeOpenDistributorWebPageAction(
|
||||
executeAction = {
|
||||
providers.setDistributorsResult(
|
||||
listOf(
|
||||
Distributor("value", "Name"),
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
launch {
|
||||
sut.quickFix(this)
|
||||
sut.run(this)
|
||||
}
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user