Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ android {
}

}

packaging {
resources {
excludes += "/META-INF/LICENSE.md"
excludes += "/META-INF/LICENSE-notice.md"
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand Down Expand Up @@ -221,6 +229,8 @@ dependencies {
testImplementation(libs.androidx.junit.runner)
testImplementation(libs.androidx.junit.ktx)
androidTestImplementation(libs.turbine)
androidTestImplementation(libs.mockk.android)
androidTestImplementation(libs.mockk.agent)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.ui.test.junit4)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package wallet.android.app.features.fiat

import app.cash.turbine.test
import com.gemwallet.android.data.repositoreis.assets.AssetsRepository
import com.gemwallet.android.data.repositoreis.buy.BuyRepository
import com.gemwallet.android.data.repositoreis.session.SessionRepository
import com.gemwallet.android.features.buy.viewmodels.FiatSceneState
import com.gemwallet.android.features.buy.viewmodels.FiatViewModel
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test

@ExperimentalCoroutinesApi
class FiatViewModelTest {

private lateinit var viewModel: FiatViewModel
private val sessionRepository: SessionRepository = mockk()
private val assetsRepository: AssetsRepository = mockk()
private val buyRepository: BuyRepository = mockk()

@Before
fun setup() {
viewModel = FiatViewModel(
sessionRepository = sessionRepository,
assetsRepository = assetsRepository,
buyRepository = buyRepository,
savedStateHandle = mockk(relaxed = true)
)
}

@Test
fun testInitialState() = runTest {
viewModel.state.test {
assertNull(awaitItem())
}
}

@Test
fun testDefaultAmountText() = runTest {
viewModel.amount.test {
assertEquals(viewModel.defaultAmount, awaitItem())
}
}

@Test
fun testUpdateAmount() = runTest {
viewModel.amount.test {
viewModel.updateAmount("150")
skipItems(1)
assertEquals("150", awaitItem())
}
}

@Test
fun testMinimumAmountErrorState() = runTest {
viewModel.state.test {
viewModel.updateAmount((FiatViewModel.MIN_FIAT_AMOUNT - 1).toString())
skipItems(1)
assert(awaitItem() is FiatSceneState.Error)
}
}

@Test
fun testValidAmountSetsLoadingState() = runTest {
viewModel.state.test {
viewModel.updateAmount("100")
skipItems(1)
assert(awaitItem() is FiatSceneState.Loading)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.gemwallet.android.R
import com.gemwallet.android.features.asset_select.viewmodels.BuySelectViewModel
import com.gemwallet.android.ui.components.getBalanceInfo
import com.gemwallet.android.ui.models.actions.CancelAction
import com.wallet.core.primitives.AssetId

@Composable
fun SelectBuyScreen(
onCancel: () -> Unit,
cancelAction: CancelAction,
onSelect: ((AssetId) -> Unit)?,
viewModel: BuySelectViewModel = hiltViewModel()
) {
Expand All @@ -19,7 +20,9 @@ fun SelectBuyScreen(
titleBadge = { null },
itemTrailing = { getBalanceInfo(it)() },
onSelect = onSelect,
onCancel = onCancel,
onCancel = {
cancelAction.invoke()
},
viewModel = viewModel,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.gemwallet.android.features.buy.models

import androidx.compose.runtime.Stable
import com.gemwallet.android.model.format
import com.gemwallet.android.ui.models.CryptoFormattedUIModel
import com.wallet.core.primitives.Asset
import com.wallet.core.primitives.Currency
import com.wallet.core.primitives.FiatProvider
import com.wallet.core.primitives.FiatQuote

@Stable
data class BuyFiatProviderUIModel(
val provider: FiatProvider,
override val asset: Asset,
override val cryptoAmount: Double,
val rate: String,
val redirectUrl: String?,
) : CryptoFormattedUIModel {
override val cryptoFormatted: String
get() = "≈ ${super.cryptoFormatted}"
}

fun FiatQuote.toProviderUIModel(
asset: Asset,
currency: Currency,
) = BuyFiatProviderUIModel(
provider = provider,
asset = asset,
cryptoAmount = cryptoAmount,
rate = asset.rateText(fiatAmount, cryptoAmount, currency),
redirectUrl = redirectUrl
)

private fun Asset.rateText(fiatAmount: Double, cryptoAmount: Double, currency: Currency) =
"1 $symbol ≈ ${currency.format(fiatAmount / cryptoAmount).format(currency.string, 2)}"


This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,47 @@ package com.gemwallet.android.features.buy.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import androidx.navigation.navOptions
import androidx.navigation.navigation
import com.gemwallet.android.ext.toAssetId
import com.gemwallet.android.ext.toIdentifier
import com.gemwallet.android.ext.urlDecode
import com.gemwallet.android.ext.urlEncode
import com.gemwallet.android.features.asset_select.views.SelectBuyScreen
import com.gemwallet.android.features.buy.views.BuyScreen
import com.gemwallet.android.features.buy.views.FiatScreen
import com.gemwallet.android.ui.models.actions.CancelAction
import com.wallet.core.primitives.AssetId
import kotlinx.serialization.Serializable

internal const val assetIdArg = "assetId"
const val buyRoute = "buy"
const val buySelectAssetRoute = "buySelectAsset"
@Serializable
data class FiatInput(val assetId: String)

@Serializable
object FiatSelect

@Serializable
object Fiat

fun NavController.navigateToBuyScreen(assetId: AssetId? = null, navOptions: NavOptions? = null) {
if (assetId == null) {
navigate(buySelectAssetRoute, navOptions ?: navOptions { launchSingleTop = true })
navigate(FiatSelect, navOptions ?: navOptions { launchSingleTop = true })
} else {
navigate("$buyRoute/${assetId.toIdentifier().urlEncode()}", navOptions ?: navOptions { launchSingleTop = true })
navigate(FiatInput(assetId.toIdentifier()), navOptions ?: navOptions { launchSingleTop = true })
}
}

fun NavGraphBuilder.buyScreen(
onCancel: () -> Unit,
fun NavGraphBuilder.fiatScreen(
cancelAction: CancelAction,
onBuy: (AssetId) -> Unit,
) {
navigation("$buyRoute/{$assetIdArg}", buyRoute) {
composable(
route = "$buyRoute/{$assetIdArg}",
arguments = listOf(
navArgument(assetIdArg) {
type = NavType.StringType
nullable = true
},
navigation<Fiat>(startDestination = FiatSelect) {
composable<FiatInput> {
FiatScreen(
cancelAction = cancelAction
)
) {
val assetId = it.arguments?.getString(assetIdArg)?.urlDecode()?.toAssetId()
if (assetId == null) {
onCancel()
} else {
BuyScreen(
assetId = assetId,
onCancel = onCancel
)
}
}

composable(buySelectAssetRoute) {
composable<FiatSelect> {
SelectBuyScreen(
onCancel = onCancel,
cancelAction = cancelAction,
onSelect = {
onBuy(it)
}
Expand Down
Loading
Loading