Improve tests

This commit is contained in:
Phillip Thelen 2023-02-13 17:29:12 +01:00
parent afb2b234d9
commit 18796e1f8a
40 changed files with 337 additions and 90 deletions

View file

@ -58,12 +58,12 @@ dependencies {
implementation "com.amplitude:analytics-android:$amplitude_version"
//Tests
testImplementation 'io.kotest:kotest-runner-junit5:5.5.5'
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'io.mockk:mockk:1.13.4'
testImplementation 'io.mockk:mockk-android:1.13.4'
testImplementation 'io.kotest:kotest-assertions-core:5.5.5'
testImplementation 'io.kotest:kotest-framework-datatest:5.5.5'
testImplementation "io.mockk:mockk:$mockk_version"
testImplementation "io.mockk:mockk-android:$mockk_version"
testImplementation "io.kotest:kotest-runner-junit5:$kotest_version"
testImplementation "io.kotest:kotest-assertions-core:$kotest_version"
testImplementation "io.kotest:kotest-framework-datatest:$kotest_version"
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.5.1') {
exclude module: "protobuf-lite"
}
@ -74,8 +74,8 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5'
androidTestImplementation 'io.mockk:mockk-android:1.13.4'
androidTestImplementation 'io.kotest:kotest-assertions-core:5.5.5'
androidTestImplementation "io.mockk:mockk-android:$mockk_version"
androidTestImplementation "io.kotest:kotest-assertions-core:$kotest_version"
androidTestUtil("androidx.test:orchestrator:1.4.2")
implementation 'com.facebook.shimmer:shimmer:0.5.0'

View file

@ -71,7 +71,7 @@ open class SubscriptionPlan : RealmObject(), BaseObject {
val monthsUntilNextHourglass: Int
get() {
return if (subMonthCount > 0) {
(consecutive?.offset ?: 0) + 1
(consecutive?.offset ?: 0)
} else {
(3 - (((consecutive?.count ?: 0)) % 3))
}

View file

@ -13,7 +13,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.databinding.ActivityAdventureGuideBinding
import com.habitrpg.android.habitica.databinding.AdventureGuideItemBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel

View file

@ -10,7 +10,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.ActivityDeathBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.extensions.observeOnce
import com.habitrpg.android.habitica.helpers.AdHandler
import com.habitrpg.android.habitica.helpers.AdType

View file

@ -20,7 +20,6 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.core.os.bundleOf
import androidx.core.view.children
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.lifecycleScope
@ -264,16 +263,10 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
showAsBottomSheet { onClose ->
val group by viewModel.userViewModel.currentTeamPlanGroup.collectAsState(null)
val members by viewModel.userViewModel.currentTeamPlanMembers.observeAsState()
GroupPlanMemberList(members, group, {
GroupPlanMemberList(members, group) {
onClose()
FullProfileActivity.open(it)
}, { member ->
onClose()
MainNavigationController.navigate(
R.id.inboxMessageListFragment,
bundleOf(Pair("username", member.username), Pair("userID", member.id))
)
})
}
}
}
}

View file

@ -19,7 +19,7 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.ActivityNotificationsBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.helpers.launchCatching
import com.habitrpg.android.habitica.models.inventory.QuestContent

View file

@ -8,7 +8,7 @@ import android.widget.Button
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.ShopHeaderBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.shops.Shop

View file

@ -16,7 +16,7 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentQuestDetailBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.helpers.HapticFeedbackManager
import com.habitrpg.android.habitica.helpers.launchCatching

View file

