Fix some tests

This commit is contained in:
Phillip Thelen 2023-01-13 12:05:29 +01:00
parent 553cd19176
commit 6222b24df6
11 changed files with 330 additions and 312 deletions

View file

@ -96,6 +96,7 @@
app:layout_collapseMode="pin"
app:tabGravity="fill"
app:tabIndicatorColor="?textColorPrimary"
app:tabSelectedTextColor="?textColorPrimary"
app:tabMode="fixed" />
<FrameLayout
android:id="@+id/connection_issue_view"

View file

@ -1338,6 +1338,7 @@
<string name="twenty_gems">20 Gems</string>
<string name="background">Background</string>
<string name="birthday_set">Birthday Set</string>
<string name="you_equipped_x">You equipped %s</string>
<plurals name="you_x_others">
<item quantity="zero">You</item>

View file

@ -17,7 +17,7 @@ import io.github.kakaocup.kakao.text.KTextView
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.flow.flowOf
import org.junit.Test
import org.junit.runner.RunWith
@ -38,7 +38,7 @@ class PartyDetailFragmentTest : FragmentTestCase<PartyDetailFragment, FragmentPa
override fun makeFragment() {
val group = Group()
group.name = "Group Name"
every { socialRepository.getGroup(any()) } returns Flowable.just(group)
every { socialRepository.getGroup(any()) } returns flowOf(group)
viewModel = PartyViewModel(false)
viewModel.socialRepository = socialRepository
viewModel.userRepository = userRepository

View file

@ -10,10 +10,11 @@ import com.habitrpg.shared.habitica.models.tasks.Attribute
import io.github.kakaocup.kakao.common.views.KView
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.kakao.text.KButton
import io.mockk.coVerify
import io.mockk.every
import io.mockk.spyk
import io.mockk.verify
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.flow.flowOf
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@ -63,9 +64,9 @@ class StatsFragmentTest : FragmentTestCase<StatsFragment, FragmentStatsBinding,
fun setUpUser() {
user.stats?.lvl = 20
user.stats?.points = 30
userState.onNext(user)
userState.value = user
every { inventoryRepository.getEquipment(listOf()) } returns Flowable.just(listOf())
every { inventoryRepository.getEquipment(listOf()) } returns flowOf(listOf())
}
@Test
@ -96,13 +97,13 @@ class StatsFragmentTest : FragmentTestCase<StatsFragment, FragmentStatsBinding,
fun allocatesOnClick() {
screen {
strengthAllocateButton.click()
verify { userRepository.allocatePoint(Attribute.STRENGTH) }
coVerify { userRepository.allocatePoint(Attribute.STRENGTH) }
intelligenceAllocateButton.click()
verify { userRepository.allocatePoint(Attribute.INTELLIGENCE) }
coVerify { userRepository.allocatePoint(Attribute.INTELLIGENCE) }
constitutionAllocateButton.click()
verify { userRepository.allocatePoint(Attribute.CONSTITUTION) }
coVerify { userRepository.allocatePoint(Attribute.CONSTITUTION) }
perceptionAllocateButton.click()
verify { userRepository.allocatePoint(Attribute.PERCEPTION) }
coVerify { userRepository.allocatePoint(Attribute.PERCEPTION) }
}
}
}

View file

