chinwag-android/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt
Konrad Pozniak 9e597800c9
Move all database queries off the ui thread & add a ViewModel for MainActivity (#4786)
- Move all database queries off the ui thread - this is a massive
performance improvement
- ViewModel for MainActivity - this makes MainActivity smaller and
network requests won't be retried when rotating the screen
- removes the Push Notification Migration feature. We had it long
enough, all users who want push notifications should be migrated by now
- AccountEntity is now immutable
- converted BaseActivity to Kotlin
- The header image of Accounts is now cached as well
2025-01-17 12:35:35 +01:00

174 lines
6.4 KiB
Kotlin

package com.keylesspalace.tusky
import android.app.Activity
import android.app.NotificationManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.viewpager2.widget.ViewPager2
import androidx.work.testing.WorkManagerTestInitHelper
import at.connyduck.calladapter.networkresult.NetworkResult
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
import com.keylesspalace.tusky.components.systemnotifications.NotificationHelper
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.db.entity.AccountEntity
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Notification
import com.keylesspalace.tusky.entity.TimelineAccount
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.getSerializableExtraCompat
import java.util.Date
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.robolectric.Robolectric
import org.robolectric.Shadows.shadowOf
import org.robolectric.android.util.concurrent.BackgroundExecutor.runInBackground
import org.robolectric.annotation.Config
@Config(sdk = [28])
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val account = Account(
id = "1",
localUsername = "",
username = "",
displayName = "",
createdAt = Date(),
note = "",
url = "",
avatar = "",
header = ""
)
private val accountEntity = AccountEntity(
id = 1,
domain = "test.domain",
accessToken = "fakeToken",
clientId = "fakeId",
clientSecret = "fakeSecret",
isActive = true
)
@Before
fun setup() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
}
@After
fun teardown() {
WorkManagerTestInitHelper.closeWorkDatabase()
}
@Test
fun `clicking notification of type FOLLOW shows notification tab`() {
val intent = showNotification(Notification.Type.FOLLOW)
val activity = startMainActivity(intent)
val currentTab = activity.findViewById<ViewPager2>(R.id.viewPager).currentItem
val notificationTab = defaultTabs().indexOfFirst { it.id == NOTIFICATIONS }
assertEquals(currentTab, notificationTab)
}
@Test
fun `clicking notification of type FOLLOW_REQUEST shows follow requests`() {
val intent = showNotification(Notification.Type.FOLLOW_REQUEST)
val activity = startMainActivity(intent)
val nextActivity = shadowOf(activity).peekNextStartedActivity()
assertNotNull(nextActivity)
assertEquals(ComponentName(context, AccountListActivity::class.java.name), nextActivity.component)
assertEquals(AccountListActivity.Type.FOLLOW_REQUESTS, nextActivity.getSerializableExtraCompat("type"))
}
private fun showNotification(type: Notification.Type): Intent {
val notificationManager = context.getSystemService(NotificationManager::class.java)
val shadowNotificationManager = shadowOf(notificationManager)
NotificationHelper.createNotificationChannelsForAccount(accountEntity, context)
runInBackground {
val notification = NotificationHelper.makeBaseNotification(
context,
notificationManager,
Notification(
type = type,
id = "id",
account = TimelineAccount(
id = "1",
localUsername = "connyduck",
username = "connyduck@mastodon.example",
displayName = "Conny Duck",
note = "This is their bio",
url = "https://mastodon.example/@ConnyDuck",
avatar = "https://mastodon.example/system/accounts/avatars/000/150/486/original/ab27d7ddd18a10ea.jpg"
),
status = null,
report = null
),
accountEntity
)
notificationManager.notify("id", 1, notification)
}
val notification = shadowNotificationManager.allNotifications.first()
return shadowOf(notification.contentIntent).savedIntent
}
private fun startMainActivity(intent: Intent): Activity {
val controller = Robolectric.buildActivity(MainActivity::class.java, intent)
val activity = controller.get()
val eventHub = EventHub()
activity.eventHub = eventHub
val accountManager: AccountManager = mock {
on { accounts } doReturn listOf(accountEntity)
on { accountsFlow } doReturn MutableStateFlow(listOf(accountEntity))
on { activeAccount } doReturn accountEntity
}
activity.accountManager = accountManager
activity.draftsAlert = mock { }
val api: MastodonApi = mock {
onBlocking { accountVerifyCredentials() } doReturn NetworkResult.success(account)
onBlocking { announcements() } doReturn NetworkResult.success(emptyList())
}
activity.mastodonApi = api
activity.preferences = mock(defaultAnswer = {
when (it.method.returnType) {
String::class.java -> "test"
Boolean::class.java -> false
else -> null
}
})
val viewModel = MainViewModel(
context = mock {
on { getSystemService(Context.NOTIFICATION_SERVICE) } doReturn mock<NotificationManager>()
},
api = api,
eventHub = eventHub,
accountManager = accountManager,
shareShortcutHelper = mock()
)
val testViewModelFactory = viewModelFactory {
initializer { viewModel }
}
activity.viewModelProviderFactory = testViewModelFactory
controller.create().start()
return activity
}
}