mirror of
https://github.com/ouchadam/small-talk.git
synced 2024-12-31 20:07:35 +01:00
adding tests around notification rendering
This commit is contained in:
parent
4861c44e9c
commit
e1029dc497
@ -174,6 +174,7 @@ internal class FeatureModules internal constructor(
|
||||
context,
|
||||
workModule.workScheduler(),
|
||||
intentFactory = coreAndroidModule.intentFactory(),
|
||||
dispatchers = coroutineDispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,11 @@ package app.dapk.st.core
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
data class CoroutineDispatchers(val io: CoroutineDispatcher = Dispatchers.IO, val global: CoroutineScope = GlobalScope)
|
||||
data class CoroutineDispatchers(
|
||||
val io: CoroutineDispatcher = Dispatchers.IO,
|
||||
val main: CoroutineDispatcher = Dispatchers.Main,
|
||||
val global: CoroutineScope = GlobalScope,
|
||||
)
|
||||
|
||||
suspend fun <T> CoroutineDispatchers.withIoContext(
|
||||
block: suspend CoroutineScope.() -> T
|
||||
|
@ -0,0 +1,14 @@
|
||||
package fixture
|
||||
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
object CoroutineDispatchersFixture {
|
||||
|
||||
fun aCoroutineDispatchers() = CoroutineDispatchers(
|
||||
Dispatchers.Unconfined,
|
||||
main = Dispatchers.Unconfined,
|
||||
global = CoroutineScope(Dispatchers.Unconfined)
|
||||
)
|
||||
}
|
@ -12,7 +12,6 @@ fun runExpectTest(testBody: suspend ExpectTestScope.() -> Unit) {
|
||||
runTest { testBody(ExpectTest(coroutineContext)) }
|
||||
}
|
||||
|
||||
|
||||
class ExpectTest(override val coroutineContext: CoroutineContext) : ExpectTestScope {
|
||||
|
||||
private val expects = mutableListOf<Pair<Int, suspend MockKVerificationScope.() -> Unit>>()
|
||||
|
@ -0,0 +1,12 @@
|
||||
package fake
|
||||
|
||||
import android.app.Notification
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeNotification {
|
||||
|
||||
val instance = mockk<Notification>()
|
||||
|
||||
}
|
||||
|
||||
fun aFakeNotification() = FakeNotification().instance
|
@ -0,0 +1,14 @@
|
||||
package fake
|
||||
|
||||
import android.app.NotificationManager
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
|
||||
class FakeNotificationManager {
|
||||
|
||||
val instance = mockk<NotificationManager>()
|
||||
|
||||
fun verifyCancelled(tag: String, id: Int) {
|
||||
verify { instance.cancel(tag, id) }
|
||||
}
|
||||
}
|
@ -20,4 +20,5 @@ dependencies {
|
||||
androidImportFixturesWorkaround(project, project(":core"))
|
||||
androidImportFixturesWorkaround(project, project(":matrix:common"))
|
||||
androidImportFixturesWorkaround(project, project(":matrix:services:sync"))
|
||||
androidImportFixturesWorkaround(project, project(":domains:android:stub"))
|
||||
}
|
@ -3,6 +3,7 @@ package app.dapk.st.notifications
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import app.dapk.st.core.AppLogTag
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.extensions.ifNull
|
||||
import app.dapk.st.core.log
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
@ -17,13 +18,14 @@ private const val MESSAGE_NOTIFICATION_ID = 100
|
||||
class NotificationRenderer(
|
||||
private val notificationManager: NotificationManager,
|
||||
private val notificationFactory: NotificationFactory,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
|
||||
suspend fun render(allUnread: Map<RoomOverview, List<RoomEvent>>, removedRooms: Set<RoomId>, roomsWithNewEvents: Set<RoomId>, newRooms: Set<RoomId>) {
|
||||
removedRooms.forEach { notificationManager.cancel(it.value, MESSAGE_NOTIFICATION_ID) }
|
||||
val notifications = notificationFactory.createNotifications(allUnread, roomsWithNewEvents, newRooms)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
withContext(dispatchers.main) {
|
||||
notifications.summaryNotification.ifNull {
|
||||
log(AppLogTag.NOTIFICATION, "cancelling summary")
|
||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
||||
|
@ -2,6 +2,7 @@ package app.dapk.st.notifications
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
import app.dapk.st.imageloader.IconLoader
|
||||
import app.dapk.st.matrix.common.CredentialsStore
|
||||
@ -22,6 +23,7 @@ class NotificationsModule(
|
||||
private val context: Context,
|
||||
private val workScheduler: WorkScheduler,
|
||||
private val intentFactory: IntentFactory,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : ProvidableModule {
|
||||
|
||||
fun pushUseCase() = pushService
|
||||
@ -30,7 +32,7 @@ class NotificationsModule(
|
||||
fun firebasePushTokenUseCase() = firebasePushTokenUseCase
|
||||
fun roomStore() = roomStore
|
||||
fun notificationsUseCase() = NotificationsUseCase(
|
||||
NotificationRenderer(notificationManager(), NotificationFactory(iconLoader, context, intentFactory)),
|
||||
NotificationRenderer(notificationManager(), NotificationFactory(iconLoader, context, intentFactory), dispatchers),
|
||||
ObserveUnreadNotificationsUseCaseImpl(roomStore),
|
||||
NotificationChannels(notificationManager()),
|
||||
)
|
||||
|
@ -0,0 +1,85 @@
|
||||
package app.dapk.st.notifications
|
||||
|
||||
import app.dapk.st.notifications.NotificationFixtures.aNotifications
|
||||
import fake.FakeNotificationFactory
|
||||
import fake.FakeNotificationManager
|
||||
import fake.aFakeNotification
|
||||
import fixture.CoroutineDispatchersFixture.aCoroutineDispatchers
|
||||
import fixture.aRoomId
|
||||
import org.junit.Test
|
||||
import test.expect
|
||||
import test.runExpectTest
|
||||
|
||||
private const val SUMMARY_ID = 101
|
||||
private const val ROOM_MESSAGE_ID = 100
|
||||
private val A_SUMMARY_NOTIFICATION = aFakeNotification()
|
||||
|
||||
class NotificationRendererTest {
|
||||
|
||||
private val fakeNotificationManager = FakeNotificationManager()
|
||||
private val fakeNotificationFactory = FakeNotificationFactory()
|
||||
|
||||
private val notificationRenderer = NotificationRenderer(
|
||||
fakeNotificationManager.instance,
|
||||
fakeNotificationFactory.instance,
|
||||
aCoroutineDispatchers()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given removed rooms when rendering then cancels notifications with cancelled room ids`() = runExpectTest {
|
||||
val removedRooms = setOf(aRoomId("id-1"), aRoomId("id-2"))
|
||||
fakeNotificationFactory.instance.expect { it.createNotifications(emptyMap(), emptySet(), emptySet()) }
|
||||
fakeNotificationManager.instance.expectUnit {
|
||||
removedRooms.forEach { removedRoom -> it.cancel(removedRoom.value, ROOM_MESSAGE_ID) }
|
||||
}
|
||||
|
||||
notificationRenderer.render(emptyMap(), removedRooms, emptySet(), emptySet())
|
||||
|
||||
verifyExpects()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given summary notification is not created, when rendering, then cancels summary notification`() = runExpectTest {
|
||||
fakeNotificationFactory.givenNotifications(emptyMap(), emptySet(), emptySet()).returns(aNotifications(summaryNotification = null))
|
||||
fakeNotificationManager.instance.expectUnit { it.cancel(SUMMARY_ID) }
|
||||
|
||||
notificationRenderer.render(emptyMap(), emptySet(), emptySet(), emptySet())
|
||||
|
||||
verifyExpects()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given update is only removals, when rendering, then only renders room dismiss`() = runExpectTest {
|
||||
fakeNotificationFactory.givenNotifications(emptyMap(), emptySet(), emptySet()).returns(aNotifications(summaryNotification = null))
|
||||
fakeNotificationManager.instance.expectUnit { it.cancel(SUMMARY_ID) }
|
||||
|
||||
notificationRenderer.render(emptyMap(), emptySet(), emptySet(), emptySet())
|
||||
|
||||
verifyExpects()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given rooms with events, when rendering, then notifies summary and new rooms`() = runExpectTest {
|
||||
val roomNotification = aRoomNotification()
|
||||
val roomsWithNewEvents = setOf(roomNotification.roomId)
|
||||
fakeNotificationFactory.givenNotifications(emptyMap(), roomsWithNewEvents, emptySet()).returns(
|
||||
aNotifications(summaryNotification = A_SUMMARY_NOTIFICATION, delegates = listOf(roomNotification))
|
||||
)
|
||||
fakeNotificationManager.instance.expectUnit { it.notify(SUMMARY_ID, A_SUMMARY_NOTIFICATION) }
|
||||
fakeNotificationManager.instance.expectUnit { it.notify(roomNotification.roomId.value, ROOM_MESSAGE_ID, roomNotification.notification) }
|
||||
|
||||
notificationRenderer.render(emptyMap(), emptySet(), roomsWithNewEvents, emptySet())
|
||||
|
||||
verifyExpects()
|
||||
}
|
||||
|
||||
private fun aRoomNotification() = NotificationDelegate.Room(
|
||||
aFakeNotification(),
|
||||
aRoomId(),
|
||||
"a summary line",
|
||||
messageCount = 1,
|
||||
isAlerting = false
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package fake
|
||||
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.matrix.sync.RoomEvent
|
||||
import app.dapk.st.matrix.sync.RoomOverview
|
||||
import app.dapk.st.notifications.NotificationFactory
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import test.delegateReturn
|
||||
|
||||
class FakeNotificationFactory {
|
||||
|
||||
val instance = mockk<NotificationFactory>()
|
||||
|
||||
fun givenNotifications(allUnread: Map<RoomOverview, List<RoomEvent>>, roomsWithNewEvents: Set<RoomId>, newRooms: Set<RoomId>) = coEvery {
|
||||
instance.createNotifications(allUnread, roomsWithNewEvents, newRooms)
|
||||
}.delegateReturn()
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package app.dapk.st.notifications
|
||||
|
||||
import android.app.Notification
|
||||
|
||||
object NotificationFixtures {
|
||||
|
||||
fun aNotifications(
|
||||
summaryNotification: Notification? = null,
|
||||
delegates: List<NotificationDelegate> = emptyList(),
|
||||
) = Notifications(summaryNotification, delegates)
|
||||
|
||||
}
|
@ -58,7 +58,11 @@ class TestMatrix(
|
||||
|
||||
private val preferences = InMemoryPreferences()
|
||||
private val database = InMemoryDatabase.realInstance(user.roomMember.id.value)
|
||||
private val coroutineDispatchers = CoroutineDispatchers(Dispatchers.Unconfined, CoroutineScope(Dispatchers.Unconfined))
|
||||
private val coroutineDispatchers = CoroutineDispatchers(
|
||||
Dispatchers.Unconfined,
|
||||
main = Dispatchers.Unconfined,
|
||||
global = CoroutineScope(Dispatchers.Unconfined)
|
||||
)
|
||||
|
||||
private val storeModule = StoreModule(
|
||||
database = database,
|
||||
|
Loading…
Reference in New Issue
Block a user