@ -15,12 +15,14 @@ import io.github.kakaocup.kakao.recycler.KRecyclerView
import io.github.kakaocup.kakao.screen.Screen
import io.kotest.matchers.shouldBe
import io.mockk.CapturingSlot
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.spyk
import io.mockk.verify
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.flow.flowOf
import org.junit.Test
class PetDetailScreen : Screen<PetDetailScreen>() {
@ -35,12 +37,12 @@ class PetDetailScreen : Screen<PetDetailScreen>() {
internal class PetDetailRecyclerFragmentTest :
FragmentTestCase<PetDetailRecyclerFragment, FragmentRecyclerviewBinding, PetDetailScreen>(false) {
override fun makeFragment() {
every { inventoryRepository.getOwnedPets() } returns Flowable.just(user.items?.pets!!)
every { inventoryRepository.getOwnedMounts() } returns Flowable.just(user.items?.mounts!!)
every { inventoryRepository.getOwnedItems("food") } returns Flowable.just(user.items?.food!!.filter { it.numberOwned > 0 })
every { inventoryRepository.getOwnedPets() } returns flowOf(user.items?.pets!!)
every { inventoryRepository.getOwnedMounts() } returns flowOf(user.items?.mounts!!)
every { inventoryRepository.getOwnedItems("food") } returns flowOf(user.items?.food!!.filter { it.numberOwned > 0 })
val saddle = OwnedItem()
saddle.numberOwned = 1
every { inventoryRepository.getOwnedItems(true) } returns Flowable.just(
every { inventoryRepository.getOwnedItems(true) } returns flowOf(
mapOf(
Pair(
"Saddle-food",
@ -69,21 +71,21 @@ internal class PetDetailRecyclerFragmentTest :
@Test
fun canFeedPet() {
val slot = CapturingSlot<FeedPetUseCase.RequestValues>()
every { feedPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
coEvery { feedPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
every {
inventoryRepository.getPets(
any(),
any(),
any()
)
} returns Flowable.just(content.pets.filter { it.animal == "Cactus" })
} returns flowOf(content.pets.filter { it.animal == "Cactus" })
every {
inventoryRepository.getMounts(
any(),
any(),
any()
)
} returns Flowable.just(content.mounts.filter { it.animal == "Cactus" })
} returns flowOf(content.mounts.filter { it.animal == "Cactus" })
launchFragment(
PetDetailRecyclerFragmentArgs.Builder("cactus", "drop", "").build().toBundle()
)
@ -92,7 +94,7 @@ internal class PetDetailRecyclerFragmentTest :
childWith<PetItem> { withContentDescription("Skeleton Cactus") }.click()
KView { withText(R.string.feed) }.click()
KView { withText("Meat") }.click()
verify { feedPetUseCase.callInteractor(any()) }
coVerify { feedPetUseCase.callInteractor(any()) }
slot.captured.pet.key shouldBe "Cactus-Skeleton"
slot.captured.food.key shouldBe "Meat"
}
@ -102,27 +104,27 @@ internal class PetDetailRecyclerFragmentTest :
@Test
fun canUseSaddle() {
val slot = CapturingSlot<FeedPetUseCase.RequestValues>()
every { feedPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
coEvery { feedPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
every {
inventoryRepository.getPets(
any(),
any(),
any()
)
} returns Flowable.just(content.pets.filter { it.animal == "Fox" })
} returns flowOf(content.pets.filter { it.animal == "Fox" })
every {
inventoryRepository.getMounts(
any(),
any(),
any()
)
} returns Flowable.just(content.mounts.filter { it.animal == "Fox" })
} returns flowOf(content.mounts.filter { it.animal == "Fox" })
launchFragment(PetDetailRecyclerFragmentArgs.Builder("fox", "drop", "").build().toBundle())
screen {
recycler {
childWith<PetItem> { withContentDescription("Shade Fox") }.click()
KView { withText(R.string.use_saddle) }.click()
verify { feedPetUseCase.callInteractor(any()) }
coVerify { feedPetUseCase.callInteractor(any()) }
slot.captured.pet.key shouldBe "Fox-Shade"
slot.captured.food.key shouldBe "Saddle"
}

View file

@ -15,7 +15,7 @@ import io.github.kakaocup.kakao.text.KTextView
import io.mockk.every
import io.mockk.spyk
import io.mockk.verify
import io.reactivex.rxjava3.core.Flowable
import kotlinx.coroutines.flow.flowOf
import org.hamcrest.Matcher
import org.junit.Test
@ -40,8 +40,8 @@ class StableScreen : Screen<StableScreen>() {
internal class StableRecyclerFragmentTest : FragmentTestCase<StableRecyclerFragment, FragmentRecyclerviewBinding, StableScreen>(false) {
override fun makeFragment() {
every { inventoryRepository.getOwnedPets() } returns Flowable.just(user.items?.pets!!)
every { inventoryRepository.getOwnedMounts() } returns Flowable.just(user.items?.mounts!!)
every { inventoryRepository.getOwnedPets() } returns flowOf(user.items?.pets!!)
every { inventoryRepository.getOwnedMounts() } returns flowOf(user.items?.mounts!!)
fragment = spyk()
fragment.shouldInitializeComponent = false
fragment.itemType = "pets"

View file

@ -68,4 +68,5 @@ interface TaskRepository : BaseRepository {
fun getTasksForChallenge(challengeID: String?): Flow<List<Task>>
suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData?
suspend fun markTaskNeedsWork(task: Task, userID: String)
}

View file

@ -196,7 +196,9 @@ class TaskRepositoryImpl(
override suspend fun markTaskNeedsWork(task: Task, userID: String) {
val savedTask = apiClient.markTaskNeedsWork(task.id ?: "", userID)
if (savedTask != null) {
savedTask.id = task.id
savedTask.position = task.position
savedTask.group?.assignedUsersDetail?.firstOrNull { it.assignedUserID == userID }?.completed = false
localRepository.save(savedTask)
}
}

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.ui.activities
import android.app.Activity
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
@ -20,9 +21,16 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.DrawerState
import androidx.compose.material.DrawerValue
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.SnackbarHostState
import androidx.compose.material.Text
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -49,6 +57,7 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.lifecycleScope
import coil.compose.AsyncImage
import com.android.billingclient.api.ProductDetails
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
@ -81,6 +90,7 @@ class BirthdayActivity : BaseActivity() {
@Inject
lateinit var configManager: AppConfigManager
val scaffoldState: ScaffoldState = ScaffoldState(DrawerState(DrawerValue.Closed), SnackbarHostState())
private val isPurchasing = mutableStateOf(false)
private val price = mutableStateOf("")
private val hasGryphatrice = mutableStateOf(false)
@ -94,6 +104,7 @@ class BirthdayActivity : BaseActivity() {
setContent {
HabiticaTheme {
BirthdayActivityView(
scaffoldState,
isPurchasing.value,
hasGryphatrice.value,
price.value,
@ -114,12 +125,13 @@ class BirthdayActivity : BaseActivity() {
userRepository.retrieveUser(false, true)
isPurchasing.value = false
}
},
{
lifecycleScope.launchCatching {
inventoryRepository.equip("pet", "Gryphatrice-Jubilant")
}
})
}
) {
lifecycleScope.launchCatching {
inventoryRepository.equip("pet", "Gryphatrice-Jubilant")
scaffoldState.snackbarHostState.showSnackbar(getString(R.string.you_equipped_x, getString(R.string.animated_gryphatrice_pet)))
}
}
}
}
@ -177,6 +189,7 @@ fun BirthdayTitle(text: String) {
@Composable
fun BirthdayActivityView(
scaffoldState: ScaffoldState,
isPurchasing: Boolean,
hasGryphatrice: Boolean,
price: String,
@ -191,250 +204,269 @@ fun BirthdayActivityView(
val textColor = Color.White
val specialTextColor = colorResource(R.color.yellow_50)
Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
.background(
Brush.verticalGradient(
Pair(0.0f, colorResource(id = R.color.brand_300)),
Pair(1.0f, colorResource(id = R.color.brand_200))
)
)
.fillMaxWidth()
.verticalScroll(rememberScrollState())
) {
Button(
onClick = {
if (activity != null) {
activity.finish()
return@Button
}
MainNavigationController.navigateBack()
},
colors = ButtonDefaults.textButtonColors(contentColor = textColor),
elevation = ButtonDefaults.elevation(0.dp, 0.dp),
modifier = Modifier.align(Alignment.Start)
) {
Image(
painterResource(R.drawable.arrow_back),
stringResource(R.string.action_back),
colorFilter = ColorFilter.tint(
textColor
)
)
}
val systemUiController = rememberSystemUiController()
val statusbarColor = colorResource(R.color.brand_300)
val navigationbarColor = colorResource(R.color.brand_50)
DisposableEffect(systemUiController) {
systemUiController.setStatusBarColor(statusbarColor, darkIcons = false)
systemUiController.setNavigationBarColor(navigationbarColor)
onDispose {}
}
Scaffold(
scaffoldState = scaffoldState
) { padding ->
Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
) {
Image(
painterResource(R.drawable.birthday_header),
null,
Modifier.padding(bottom = 8.dp)
)
Row(verticalAlignment = Alignment.CenterVertically) {
Image(painterResource(R.drawable.birthday_gifts), null)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 22.dp)
) {
Text(
stringResource(id = R.string.limited_event).toUpperCase(Locale.current),
fontSize = 12.sp,
color = specialTextColor,
fontWeight = FontWeight.Bold
.background(
Brush.verticalGradient(
Pair(0.0f, colorResource(id = R.color.brand_300)),
Pair(1.0f, colorResource(id = R.color.brand_200))
)
)
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(padding)
) {
Button(
onClick = {
if (activity != null) {
activity.finish()
return@Button
}
MainNavigationController.navigateBack()
},
colors = ButtonDefaults.textButtonColors(contentColor = textColor),
elevation = ButtonDefaults.elevation(0.dp, 0.dp),
modifier = Modifier.align(Alignment.Start)
) {
Image(
painterResource(R.drawable.arrow_back),
stringResource(R.string.action_back),
colorFilter = ColorFilter.tint(
textColor
)
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
) {
Image(
painterResource(R.drawable.birthday_header),
null,
Modifier.padding(bottom = 8.dp)
)
Row(verticalAlignment = Alignment.CenterVertically) {
Image(painterResource(R.drawable.birthday_gifts), null)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 22.dp)
) {
Text(
stringResource(id = R.string.limited_event).toUpperCase(Locale.current),
fontSize = 12.sp,
color = specialTextColor,
fontWeight = FontWeight.Bold
)
Text(
stringResource(
R.string.x_to_y,
dateFormat.format(startDate),
dateFormat.format(endDate)
),
fontSize = 12.sp,
color = textColor,
fontWeight = FontWeight.Bold
)
}
// right image should be flipped
Image(
painterResource(id = R.drawable.birthday_gifts),
null,
modifier = Modifier.scale(-1f, 1f)
)
}
Text(
stringResource(R.string.birthday_title_description),
fontSize = 16.sp,
color = specialTextColor,
fontWeight = FontWeight.SemiBold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 22.dp)
)
BirthdayTitle(stringResource(id = R.string.animated_gryphatrice_pet))
Box(
Modifier
.size(161.dp, 129.dp)
.padding(vertical = 20.dp)
.background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))
) {
}
Text(
stringResource(R.string.limited_edition).toUpperCase(Locale.current),
fontSize = 12.sp,
color = specialTextColor,
fontWeight = FontWeight.Bold
)
Text(
stringResource(R.string.gryphatrice_description),
fontSize = 16.sp,
color = textColor,
textAlign = TextAlign.Center,
lineHeight = 20.sp,
modifier = Modifier.padding(bottom = 16.dp)
)
if (hasGryphatrice) {
Text(
stringResource(
R.string.x_to_y,
dateFormat.format(startDate),
dateFormat.format(endDate)
),
stringResource(R.string.thanks_for_support),
fontSize = 12.sp,
color = textColor,
fontWeight = FontWeight.Bold
fontWeight = FontWeight.SemiBold
)
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
onEquipClick()
},
modifier = Modifier.padding(top = 20.dp)
) {
Text(stringResource(R.string.equip))
}
} else if (isPurchasing) {
CircularProgressIndicator()
} else {
Text(buildAnnotatedString {
append("Buy for ")
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(price)
}
append(" or ")
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append("60 Gems")
}
}, color = Color.White)
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
onPurchaseClick()
},
modifier = Modifier.padding(top = 20.dp)
) {
Text(stringResource(R.string.buy_for_x, price))
}
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
onGemPurchaseClick()
},
modifier = Modifier.padding(top = 20.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.buy_for))
CurrencyText(currency = "gems", value = 60)
}
}
}
// right image should be flipped
Image(
painterResource(id = R.drawable.birthday_gifts),
null,
modifier = Modifier.scale(-1f, 1f)
)
}
Text(
stringResource(R.string.birthday_title_description),
fontSize = 16.sp,
color = specialTextColor,
fontWeight = FontWeight.SemiBold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 22.dp)
)
BirthdayTitle(stringResource(id = R.string.animated_gryphatrice_pet))
Box(
Modifier
.size(161.dp, 129.dp)
.padding(vertical = 20.dp)
.background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))
) {
}
Text(
stringResource(R.string.limited_edition).toUpperCase(Locale.current),
fontSize = 12.sp,
color = specialTextColor,
fontWeight = FontWeight.Bold
)
Text(
stringResource(R.string.gryphatrice_description),
fontSize = 16.sp,
color = textColor,
textAlign = TextAlign.Center,
lineHeight = 20.sp,
modifier = Modifier.padding(bottom = 16.dp)
)
if (hasGryphatrice) {
BirthdayTitle(stringResource(id = R.string.plenty_of_potions))
Text(
stringResource(R.string.thanks_for_support),
fontSize = 12.sp,
stringResource(R.string.plenty_of_potions_description),
fontSize = 16.sp,
color = textColor,
fontWeight = FontWeight.SemiBold
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{},
modifier = Modifier.padding(top = 20.dp)
) {
Text(stringResource(R.string.equip))
}
} else if (isPurchasing) {
CircularProgressIndicator()
} else {
Text(buildAnnotatedString {
append("Buy for ")
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(price)
}
append(" or ")
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append("60 Gems")
}
}, color = Color.White)
PotionGrid()
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
onPurchaseClick()
MainScope().launchCatching {
activity?.finish()
delay(500)
MainNavigationController.navigate(R.id.marketFragment)
}
},
modifier = Modifier.padding(top = 20.dp)
) {
Text(stringResource(R.string.buy_for_x, ""))
Text(stringResource(R.string.visit_the_market))
}
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
onGemPurchaseClick()
},
modifier = Modifier.padding(top = 20.dp)
BirthdayTitle(stringResource(id = R.string.for_for_free))
Text(
stringResource(R.string.for_for_free_description),
fontSize = 16.sp,
color = textColor,
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.padding(vertical = 20.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.buy_for))
CurrencyText(currency = "gems", value = 60)
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 1,
title = stringResource(R.string.a_party_robe),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 1,
title = stringResource(R.string.twenty_gems),
imageName = "",
modifier = Modifier.weight(1f)
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 5,
title = stringResource(R.string.birthday_set),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 10,
title = stringResource(R.string.background),
imageName = "",
modifier = Modifier.weight(1f)
)
}
}
}
BirthdayTitle(stringResource(id = R.string.plenty_of_potions))
Text(
stringResource(R.string.plenty_of_potions_description),
fontSize = 16.sp,
color = textColor,
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
PotionGrid()
HabiticaButton(
Color.White,
colorResource(R.color.brand_200),
{
MainScope().launchCatching {
activity?.finish()
delay(500)
MainNavigationController.navigate(R.id.marketFragment)
}
},
modifier = Modifier.padding(top = 20.dp)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(top = 20.dp)
.background(colorResource(R.color.brand_50))
.padding(horizontal = 20.dp)
.padding(top = 20.dp, bottom = 60.dp)
) {
Text(stringResource(R.string.visit_the_market))
Text(
stringResource(R.string.limitations),
fontSize = 16.sp,
color = colorResource(R.color.brand_600),
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
Text(
stringResource(R.string.birthday_limitations),
fontSize = 14.sp,
color = colorResource(R.color.brand_600),
lineHeight = 20.sp,
textAlign = TextAlign.Center
)
}
BirthdayTitle(stringResource(id = R.string.for_for_free))
Text(
stringResource(R.string.for_for_free_description),
fontSize = 16.sp,
color = textColor,
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
Column(verticalArrangement = Arrangement.spacedBy(14.dp), modifier = Modifier.padding(vertical = 20.dp)) {
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 1,
title = stringResource(R.string.a_party_robe),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 1,
title = stringResource(R.string.twenty_gems),
imageName = "",
modifier = Modifier.weight(1f)
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 5,
title = stringResource(R.string.birthday_set),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 10,
title = stringResource(R.string.background),
imageName = "",
modifier = Modifier.weight(1f)
)
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(top = 20.dp)
.background(colorResource(R.color.brand_50))
.padding(horizontal = 20.dp)
.padding(top = 20.dp, bottom = 60.dp)
) {
Text(
stringResource(R.string.limitations),
fontSize = 16.sp,
color = colorResource(R.color.brand_600),
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
Text(
stringResource(R.string.birthday_limitations),
fontSize = 14.sp,
color = colorResource(R.color.brand_600),
lineHeight = 20.sp,
textAlign = TextAlign.Center
)
}
}
}
@ -532,9 +564,10 @@ fun HabiticaButton(
}
@Preview(device = Devices.PIXEL_4)
@Preview(device = Devices.PIXEL_4, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
BirthdayActivityView(true, false, "", Date(), Date(), {
}, {}, {})
val scaffoldState = rememberScaffoldState()
BirthdayActivityView(scaffoldState, true, false, "", Date(), Date(), {
}, {}) {}
}

View file

@ -199,7 +199,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
binding?.guildSummary?.setMarkdown(guild?.summary)
binding?.guildDescription?.setMarkdown(guild?.description)
binding?.inviteButton?.isVisible = guild?.isGroupPlan == true
binding?.inviteButton?.isVisible = guild?.isGroupPlan != true
}
companion object {

View file

@ -4,27 +4,27 @@ import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.local.TaskLocalRepository
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.shared.habitica.models.responses.TaskDirectionData
import com.habitrpg.shared.habitica.models.responses.TaskScoringResult
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.tasks.TaskList
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.shared.habitica.models.tasks.TasksOrder
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.shared.habitica.models.responses.TaskDirectionData
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.shared.habitica.models.tasks.TasksOrder
import io.kotest.common.ExperimentalKotest
import io.kotest.core.spec.style.WordSpec
import io.kotest.framework.concurrency.eventually
import io.kotest.matchers.shouldBe
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.spyk
import io.mockk.verify
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.subscribers.TestSubscriber
import io.realm.Realm
import kotlinx.coroutines.flow.flowOf
import java.util.UUID
@OptIn(ExperimentalKotest::class)
@ -52,12 +52,10 @@ class TaskRepositoryImplTest : WordSpec({
"retrieveTasks" should {
"save tasks locally" {
val list = TaskList()
every { apiClient.tasks } returns Flowable.just(list)
coEvery { apiClient.getTasks() } returns list
every { localRepository.saveTasks("", any(), any()) } returns Unit
val order = TasksOrder()
val subscriber = TestSubscriber<TaskList>()
repository.retrieveTasks("", order).subscribe(subscriber)
subscriber.assertComplete()
repository.retrieveTasks("", order)
verify { localRepository.saveTasks("", order, list) }
}
}
@ -70,32 +68,25 @@ class TaskRepositoryImplTest : WordSpec({
user.stats = Stats()
}
"debounce" {
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(
TaskDirectionData()
)
repository.taskChecked(user, task, true, false, null).subscribe()
repository.taskChecked(user, task, true, false, null).subscribe()
verify(exactly = 1) { apiClient.postTaskDirection(any(), any()) }
coEvery { apiClient.postTaskDirection(any(), "up") } returns TaskDirectionData()
repository.taskChecked(user, task, true, false, null)
repository.taskChecked(user, task, true, false, null)
coVerify(exactly = 1) { apiClient.postTaskDirection(any(), any()) }
}
"get user if not passed" {
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(
TaskDirectionData()
)
every { localRepository.getUserFlowable("") } returns Flowable.just(user)
coEvery { apiClient.postTaskDirection(any(), "up") } returns TaskDirectionData()
coEvery { localRepository.getUser("") } returns flowOf(user)
repository.taskChecked(null, task, true, false, null)
eventually(5000) {
localRepository.getUserFlowable("")
localRepository.getUser("")
}
}
"does not update user for team tasks" {
val data = TaskDirectionData()
data.lvl = 0
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
val subscriber = TestSubscriber<TaskScoringResult>()
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
repository.taskChecked(user, task, true, false, null)
verify(exactly = 0) { user.stats }
subscriber.values().first().level shouldBe null
}
"builds task result correctly" {
val data = TaskDirectionData()
@ -106,67 +97,53 @@ class TaskRepositoryImplTest : WordSpec({
user.stats?.lvl = 10
user.stats?.hp = 8.0
user.stats?.mp = 4.0
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
val subscriber = TestSubscriber<TaskScoringResult>()
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
subscriber.values().first().level shouldBe 10
subscriber.values().first().healthDelta shouldBe 12.0
subscriber.values().first().manaDelta shouldBe 26.0
subscriber.values().first().hasLeveledUp shouldBe false
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
val result = repository.taskChecked(user, task, true, false, null)
result?.level shouldBe 10
result?.healthDelta shouldBe 12.0
result?.manaDelta shouldBe 26.0
result?.hasLeveledUp shouldBe false
}
"set hasLeveledUp correctly" {
val subscriber = TestSubscriber<TaskScoringResult>()
val data = TaskDirectionData()
data.lvl = 11
user.stats?.lvl = 10
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
subscriber.values().first().level shouldBe 11
subscriber.values().first().hasLeveledUp shouldBe true
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
val result = repository.taskChecked(user, task, true, false, null)
result?.level shouldBe 11
result?.hasLeveledUp shouldBe true
}
"handle stats not being there" {
val subscriber = TestSubscriber<TaskScoringResult>()
val data = TaskDirectionData()
data.lvl = 1
user.stats = null
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
repository.taskChecked(user, task, true, false, null)
}
"update daily streak" {
val subscriber = TestSubscriber<TaskScoringResult>()
val data = TaskDirectionData()
data.delta = 1.0f
data.lvl = 1
task.type = TaskType.DAILY
task.value = 0.0
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
repository.taskChecked(user, task, true, false, null)
task.streak shouldBe 1
task.completed shouldBe true
}
"update habit counter" {
val subscriber = TestSubscriber<TaskScoringResult>()
val data = TaskDirectionData()
data.delta = 1.0f
data.lvl = 1
task.type = TaskType.HABIT
task.value = 0.0
every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just(data)
repository.taskChecked(user, task, true, false, null).subscribe(subscriber)
subscriber.assertComplete()
coEvery { apiClient.postTaskDirection(any(), "up") } returns data
repository.taskChecked(user, task, true, false, null)
task.counterUp shouldBe 1
data.delta = -10.0f
every { apiClient.postTaskDirection(any(), "down") } returns Flowable.just(data)
val downSubscriber = TestSubscriber<TaskScoringResult>()
repository.taskChecked(user, task, false, true, null).subscribe(downSubscriber)
downSubscriber.assertComplete()
coEvery { apiClient.postTaskDirection(any(), "down") } returns data
repository.taskChecked(user, task, false, true, null)
task.counterUp shouldBe 1
task.counterDown shouldBe 1
}