@ -163,6 +163,7 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
fun leaveGroup(
groupChallenges: List<Challenge>,
keepChallenges: Boolean = true,
function: (() -> Unit)? = null
) {
if (!keepChallenges) {
viewModelScope.launchCatching {
@ -174,6 +175,7 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
viewModelScope.launch(ExceptionHandler.coroutine()) {
socialRepository.leaveGroup(groupID ?: "", keepChallenges)
userRepository.retrieveUser(withTasks = false, forced = true)
function?.invoke()
}
}

View file

@ -40,7 +40,8 @@ import kotlin.random.Random
fun GroupPlanMemberList(
members: List<Member>?,
group: Group?,
onMemberClicked: (String) -> Unit) {
onMemberClicked: (String) -> Unit
) {
LazyColumn {
item {
Text(

View file

@ -6,7 +6,7 @@ import android.view.View
import android.widget.TextView
import com.habitrpg.android.habitica.databinding.DialogAchievementDetailBinding
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.views.PixelArtView

View file

@ -7,7 +7,7 @@ import android.view.View
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DialogAchievementDetailBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.common.habitica.extensions.layoutInflater

View file

@ -11,7 +11,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DialogCompletedQuestContentBinding
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.inventory.QuestDropItem
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper

View file

@ -6,7 +6,7 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.models.notifications.ChallengeWonData
import com.habitrpg.common.habitica.views.PixelArtView

View file

@ -5,7 +5,7 @@ import android.util.AttributeSet
import android.view.Gravity
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.common.habitica.extensions.dpToPx

View file

@ -81,13 +81,6 @@ class TaskRepositoryImplTest : WordSpec({
localRepository.getUser("")
}
}
"does not update user for team tasks" {
val data = TaskDirectionData()
data.lvl = 0
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
repository.taskChecked(user, task, true, false, null)
verify(exactly = 0) { user.stats }
}
"builds task result correctly" {
val data = TaskDirectionData()
data.lvl = 10

View file

@ -31,9 +31,17 @@ class MemberTest : WordSpec({
member.hasClass shouldBe false
}
"false if user is below level 10" {
member.flags?.classSelected = true
member.stats?.habitClass = Stats.ROGUE
member.stats?.lvl = 9
member.hasClass shouldBe false
}
"true if class was selected and not disabled" {
member.flags?.classSelected = true
member.stats?.habitClass = Stats.ROGUE
member.stats?.lvl = 10
member.hasClass shouldBe true
}
}

View file

@ -37,10 +37,17 @@ class UserTest : WordSpec({
user.hasClass shouldBe false
}
"false if user is below level 10" {
user.flags?.classSelected = true
user.stats?.habitClass = Stats.ROGUE
user.stats?.lvl = 9
user.hasClass shouldBe false
}
"true if class was selected and not disabled" {
user.flags?.classSelected = true
user.preferences?.disableClasses = false
user.stats?.habitClass = Stats.ROGUE
user.stats?.lvl = 10
user.hasClass shouldBe true
}
}

View file

@ -16,10 +16,12 @@ buildscript {
coroutines_version = '1.6.4'
daggerhilt_version = '2.44.2'
firebase_bom = '31.2.0'
kotest_version = '5.5.5'
kotlin_version = '1.8.10'
ktlint_version = '0.48.2'
lifecycle_version = '2.5.1'
markwon_version = '4.6.2'
mockk_version = '1.13.4'
moshi_version = '1.14.0'
navigation_version = '2.5.3'
okhttp_version = '4.10.0'

View file

@ -38,6 +38,37 @@ android {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
namespace 'com.habitrpg.common.habitica'
flavorDimensions "buildType"
productFlavors {
dev {
dimension "buildType"
}
staff {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"staff\""
}
partners {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"partners\""
}
alpha {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"alpha\""
}
beta {
buildConfigField "String", "TESTING_LEVEL", "\"beta\""
}
prod {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
}
}
}
dependencies {
@ -57,13 +88,23 @@ dependencies {
implementation("io.coil-kt:coil:$coil_version")
implementation("io.coil-kt:coil-gif:$coil_version")
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
testImplementation "io.mockk:mockk:$mockk_version"
testImplementation "io.mockk:mockk-android:$mockk_version"
testImplementation "io.kotest:kotest-runner-junit5:$kotest_version"
testImplementation "io.kotest:kotest-assertions-core:$kotest_version"
testImplementation "io.kotest:kotest-framework-datatest:$kotest_version"
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation project(':shared')
}
android.testOptions {
unitTests.all {
it.useJUnitPlatform()
}
}
// Add Habitica Properties to buildConfigField
final File HRPG_PROPS_FILE = new File(projectDir.absolutePath + '/../habitica.properties')
if (HRPG_PROPS_FILE.canRead()) {

View file

@ -1,4 +1,4 @@
package com.habitrpg.android.habitica.extensions
package com.habitrpg.common.habitica.extensions
import android.text.Html
import android.text.Spannable

View file

@ -0,0 +1,21 @@
package com.habitrpg.common.habitica.api
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.string.shouldEndWith
class ServerTest : WordSpec({
"constructor" should {
"add api version if missing" {
val server = Server("https://habitica.com")
server.toString() shouldEndWith "/api/v4/"
}
"add api version if missing but has trailing slash" {
val server = Server("https://habitica.com")
server.toString() shouldEndWith ".com/api/v4/"
}
"not add api version multiple times" {
val server = Server("https://habitica.com")
server.toString() shouldEndWith ".com/api/v4/"
}
}
})

View file

@ -3,20 +3,19 @@ plugins {
id("com.android.library")
id("kotlin-parcelize")
id("kotlin-kapt")
id("io.kotest.multiplatform") version "5.5.5"
}
allprojects {
repositories {
mavenCentral()
mavenLocal()
}
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
ios()
sourceSets {
val commonMain by getting {
@ -26,29 +25,13 @@ kotlin {
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test")) // This brings all the platform dependencies automatically
}
}
val androidMain by getting
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
val iosMain by getting
val iosTest by getting
}
}
@ -66,4 +49,4 @@ android {
}
namespace = "com.habitrpg.shared.habitica"
}
}

View file

@ -1,7 +1,6 @@
package com.habitrpg.shared.habitica.models.tasks
interface BaseTask {
val completed: Boolean
var type: TaskType?
var isDue: Boolean?

View file

@ -0,0 +1,14 @@
package com.habitrpg.shared.habitica.models.tasks
import kotlin.test.Test
import kotlin.test.assertEquals
class AttributeTest {
@Test
fun testFrom() {
assertEquals(Attribute.STRENGTH, Attribute.from("str"))
assertEquals(Attribute.CONSTITUTION, Attribute.from("con"))
assertEquals(Attribute.PERCEPTION, Attribute.from("per"))
assertEquals(Attribute.INTELLIGENCE, Attribute.from("int"))
}
}

View file

@ -11,6 +11,13 @@ apply plugin: 'kotlin-android'
android {
compileSdk target_sdk
testOptions {
unitTests {
includeAndroidResources = true
}
animationsDisabled = true
}
defaultConfig {
applicationId "com.habitrpg.android.habitica"
minSdk 26
@ -137,11 +144,25 @@ dependencies {
kapt "com.google.dagger:hilt-compiler:$daggerhilt_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation "io.mockk:mockk:$mockk_version"
testImplementation "io.mockk:mockk-android:$mockk_version"
testImplementation "io.kotest:kotest-runner-junit5:$kotest_version"
testImplementation "io.kotest:kotest-assertions-core:$kotest_version"
testImplementation "io.kotest:kotest-framework-datatest:$kotest_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
testImplementation 'app.cash.turbine:turbine:0.12.1'
}
repositories {
mavenCentral()
}
android.testOptions {
unitTests.all {
it.useJUnitPlatform()
}
}
final File HRPG_PROPS_FILE = new File(projectDir.absolutePath + '/../habitica.properties')
if (HRPG_PROPS_FILE.canRead()) {
Properties HRPG_PROPS = new Properties()

View file

@ -20,6 +20,7 @@ import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -40,6 +41,7 @@ class MainApplication : Application() {
MainScope().launch {
userRepository.getUser()
.filterNotNull()
.onEach {
if (it.isDead && BaseActivity.currentActivityClassName == MainActivity::class.java.name) {
val intent = Intent(this@MainApplication, FaintActivity::class.java)

View file

@ -1,16 +1,14 @@
package com.habitrpg.wearos.habitica.data.repositories
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow
import com.habitrpg.wearos.habitica.models.user.User
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class UserLocalRepository @Inject constructor() {
private val user = MutableLiveData<User?>()
fun getUser() = user.asFlow().filterNotNull()
private val user = MutableStateFlow<User?>(null)
fun getUser() = user
fun saveUser(user: User) {
this.user.value = user

View file

@ -23,6 +23,6 @@ sealed class NetworkResult<out T : Any> {
val isError: Boolean
get() = this is Error
data class Success<out T : Any>(val data: T, val isFresh: Boolean) : NetworkResult<T>()
data class Error(val exception: Exception, val isFresh: Boolean) : NetworkResult<Nothing>()
data class Success<out T : Any>(val data: T, internal val isFresh: Boolean) : NetworkResult<T>()
data class Error(val exception: Exception, internal val isFresh: Boolean) : NetworkResult<Nothing>()
}

View file

@ -6,10 +6,13 @@ import android.view.Gravity
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.databinding.ActivityAvatarBinding
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.wearos.habitica.ui.viewmodels.AvatarViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import java.lang.Integer.max
import kotlin.math.roundToInt
@ -21,8 +24,10 @@ class AvatarActivity : BaseActivity<ActivityAvatarBinding, AvatarViewModel>() {
binding = ActivityAvatarBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
viewModel.user.observe(this) {
binding.avatarView.setAvatar(it)
lifecycleScope.launch {
viewModel.user.filterNotNull().collect {
binding.avatarView.setAvatar(it)
}
}
}

View file

@ -17,6 +17,7 @@ import com.habitrpg.wearos.habitica.ui.adapters.HubAdapter
import com.habitrpg.wearos.habitica.ui.viewmodels.MainViewModel
import com.habitrpg.wearos.habitica.util.HabiticaScrollingLayoutCallback
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
@AndroidEntryPoint
@ -111,12 +112,17 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
openSettingsActivity()
}
)
viewModel.user.observe(this) { user ->
adapter.title = user.profile?.name ?: ""
val index = adapter.data.indexOfFirst { it.identifier == "stats" }
adapter.data[index].detailText = getString(R.string.user_level, user.stats?.lvl ?: 0)
adapter.notifyItemChanged(index + 1)
lifecycleScope.launch {
viewModel.user
.filterNotNull()
.collect { user ->
adapter.title = user.profile?.name ?: ""
val index = adapter.data.indexOfFirst { it.identifier == "stats" }
adapter.data[index].detailText = getString(R.string.user_level, user.stats?.lvl ?: 0)
adapter.notifyItemChanged(index + 1)
}
}
viewModel.taskCounts.observe(this) {
adapter.data.forEach { menuItem ->
if (it.containsKey(menuItem.identifier)) {

View file

@ -12,7 +12,7 @@ import androidx.preference.PreferenceManager
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.ActivityTaskResultBinding
import com.habitrpg.android.habitica.databinding.TaskRewardDropBinding
import com.habitrpg.android.habitica.extensions.localizedCapitalize
import com.habitrpg.common.habitica.extensions.localizedCapitalize
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.shared.habitica.models.responses.TaskScoringResult

View file

@ -1,6 +1,5 @@
package com.habitrpg.wearos.habitica.ui.viewmodels
import androidx.lifecycle.asLiveData
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.managers.AppStateManager
@ -19,5 +18,5 @@ class AvatarViewModel @Inject constructor(
taskRepository,
exceptionBuilder, appStateManager
) {
var user = userRepository.getUser().asLiveData()
var user = userRepository.getUser()
}

View file

@ -17,6 +17,6 @@ class LevelupViewModel @Inject constructor(
appStateManager: AppStateManager
) : BaseViewModel(userRepository, taskRepository, exceptionBuilder, appStateManager) {
val level = userRepository.getUser()
.map { it.stats?.lvl }
.map { it?.stats?.lvl }
.asLiveData()
}

View file

@ -28,5 +28,5 @@ class MainViewModel @Inject constructor(
}
val taskCounts = taskRepository.getActiveTaskCounts().asLiveData()
val user = userRepository.getUser().asLiveData()
val user = userRepository.getUser()
}

View file

@ -10,6 +10,7 @@ import com.habitrpg.wearos.habitica.models.user.User
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -27,6 +28,7 @@ class StatsViewModel @Inject constructor(
}
var user: LiveData<User> = userRepository.getUser()
.filterNotNull()
.distinctUntilChanged { old, new ->
val oldStats = old.stats ?: return@distinctUntilChanged false
val newStats = new.stats ?: return@distinctUntilChanged false

View file

@ -0,0 +1,40 @@
package com.habitrpg.wearos.habitica
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.TaskExecutor
import io.kotest.core.config.AbstractProjectConfig
import kotlinx.coroutines.test.TestCoroutineDispatcher
object ProjectConfig : AbstractProjectConfig() {
private val testDispatcher = TestCoroutineDispatcher()
override suspend fun beforeProject() {
super.beforeProject()
setupLiveData()
}
override suspend fun afterProject() {
super.afterProject()
resetLiveData()
}
private fun setupLiveData() {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
override fun isMainThread(): Boolean {
return true
}
})
}
private fun resetLiveData() {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}

View file

@ -0,0 +1,36 @@
package com.habitrpg.wearos.habitica.data.repositories
import app.cash.turbine.test
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.wearos.habitica.models.tasks.Task
import com.habitrpg.wearos.habitica.models.tasks.TaskList
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
class TaskLocalRepositoryTest : WordSpec({
val repository = TaskLocalRepository()
val list = TaskList()
list.tasks["1"] = Task().apply {
id = "1"
type = TaskType.HABIT
}
list.tasks["2"] = Task().apply {
id = "2"
type = TaskType.DAILY
}
list.tasks["3"] = Task().apply {
id = "3"
type = TaskType.DAILY
}
list.tasks["4"] = Task().apply {
id = "4"
type = TaskType.REWARD
}
"getTask" should {
"return right task" {
repository.getTask("3").test {
awaitItem()?.id shouldBe "3"
}
}
}
})

View file

@ -0,0 +1,32 @@
package com.habitrpg.wearos.habitica.data.repositories
import app.cash.turbine.test
import com.habitrpg.wearos.habitica.models.user.User
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
class UserLocalRepositoryTest : WordSpec({
coroutineTestScope = true
val repository = UserLocalRepository()
"saveUser" should {
"update user in flow" {
val existing = User()
repository.saveUser(existing)
repository.getUser().test {
awaitItem() shouldBe existing
}
}
}
"clearData" should {
"clear user from flow" {
val existing = User()
repository.saveUser(existing)
repository.clearData()
repository.getUser().test {
awaitItem() shouldBe null
}
}
}
})

View file

@ -0,0 +1,42 @@
package com.habitrpg.wearos.habitica.models
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
class NetworkResultTest : WordSpec({
"isSuccess" should {
"be true if it's successful" {
val response = NetworkResult.Success<String>("", true)
response.isSuccess shouldBe true
}
"be false if it errored" {
val response = NetworkResult.Error(Exception(), true)
response.isSuccess shouldBe false
}
}
"isError" should {
"be true if it's errored" {
val response = NetworkResult.Error(Exception(), true)
response.isError shouldBe true
}
"be false if it's successful" {
val response = NetworkResult.Success<String>("", true)
response.isError shouldBe false
}
}
"isResponseFresh" should {
"be true if it's a fresh response" {
val response = NetworkResult.Success<String>("", true)
response.isResponseFresh shouldBe true
}
"be false if it errored" {
val response = NetworkResult.Success<String>("", false)
response.isResponseFresh shouldBe false
}
}
})