From dc80c7625cf0a31726a3c1f4f3d258b289ff503d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 May 2026 11:14:47 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../impl/amount/SwapAmountBlockComponent.kt | 9 +- .../v2/impl/amount/model/SwapAmountModel.kt | 11 +- .../SwapChooseProviderComponent.kt | 5 +- .../model/SwapChooseProviderModel.kt | 4 +- .../SwapProviderListItemConverter.kt | 20 ++- .../converter/SwapProviderStateConverter.kt | 16 +- .../common/AmountErrorCurrencyResolver.kt | 13 ++ .../SwapNotificationsComponent.kt | 4 +- .../model/SwapNotificationsModel.kt | 12 +- .../confirm/SendWithSwapConfirmComponent.kt | 2 +- .../confirm/model/SendWithSwapConfirmModel.kt | 2 +- .../SwapProviderListItemConverterTest.kt | 143 ++++++++++++++++++ .../SwapProviderStateConverterTest.kt | 135 +++++++++++++++++ .../common/AmountErrorCurrencyResolverTest.kt | 52 +++++++ 14 files changed, 405 insertions(+), 23 deletions(-) create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolver.kt create mode 100644 features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverterTest.kt create mode 100644 features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverterTest.kt create mode 100644 features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolverTest.kt diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index 4df2de3238..e4c1071209 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -17,6 +17,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.settings.usercountry.models.UserCountry +import com.tangem.domain.swap.models.SwapAmountType import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams.AmountBlockParams import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel @@ -82,7 +83,9 @@ internal class SwapAmountBlockComponent( context = childByContext(componentContext), params = SwapChooseProviderComponent.Params( providers = config.providers, - cryptoCurrency = config.cryptoCurrency, + fromCryptoCurrency = config.fromCryptoCurrency, + toCryptoCurrency = config.toCryptoCurrency, + amountType = config.amountType, selectedProvider = config.selectedProvider, userCountry = config.userCountry, callback = model, @@ -93,7 +96,9 @@ internal class SwapAmountBlockComponent( data class SwapChooseProviderConfig( val providers: ImmutableList, - val cryptoCurrency: CryptoCurrency, + val fromCryptoCurrency: CryptoCurrency, + val toCryptoCurrency: CryptoCurrency, + val amountType: SwapAmountType, val selectedProvider: ExpressProvider, val userCountry: UserCountry, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 59de970d62..2bcecfac64 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -375,7 +375,12 @@ internal class SwapAmountModel @Inject constructor( override fun onProviderClick() { val amountUM = uiState.value as? SwapAmountUM.Content ?: return val selectedProvider = amountUM.selectedQuote.provider ?: return - val cryptoCurrency = params.secondaryCryptoCurrency ?: return + val secondaryStatus = amountUM.secondaryCryptoCurrencyStatus ?: return + + val (fromCryptoCurrency, toCryptoCurrency) = amountUM.swapDirection.withSwapDirection( + onDirect = { amountUM.primaryCryptoCurrencyStatus.currency to secondaryStatus.currency }, + onReverse = { secondaryStatus.currency to amountUM.primaryCryptoCurrencyStatus.currency }, + ) analyticsEventHandler.send( SwapAmountAnalyticEvents.ProviderSelectorClicked( @@ -386,7 +391,9 @@ internal class SwapAmountModel @Inject constructor( bottomSheetNavigation.activate( SwapChooseProviderConfig( providers = amountUM.swapQuotes, - cryptoCurrency = cryptoCurrency, + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + amountType = amountUM.selectedAmountType, selectedProvider = selectedProvider, userCountry = userCountry, ), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt index c04a88654c..351e5cbad0 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt @@ -11,6 +11,7 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.settings.usercountry.models.UserCountry +import com.tangem.domain.swap.models.SwapAmountType import com.tangem.features.swap.v2.impl.chooseprovider.model.SwapChooseProviderModel import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderBottomSheet import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent @@ -49,7 +50,9 @@ internal class SwapChooseProviderComponent( } data class Params( - val cryptoCurrency: CryptoCurrency, + val fromCryptoCurrency: CryptoCurrency, + val toCryptoCurrency: CryptoCurrency, + val amountType: SwapAmountType, val selectedProvider: ExpressProvider, val providers: ImmutableList, val userCountry: UserCountry, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt index dc784cc913..9d550636df 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt @@ -29,7 +29,9 @@ internal class SwapChooseProviderModel @Inject constructor( private val swapProviderListItemConverter by lazy(LazyThreadSafetyMode.NONE) { SwapProviderListItemConverter( - cryptoCurrency = params.cryptoCurrency, + fromCryptoCurrency = params.fromCryptoCurrency, + toCryptoCurrency = params.toCryptoCurrency, + amountType = params.amountType, selectedProvider = params.selectedProvider, isNeedApplyFCARestrictions = isNeedApplyFCARestrictions, needBestRateBadge = params.providers.filterIsInstance().isSingleItem().not(), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt index 7383f568e4..8efc25af94 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt @@ -12,21 +12,33 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.express.models.ExpressError import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA +import com.tangem.features.swap.v2.impl.common.resolveAmountErrorCurrency import com.tangem.utils.converter.Converter internal class SwapProviderListItemConverter( - private val cryptoCurrency: CryptoCurrency, + private val fromCryptoCurrency: CryptoCurrency, + private val toCryptoCurrency: CryptoCurrency, + private val amountType: SwapAmountType, private val selectedProvider: ExpressProvider, private val isNeedApplyFCARestrictions: Boolean, needBestRateBadge: Boolean, ) : Converter { + private val amountErrorCurrency: CryptoCurrency = resolveAmountErrorCurrency( + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + amountType = amountType, + ) + private val providerStateConverter = SwapProviderStateConverter( - cryptoCurrency = cryptoCurrency, + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + amountType = amountType, selectedProvider = selectedProvider, isNeedApplyFCARestrictions = isNeedApplyFCARestrictions, isNeedBestRateBadge = needBestRateBadge, @@ -63,7 +75,7 @@ internal class SwapProviderListItemConverter( is ExpressError.AmountError.TooSmallError -> resourceReference( id = R.string.express_provider_min_amount, formatArgs = wrappedList( - error.amount.format { crypto(cryptoCurrency) }, + error.amount.format { crypto(amountErrorCurrency) }, ), ) is ExpressError.AmountError.NotEnoughAllowanceError, @@ -71,7 +83,7 @@ internal class SwapProviderListItemConverter( -> resourceReference( id = R.string.express_provider_max_amount, formatArgs = wrappedList( - error.amount.format { crypto(cryptoCurrency) }, + error.amount.format { crypto(amountErrorCurrency) }, ), ) else -> TextReference.EMPTY diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt index a591206770..76db9382b1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt @@ -8,21 +8,31 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.express.models.ExpressError import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState.AdditionalBadge import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA +import com.tangem.features.swap.v2.impl.common.resolveAmountErrorCurrency import com.tangem.utils.converter.Converter @Deprecated("Remove with new design") internal class SwapProviderStateConverter( - private val cryptoCurrency: CryptoCurrency, + private val fromCryptoCurrency: CryptoCurrency, + private val toCryptoCurrency: CryptoCurrency, + private val amountType: SwapAmountType, private val selectedProvider: ExpressProvider, private val isNeedBestRateBadge: Boolean, private val isNeedApplyFCARestrictions: Boolean, ) : Converter { + private val amountErrorCurrency: CryptoCurrency = resolveAmountErrorCurrency( + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + amountType = amountType, + ) + override fun convert(value: SwapQuoteUM): SwapProviderState { return when (value) { is SwapQuoteUM.Content -> value.convertToContent() @@ -71,7 +81,7 @@ internal class SwapProviderStateConverter( is ExpressError.AmountError.TooSmallError -> resourceReference( id = R.string.express_provider_min_amount, formatArgs = wrappedList( - error.amount.format { crypto(cryptoCurrency) }, + error.amount.format { crypto(amountErrorCurrency) }, ), ) is ExpressError.AmountError.NotEnoughAllowanceError, @@ -79,7 +89,7 @@ internal class SwapProviderStateConverter( -> resourceReference( id = R.string.express_provider_max_amount, formatArgs = wrappedList( - error.amount.format { crypto(cryptoCurrency) }, + error.amount.format { crypto(amountErrorCurrency) }, ), ) else -> TextReference.EMPTY diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolver.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolver.kt new file mode 100644 index 0000000000..3420a9673e --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolver.kt @@ -0,0 +1,13 @@ +package com.tangem.features.swap.v2.impl.common + +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType + +internal fun resolveAmountErrorCurrency( + fromCryptoCurrency: CryptoCurrency, + toCryptoCurrency: CryptoCurrency, + amountType: SwapAmountType?, +): CryptoCurrency = when (amountType) { + SwapAmountType.To -> toCryptoCurrency + SwapAmountType.From, null -> fromCryptoCurrency +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/SwapNotificationsComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/SwapNotificationsComponent.kt index c7e62ed4f3..9825830ed6 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/SwapNotificationsComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/SwapNotificationsComponent.kt @@ -7,10 +7,10 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.domain.express.models.ExpressError import com.tangem.domain.express.models.ExpressProvider -import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.swap.models.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.PriceImpact import java.math.BigDecimal import com.tangem.features.swap.v2.impl.notifications.model.SwapNotificationsModel @@ -54,7 +54,7 @@ internal class SwapNotificationsComponent( val fromCryptoCurrencyStatus: CryptoCurrencyStatus? = null, val priceImpact: PriceImpact? = null, val provider: ExpressProvider? = null, - val rateType: ExpressRateType? = null, + val amountType: SwapAmountType? = null, val shouldIncludeFeeInBalanceCheck: Boolean = false, val feeValue: BigDecimal? = null, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt index d82b8ace85..c8e796d663 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/notifications/model/SwapNotificationsModel.kt @@ -8,10 +8,10 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.express.models.ExpressError -import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.transaction.usecase.IsMemoRequiredUseCase import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger import com.tangem.features.swap.v2.impl.amount.entity.PriceImpact +import com.tangem.features.swap.v2.impl.common.resolveAmountErrorCurrency import com.tangem.features.swap.v2.impl.notifications.DefaultSwapNotificationsUpdateTrigger import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent.Params.SwapNotificationData @@ -152,11 +152,11 @@ internal class SwapNotificationsModel @Inject constructor( val fromCryptoCurrency = notificationData.fromCryptoCurrency ?: return val toCryptoCurrency = notificationData.toCryptoCurrencyStatus?.currency ?: return - val amountErrorCurrency = if (notificationData.rateType == ExpressRateType.Fixed) { - toCryptoCurrency - } else { - fromCryptoCurrency - } + val amountErrorCurrency = resolveAmountErrorCurrency( + fromCryptoCurrency = fromCryptoCurrency, + toCryptoCurrency = toCryptoCurrency, + amountType = notificationData.amountType, + ) val errorNotification = when (expressError) { is ExpressError.AmountError.TooSmallError -> SwapNotificationUM.Error.MinimalAmountError( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index 387432f1fd..332b0b63f3 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -141,7 +141,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor( enteredFromAmount = model.confirmData.enteredFromAmount, fromCryptoCurrencyStatus = model.confirmData.fromCryptoCurrencyStatus, priceImpact = model.confirmData.priceImpact, - rateType = model.confirmData.rateType, + amountType = model.confirmData.amountType, ), ), ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt index 5785695c59..7e59abe87e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt @@ -460,7 +460,7 @@ internal class SendWithSwapConfirmModel @Inject constructor( fromCryptoCurrencyStatus = confirmData.fromCryptoCurrencyStatus, priceImpact = confirmData.priceImpact, provider = confirmData.quote?.provider, - rateType = confirmData.rateType, + amountType = confirmData.amountType, shouldIncludeFeeInBalanceCheck = isFixedRate && isAmountSubtractAvailable, feeValue = confirmData.fee?.amount?.value, ), diff --git a/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverterTest.kt b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverterTest.kt new file mode 100644 index 0000000000..502f29354c --- /dev/null +++ b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverterTest.kt @@ -0,0 +1,143 @@ +package com.tangem.features.swap.v2.impl.chooseprovider.model.converter + +import com.google.common.truth.Truth.assertThat +import com.tangem.core.ui.components.provider.entity.ProviderChooseUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.express.models.ExpressError +import com.tangem.domain.express.models.ExpressProvider +import com.tangem.domain.express.models.ExpressProviderType +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import io.mockk.every +import io.mockk.mockk +import org.junit.Test +import java.math.BigDecimal + +internal class SwapProviderListItemConverterTest { + + private val provider = ExpressProvider( + providerId = "p1", + name = "Test Provider", + type = ExpressProviderType.CEX, + imageLarge = "", + termsOfUse = null, + privacyPolicy = null, + slippage = null, + ) + + private val fromCurrency = mockk(relaxed = true).also { + every { it.symbol } returns FROM_SYMBOL + every { it.decimals } returns 18 + } + + private val toCurrency = mockk(relaxed = true).also { + every { it.symbol } returns TO_SYMBOL + every { it.decimals } returns 8 + } + + @Test + fun `GIVEN quote with TooSmallError and amountType To WHEN convert THEN error text uses to symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.To) + val errorQuote = errorQuote(ExpressError.AmountError.TooSmallError(code = 1, amount = BigDecimal("1.5"))) + + // WHEN + val item = converter.convert(errorQuote) + + // THEN + assertThat(item).isNotNull() + val errorText = (item!!.providerUM.extraUM as ProviderChooseUM.ExtraUM.Error).text + assertSymbolUsed(errorText, expectedSymbol = TO_SYMBOL, otherSymbol = FROM_SYMBOL) + } + + @Test + fun `GIVEN quote with TooSmallError and amountType From WHEN convert THEN error text uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote(ExpressError.AmountError.TooSmallError(code = 1, amount = BigDecimal("1.5"))) + + // WHEN + val item = converter.convert(errorQuote) + + // THEN + assertThat(item).isNotNull() + val errorText = (item!!.providerUM.extraUM as ProviderChooseUM.ExtraUM.Error).text + assertSymbolUsed(errorText, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + @Test + fun `GIVEN quote with TooBigError and amountType To WHEN convert THEN error text uses to symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.To) + val errorQuote = errorQuote(ExpressError.AmountError.TooBigError(code = 2, amount = BigDecimal("999"))) + + // WHEN + val item = converter.convert(errorQuote) + + // THEN + assertThat(item).isNotNull() + val errorText = (item!!.providerUM.extraUM as ProviderChooseUM.ExtraUM.Error).text + assertSymbolUsed(errorText, expectedSymbol = TO_SYMBOL, otherSymbol = FROM_SYMBOL) + } + + @Test + fun `GIVEN quote with TooBigError and amountType From WHEN convert THEN error text uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote(ExpressError.AmountError.TooBigError(code = 2, amount = BigDecimal("999"))) + + // WHEN + val item = converter.convert(errorQuote) + + // THEN + assertThat(item).isNotNull() + val errorText = (item!!.providerUM.extraUM as ProviderChooseUM.ExtraUM.Error).text + assertSymbolUsed(errorText, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + @Test + fun `GIVEN quote with NotEnoughAllowanceError and amountType From WHEN convert THEN error text uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote( + ExpressError.AmountError.NotEnoughAllowanceError(code = 3, amount = BigDecimal("10")), + ) + + // WHEN + val item = converter.convert(errorQuote) + + // THEN + assertThat(item).isNotNull() + val errorText = (item!!.providerUM.extraUM as ProviderChooseUM.ExtraUM.Error).text + assertSymbolUsed(errorText, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + private fun buildConverter(amountType: SwapAmountType): SwapProviderListItemConverter { + return SwapProviderListItemConverter( + fromCryptoCurrency = fromCurrency, + toCryptoCurrency = toCurrency, + amountType = amountType, + selectedProvider = provider, + isNeedApplyFCARestrictions = false, + needBestRateBadge = false, + ) + } + + private fun errorQuote(error: ExpressError): SwapQuoteUM.Error = SwapQuoteUM.Error( + provider = provider, + expressError = error, + ) + + private fun assertSymbolUsed(text: TextReference, expectedSymbol: String, otherSymbol: String) { + val res = text as TextReference.Res + val formatted = res.formatArgs.first().toString() + assertThat(formatted).contains(expectedSymbol) + assertThat(formatted).doesNotContain(otherSymbol) + } + + private companion object { + const val FROM_SYMBOL = "ETH" + const val TO_SYMBOL = "BTC" + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverterTest.kt b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverterTest.kt new file mode 100644 index 0000000000..9b01581b84 --- /dev/null +++ b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverterTest.kt @@ -0,0 +1,135 @@ +package com.tangem.features.swap.v2.impl.chooseprovider.model.converter + +import com.google.common.truth.Truth.assertThat +import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.express.models.ExpressError +import com.tangem.domain.express.models.ExpressProvider +import com.tangem.domain.express.models.ExpressProviderType +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType +import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import io.mockk.every +import io.mockk.mockk +import org.junit.Test +import java.math.BigDecimal + +@Suppress("DEPRECATION") +internal class SwapProviderStateConverterTest { + + private val provider = ExpressProvider( + providerId = "p1", + name = "Test Provider", + type = ExpressProviderType.CEX, + imageLarge = "", + termsOfUse = null, + privacyPolicy = null, + slippage = null, + ) + + private val fromCurrency = mockk(relaxed = true).also { + every { it.symbol } returns FROM_SYMBOL + every { it.decimals } returns 18 + } + + private val toCurrency = mockk(relaxed = true).also { + every { it.symbol } returns TO_SYMBOL + every { it.decimals } returns 8 + } + + @Test + fun `GIVEN error quote TooSmallError and amountType To WHEN convert THEN subtitle uses to symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.To) + val errorQuote = errorQuote(ExpressError.AmountError.TooSmallError(code = 1, amount = BigDecimal("1.5"))) + + // WHEN + val state = converter.convert(errorQuote) + + // THEN + assertSymbolUsed(state, expectedSymbol = TO_SYMBOL, otherSymbol = FROM_SYMBOL) + } + + @Test + fun `GIVEN error quote TooSmallError and amountType From WHEN convert THEN subtitle uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote(ExpressError.AmountError.TooSmallError(code = 1, amount = BigDecimal("1.5"))) + + // WHEN + val state = converter.convert(errorQuote) + + // THEN + assertSymbolUsed(state, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + @Test + fun `GIVEN error quote TooBigError and amountType To WHEN convert THEN subtitle uses to symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.To) + val errorQuote = errorQuote(ExpressError.AmountError.TooBigError(code = 2, amount = BigDecimal("999"))) + + // WHEN + val state = converter.convert(errorQuote) + + // THEN + assertSymbolUsed(state, expectedSymbol = TO_SYMBOL, otherSymbol = FROM_SYMBOL) + } + + @Test + fun `GIVEN error quote TooBigError and amountType From WHEN convert THEN subtitle uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote(ExpressError.AmountError.TooBigError(code = 2, amount = BigDecimal("999"))) + + // WHEN + val state = converter.convert(errorQuote) + + // THEN + assertSymbolUsed(state, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + @Test + fun `GIVEN error quote NotEnoughAllowanceError and amountType From WHEN convert THEN subtitle uses from symbol`() { + // GIVEN + val converter = buildConverter(amountType = SwapAmountType.From) + val errorQuote = errorQuote( + ExpressError.AmountError.NotEnoughAllowanceError(code = 3, amount = BigDecimal("10")), + ) + + // WHEN + val state = converter.convert(errorQuote) + + // THEN + assertSymbolUsed(state, expectedSymbol = FROM_SYMBOL, otherSymbol = TO_SYMBOL) + } + + private fun buildConverter(amountType: SwapAmountType): SwapProviderStateConverter { + return SwapProviderStateConverter( + fromCryptoCurrency = fromCurrency, + toCryptoCurrency = toCurrency, + amountType = amountType, + selectedProvider = provider, + isNeedBestRateBadge = false, + isNeedApplyFCARestrictions = false, + ) + } + + private fun errorQuote(error: ExpressError): SwapQuoteUM.Error = SwapQuoteUM.Error( + provider = provider, + expressError = error, + ) + + private fun assertSymbolUsed(state: SwapProviderState, expectedSymbol: String, otherSymbol: String) { + val content = state as SwapProviderState.Content + val subtitle = content.subtitle as TextReference.Res + val formatted = subtitle.formatArgs.first().toString() + assertThat(formatted).contains(expectedSymbol) + assertThat(formatted).doesNotContain(otherSymbol) + } + + private companion object { + const val FROM_SYMBOL = "ETH" + const val TO_SYMBOL = "BTC" + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolverTest.kt b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolverTest.kt new file mode 100644 index 0000000000..88ec6fdb10 --- /dev/null +++ b/features/swap-v2/impl/src/test/java/com/tangem/features/swap/v2/impl/common/AmountErrorCurrencyResolverTest.kt @@ -0,0 +1,52 @@ +package com.tangem.features.swap.v2.impl.common + +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapAmountType +import io.mockk.mockk +import org.junit.Test + +internal class AmountErrorCurrencyResolverTest { + + private val from = mockk(relaxed = true) + private val to = mockk(relaxed = true) + + @Test + fun `GIVEN amountType From WHEN resolveAmountErrorCurrency THEN returns fromCryptoCurrency`() { + // GIVEN, WHEN + val result = resolveAmountErrorCurrency( + fromCryptoCurrency = from, + toCryptoCurrency = to, + amountType = SwapAmountType.From, + ) + + // THEN + assertThat(result).isSameInstanceAs(from) + } + + @Test + fun `GIVEN amountType To WHEN resolveAmountErrorCurrency THEN returns toCryptoCurrency`() { + // GIVEN, WHEN + val result = resolveAmountErrorCurrency( + fromCryptoCurrency = from, + toCryptoCurrency = to, + amountType = SwapAmountType.To, + ) + + // THEN + assertThat(result).isSameInstanceAs(to) + } + + @Test + fun `GIVEN amountType null WHEN resolveAmountErrorCurrency THEN returns fromCryptoCurrency`() { + // GIVEN, WHEN + val result = resolveAmountErrorCurrency( + fromCryptoCurrency = from, + toCryptoCurrency = to, + amountType = null, + ) + + // THEN + assertThat(result).isSameInstanceAs(from) + } +} \ No newline at end of file