Updated on 2026-08-14
This commit is contained in:
parent
afbdca53fe
commit
edee792251
9 changed files with 265 additions and 18 deletions
|
|
@ -2,6 +2,8 @@ package com.tangem.features.foryou.impl.model
|
|||
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
|
|
@ -21,15 +23,16 @@ import com.tangem.domain.models.TotalFiatBalance
|
|||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.earn.EarnError
|
||||
import com.tangem.domain.models.earn.EarnRewardType
|
||||
import com.tangem.domain.models.earn.EarnToken
|
||||
import com.tangem.domain.models.earn.EarnTokenWithCurrency
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.domain.models.currency.yieldSupplyKey
|
||||
import com.tangem.domain.models.earn.*
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.foryou.ForYouComponent
|
||||
import com.tangem.features.foryou.impl.components.state.MarketChartUM
|
||||
import com.tangem.features.foryou.impl.entity.EarnOpportunitiesUM
|
||||
|
|
@ -41,12 +44,7 @@ import com.tangem.pagination.BatchListState
|
|||
import com.tangem.pagination.PaginationStatus
|
||||
import com.tangem.test.mock.MockAccounts
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
@ -72,6 +70,8 @@ internal class ForYouModelTest {
|
|||
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase = mockk()
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase = mockk()
|
||||
private val earnErrorResolver: EarnErrorResolver = mockk()
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory = mockk()
|
||||
private val router: AppRouter = mockk(relaxUnitFun = true)
|
||||
|
||||
private var model: ForYouModel? = null
|
||||
|
||||
|
|
@ -286,9 +286,67 @@ internal class ForYouModelTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class EarnNavigation {
|
||||
|
||||
@Test
|
||||
fun `GIVEN held yield-eligible token WHEN earn row clicked THEN yield-supply entry route is pushed`() =
|
||||
runTest {
|
||||
// Arrange — the token's backend rate is 5.5%
|
||||
val token = createYieldToken()
|
||||
every { yieldSupplyApyFlowUseCase() } returns flowOf(mapOf(token.yieldSupplyKey() to BigDecimal("5.5")))
|
||||
stubSelectedWallet(currencies = listOf(createStatus(token, loadedValue(BigDecimal("100")))))
|
||||
val model = createModel(testScope = this)
|
||||
advanceUntilIdle()
|
||||
|
||||
// Act
|
||||
model.clickFirstEarnRow()
|
||||
|
||||
// Assert — the raw percent string travels into the route
|
||||
val expected = AppRoute.YieldSupplyEntry(
|
||||
userWalletId = WALLET_ID,
|
||||
cryptoCurrency = token,
|
||||
apy = "5.5",
|
||||
)
|
||||
verify { router.push(route = expected, onComplete = any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN held stakeable token WHEN earn row clicked THEN staking route is pushed`() = runTest {
|
||||
// Arrange
|
||||
val currency = createCoin(rawCurrencyId = "eth", symbol = "ETH")
|
||||
val option: StakingOption.P2PEthPool = mockk {
|
||||
every { apy } returns BigDecimal("0.05")
|
||||
every { integrationId } returns StakingIntegrationID.P2PEthPool
|
||||
}
|
||||
coEvery { stakingAvailabilityListUseCase.invokeSync(any(), any()) } returns
|
||||
mapOf(currency to StakingAvailability.Available(option))
|
||||
stubSelectedWallet(currencies = listOf(createStatus(currency, loadedValue(BigDecimal("100")))))
|
||||
val model = createModel(testScope = this)
|
||||
advanceUntilIdle()
|
||||
|
||||
// Act
|
||||
model.clickFirstEarnRow()
|
||||
|
||||
// Assert
|
||||
val expected = AppRoute.Staking(
|
||||
userWalletId = WALLET_ID,
|
||||
cryptoCurrency = currency,
|
||||
integrationId = StakingIntegrationID.P2PEthPool,
|
||||
)
|
||||
verify { router.push(route = expected, onComplete = any()) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun PortfolioReviewUM.Content.assetRow(): TangemTokenRowUM.Content =
|
||||
tokenList.single().tokenRowUM as TangemTokenRowUM.Content
|
||||
|
||||
private fun ForYouModel.clickFirstEarnRow() {
|
||||
val earn = uiState.value.earnOpportunities as EarnOpportunitiesUM.Content
|
||||
val row = earn.tokenList.first().tokenRowUM as TangemTokenRowUM.Content
|
||||
row.onItemClick?.invoke()
|
||||
}
|
||||
|
||||
/** Wires the repository + supplier so the model derives Content from a single selected wallet. */
|
||||
private fun stubSelectedWallet(
|
||||
currencies: List<CryptoCurrencyStatus>,
|
||||
|
|
@ -343,18 +401,21 @@ internal class ForYouModelTest {
|
|||
ForYouComponent.Params(
|
||||
callbacks = object : ForYouComponent.ForYouModelCallbacks {
|
||||
override fun onTokenClick(userWalletId: UserWalletId, currency: CryptoCurrency) = Unit
|
||||
override fun onAllEarnTokensClick() = Unit
|
||||
},
|
||||
),
|
||||
),
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
multiAccountStatusListSupplier = multiAccountStatusListSupplier,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
router = router,
|
||||
dispatchers = testScope.createTestingCoroutineDispatcherProvider(),
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
getEarnTokensBatchFlowUseCase = getEarnTokensBatchFlowUseCase,
|
||||
stakingAvailabilityListUseCase = stakingAvailabilityListUseCase,
|
||||
isAccountsModeEnabledUseCase = isAccountsModeEnabledUseCase,
|
||||
earnErrorResolver = earnErrorResolver,
|
||||
addToPortfolioManagerFactory = addToPortfolioManagerFactory,
|
||||
).also { model = it }
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +485,25 @@ internal class ForYouModelTest {
|
|||
}
|
||||
}
|
||||
|
||||
/** A token whose `yieldSupplyKey()` resolves to `"ethereum_0xabc"`. */
|
||||
private fun createYieldToken(): CryptoCurrency.Token {
|
||||
val network = createNetwork(networkRawId = "ethereum")
|
||||
val currencyId: CryptoCurrency.ID = mockk {
|
||||
every { value } returns "token-usdc"
|
||||
every { rawCurrencyId } returns CryptoCurrency.RawID("usd-coin")
|
||||
}
|
||||
return mockk {
|
||||
every { id } returns currencyId
|
||||
every { symbol } returns "USDC"
|
||||
every { name } returns "USD Coin"
|
||||
every { this@mockk.network } returns network
|
||||
every { decimals } returns 6
|
||||
every { isCustom } returns false
|
||||
every { iconUrl } returns null
|
||||
every { contractAddress } returns "0xabc"
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNetwork(networkRawId: String): Network {
|
||||
val networkId: Network.ID = mockk {
|
||||
every { rawId } returns Network.RawID(networkRawId)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.models.earn.EarnType
|
|||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import com.tangem.features.foryou.impl.entity.ForYouEarnOpportunitiesType
|
||||
import com.tangem.features.foryou.impl.model.converter.EarnApyInfo
|
||||
import com.tangem.features.foryou.impl.model.converter.EarnOpportunities
|
||||
import com.tangem.test.mock.MockAccounts
|
||||
|
|
@ -180,7 +181,8 @@ internal fun createEarnApyInfo(
|
|||
isActive: Boolean = true,
|
||||
apy: BigDecimal? = BigDecimal("0.05"),
|
||||
potentialRewards: BigDecimal? = null,
|
||||
): EarnApyInfo = EarnApyInfo(isActive = isActive, apy = apy, potentialRewards = potentialRewards)
|
||||
type: ForYouEarnOpportunitiesType = ForYouEarnOpportunitiesType.YieldSupply(apy = "5.5"),
|
||||
): EarnApyInfo = EarnApyInfo(isActive = isActive, apy = apy, potentialRewards = potentialRewards, type = type)
|
||||
|
||||
internal fun createPortfolioStatus(
|
||||
currencies: List<CryptoCurrencyStatus>,
|
||||
|
|
@ -192,4 +194,5 @@ internal fun createPortfolioStatus(
|
|||
|
||||
internal fun createAccountStatusList(vararg statuses: AccountStatus): AccountStatusList = mockk {
|
||||
every { accountStatuses } returns statuses.toList()
|
||||
every { userWalletId } returns MockAccounts.userWalletId
|
||||
}
|
||||
|
|
@ -15,12 +15,14 @@ import com.tangem.domain.models.currency.yieldSupplyKey
|
|||
import com.tangem.domain.models.earn.EarnTopToken
|
||||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.staking.model.common.RewardInfo
|
||||
import com.tangem.domain.staking.model.common.RewardType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.features.foryou.impl.entity.ForYouEarnOpportunitiesType
|
||||
import com.tangem.features.foryou.impl.entity.EarnOpportunitiesUM
|
||||
import com.tangem.test.mock.MockAccounts
|
||||
import io.mockk.every
|
||||
|
|
@ -290,6 +292,55 @@ internal class ForYouEarnOpportunitiesConverterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class TypeResolution {
|
||||
|
||||
@Test
|
||||
fun `GIVEN yield-eligible token row clicked WHEN convert THEN yield type carries the raw percent apy`() {
|
||||
// Arrange — the backend rate (10.00%) must reach the click callback unscaled
|
||||
val token = createEarnTokenCurrency()
|
||||
val status = createStatus(token, createEarnStatusValue(fiatAmount = BigDecimal("100")))
|
||||
var clickedType: ForYouEarnOpportunitiesType? = null
|
||||
val converter = createConverter(
|
||||
yieldSupplyAvailability = mapOf(token.yieldSupplyKey() to BigDecimal("10.00")),
|
||||
onTokenClick = { _, _, type -> clickedType = type },
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = converter.convert(createAccountStatusList(createPortfolioStatus(listOf(status))))
|
||||
result.clickFirstRow()
|
||||
|
||||
// Assert
|
||||
assertThat(clickedType).isEqualTo(ForYouEarnOpportunitiesType.YieldSupply(apy = "10.00"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN staking-eligible token row clicked WHEN convert THEN staking type carries the integration id`() {
|
||||
// Arrange
|
||||
val currency = createEarnCurrency()
|
||||
val status = createStatus(currency, createEarnStatusValue(fiatAmount = BigDecimal("100")))
|
||||
val availability = stakingAvailable(apy = BigDecimal("0.05"))
|
||||
var clickedType: ForYouEarnOpportunitiesType? = null
|
||||
val converter = createConverter(
|
||||
yieldStakingAvailability = mapOf(currency to availability),
|
||||
onTokenClick = { _, _, type -> clickedType = type },
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = converter.convert(createAccountStatusList(createPortfolioStatus(listOf(status))))
|
||||
result.clickFirstRow()
|
||||
|
||||
// Assert — the id comes from the resolved staking option
|
||||
val option = (availability as StakingAvailability.Available).option
|
||||
assertThat(clickedType).isEqualTo(ForYouEarnOpportunitiesType.Staking(integrationID = option.integrationId))
|
||||
}
|
||||
|
||||
private fun EarnOpportunitiesUM.clickFirstRow() {
|
||||
val row = tokenList.first().tokenRowUM as TangemTokenRowUM.Content
|
||||
row.onItemClick?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class AccountOrdering {
|
||||
|
||||
|
|
@ -328,6 +379,7 @@ internal class ForYouEarnOpportunitiesConverterTest {
|
|||
yieldStakingAvailability: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
topEarnTokens: EarnTopToken? = null,
|
||||
isAccountsModeEnabled: Boolean = false,
|
||||
onTokenClick: (UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType) -> Unit = { _, _, _ -> },
|
||||
) = ForYouEarnOpportunitiesConverter(
|
||||
appCurrency = appCurrency,
|
||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||
|
|
@ -336,19 +388,23 @@ internal class ForYouEarnOpportunitiesConverterTest {
|
|||
yieldSupplyAvailability = yieldSupplyAvailability,
|
||||
yieldStakingAvailability = yieldStakingAvailability,
|
||||
topEarnTokens = topEarnTokens,
|
||||
onTokenClick = onTokenClick,
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Real StakingIntegrationID values are used below: mocking the sealed interface makes mockk try to
|
||||
// retransform its enum implementations, which the JVM rejects ("cannot change the class modifiers").
|
||||
private fun stakingOption(apy: BigDecimal): StakingOption.P2PEthPool = mockk {
|
||||
every { this@mockk.apy } returns apy
|
||||
every { integrationId } returns StakingIntegrationID.P2PEthPool
|
||||
}
|
||||
|
||||
private fun stakingAvailable(apy: BigDecimal): StakingAvailability =
|
||||
StakingAvailability.Available(option = stakingOption(apy))
|
||||
|
||||
private fun stakeKitAvailable(vararg validatorList: Yield.Validator): StakingAvailability {
|
||||
// A real StakeKit option with a real integration id: stubbing `integrationId` on a mock would
|
||||
// make mockk instrument StakingIntegrationID.StakeKit, whose implementations are enums that the
|
||||
// JVM refuses to retransform ("cannot change the class modifiers").
|
||||
// A real StakeKit option: stubbing `integrationId` on a mock would make mockk instrument
|
||||
// StakingIntegrationID.StakeKit, hitting the same enum-retransformation limitation.
|
||||
val yieldModel: Yield = mockk {
|
||||
every { validators } returns validatorList.toList()
|
||||
every { apy } returns BigDecimal("0.10")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ internal class ForYouEarnOpportunitiesNoTokensConverterTest {
|
|||
topEarnTokens = List(7) { index ->
|
||||
createTopEarnToken(tokenId = "token-$index", networkRawId = "NET")
|
||||
}.right(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -39,6 +41,8 @@ internal class ForYouEarnOpportunitiesNoTokensConverterTest {
|
|||
createTopEarnToken(apy = "7.25", rewardType = EarnRewardType.APR),
|
||||
createTopEarnToken(tokenId = "solana", apy = "99.9", rewardType = EarnRewardType.APY),
|
||||
).right(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -56,6 +60,8 @@ internal class ForYouEarnOpportunitiesNoTokensConverterTest {
|
|||
// Arrange
|
||||
val converter = ForYouEarnOpportunitiesNoTokensConverter(
|
||||
topEarnTokens = null,
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.foryou.impl.entity.ForYouEarnOpportunitiesType
|
||||
import com.tangem.features.foryou.impl.entity.EarnOpportunitiesUM
|
||||
import com.tangem.test.mock.MockAccounts
|
||||
import org.junit.jupiter.api.Test
|
||||
|
|
@ -108,6 +111,32 @@ internal class ForYouEarnOpportunitiesPotentialRewardsConverterTest {
|
|||
assertThat(clickedAssetId).isEqualTo(account.accountId.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN token row clicked WHEN convert THEN token callback receives wallet currency and earn type`() {
|
||||
// Arrange
|
||||
val currency = createEarnCurrency()
|
||||
val earnType = ForYouEarnOpportunitiesType.YieldSupply(apy = "7.5")
|
||||
val earnData = createEarnOpportunities(
|
||||
earnCurrencies = mapOf(
|
||||
createStatus(currency, createRowLoadedValue()) to createEarnApyInfo(isActive = false, type = earnType),
|
||||
),
|
||||
)
|
||||
val walletId = UserWalletId("01")
|
||||
var clicked: Triple<UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType>? = null
|
||||
val converter = createConverter(
|
||||
isAccountsModeEnabled = false,
|
||||
userWalletId = walletId,
|
||||
onTokenClick = { id, clickedCurrency, type -> clicked = Triple(id, clickedCurrency, type) },
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = converter.convert(listOf(earnData)) as EarnOpportunitiesUM.Content
|
||||
(result.tokenList.single().tokenRowUM as TangemTokenRowUM.Content).onItemClick?.invoke()
|
||||
|
||||
// Assert
|
||||
assertThat(clicked).isEqualTo(Triple(walletId, currency, earnType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN several accounts WHEN convert THEN header reward is the sum across accounts`() {
|
||||
// Arrange
|
||||
|
|
@ -137,10 +166,15 @@ internal class ForYouEarnOpportunitiesPotentialRewardsConverterTest {
|
|||
isAccountsModeEnabled: Boolean,
|
||||
expandedAssetIds: Set<String> = emptySet(),
|
||||
expandClick: (String) -> Unit = {},
|
||||
userWalletId: UserWalletId? = UserWalletId("01"),
|
||||
onTokenClick: (UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType) -> Unit = { _, _, _ -> },
|
||||
) = ForYouEarnOpportunitiesPotentialRewardsConverter(
|
||||
appCurrency = appCurrency,
|
||||
userWalletId = userWalletId,
|
||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||
expandedAssetIds = expandedAssetIds,
|
||||
expandClick = expandClick,
|
||||
onTokenClick = onTokenClick,
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -13,7 +13,10 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.foryou.impl.entity.ForYouEarnOpportunitiesType
|
||||
import com.tangem.utils.StringsSigns
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
|
@ -23,7 +26,16 @@ import java.math.BigDecimal
|
|||
internal class ForYouEarnOpportunitiesTokenRowConverterTest {
|
||||
|
||||
private val appCurrency: AppCurrency = AppCurrency.Default
|
||||
private val converter = ForYouEarnOpportunitiesTokenRowConverter(appCurrency = appCurrency)
|
||||
private val converter = createConverter()
|
||||
|
||||
private fun createConverter(
|
||||
userWalletId: UserWalletId? = UserWalletId("01"),
|
||||
onTokenClick: (UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType) -> Unit = { _, _, _ -> },
|
||||
) = ForYouEarnOpportunitiesTokenRowConverter(
|
||||
appCurrency = appCurrency,
|
||||
userWalletId = userWalletId,
|
||||
onTokenClick = onTokenClick,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN loading status WHEN convert THEN row is Loading with currency id`() {
|
||||
|
|
@ -95,6 +107,27 @@ internal class ForYouEarnOpportunitiesTokenRowConverterTest {
|
|||
assertThat(bottomEnd.startIcons).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN row clicked WHEN convert THEN callback receives wallet currency and resolved earn type`() {
|
||||
// Arrange
|
||||
val currency = createEarnCurrency()
|
||||
val status = createStatus(currency, createRowLoadedValue())
|
||||
val earnType = ForYouEarnOpportunitiesType.YieldSupply(apy = "5.5")
|
||||
val walletId = UserWalletId("01")
|
||||
var clicked: Triple<UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType>? = null
|
||||
val converter = createConverter(
|
||||
userWalletId = walletId,
|
||||
onTokenClick = { id, clickedCurrency, type -> clicked = Triple(id, clickedCurrency, type) },
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = converter.convert(status to createEarnApyInfo(type = earnType)) as TangemTokenRowUM.Content
|
||||
result.onItemClick?.invoke()
|
||||
|
||||
// Assert
|
||||
assertThat(clicked).isEqualTo(Triple(walletId, currency, earnType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unreachable status WHEN convert THEN both ends are dashes`() {
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
createTopEarnToken(tokenId = "ethereum", networkRawId = "ETH"),
|
||||
createTopEarnToken(tokenId = "solana", networkRawId = "SOL"),
|
||||
).right(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -45,6 +47,8 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
topEarnTokens = List(8) { index ->
|
||||
createTopEarnToken(tokenId = "token-$index", networkRawId = "NET")
|
||||
}.right(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -69,6 +73,8 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
createTopEarnToken(tokenId = "usd-coin", networkRawId = "ETH"),
|
||||
createTopEarnToken(tokenId = "usd-coin", networkRawId = "SOL"),
|
||||
).right(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -80,9 +86,12 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
|
||||
@Test
|
||||
fun `GIVEN no top tokens loaded WHEN convert THEN content with empty suggestions`() {
|
||||
// Arrange
|
||||
// Arrange — the callback instance is shared with `expected` so the whole-object equality holds
|
||||
val onAllEarnTokensClick: () -> Unit = {}
|
||||
val converter = ForYouEarnOpportunitiesTokensActiveConverter(
|
||||
topEarnTokens = null,
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = onAllEarnTokensClick,
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
@ -94,6 +103,7 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
subtitleRes = R.string.for_you_earn_opportunities_all_tokens_active,
|
||||
potentialReward = null,
|
||||
potentialRewardType = null,
|
||||
onAllEarnTokensClick = onAllEarnTokensClick,
|
||||
)
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
|
@ -103,6 +113,8 @@ internal class ForYouEarnOpportunitiesTokensActiveConverterTest {
|
|||
// Arrange
|
||||
val converter = ForYouEarnOpportunitiesTokensActiveConverter(
|
||||
topEarnTokens = EarnError.NotHttpError().left(),
|
||||
onTokenClick = { _, _, _ -> },
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -9,13 +9,16 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.foryou.impl.entity.ForYouEarnOpportunitiesType
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class ForYouEarnOpportunitiesTopTokenRowConverterTest {
|
||||
|
||||
private val converter = ForYouEarnOpportunitiesTopTokenRowConverter()
|
||||
private val converter = ForYouEarnOpportunitiesTopTokenRowConverter(onTokenClick = { _, _, _ -> })
|
||||
|
||||
@Test
|
||||
fun `GIVEN top-earn token WHEN convert THEN row carries currency identity and network subtitle`() {
|
||||
|
|
@ -77,6 +80,25 @@ internal class ForYouEarnOpportunitiesTopTokenRowConverterTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN yield token row clicked WHEN convert THEN callback gets null wallet and yield type with raw apy`() {
|
||||
// Arrange — suggestions are tokens the user doesn't hold, so no wallet id is forwarded
|
||||
val topToken = createTopEarnToken(type = EarnType.YIELD, apy = "7.25")
|
||||
var clicked: Triple<UserWalletId?, CryptoCurrency, ForYouEarnOpportunitiesType>? = null
|
||||
val converter = ForYouEarnOpportunitiesTopTokenRowConverter(
|
||||
onTokenClick = { id, currency, type -> clicked = Triple(id, currency, type) },
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = converter.convert(topToken)
|
||||
(result.tokenRowUM as TangemTokenRowUM.Content).onItemClick?.invoke()
|
||||
|
||||
// Assert
|
||||
assertThat(clicked).isEqualTo(
|
||||
Triple(null, topToken.cryptoCurrency, ForYouEarnOpportunitiesType.YieldSupply(apy = "7.25")),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN top-earn token WHEN convert THEN item is a flat non-expandable row`() {
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ internal class SetPortfolioReviewTransformerTest {
|
|||
subtitleRes = 0,
|
||||
potentialReward = null,
|
||||
potentialRewardType = null,
|
||||
onAllEarnTokensClick = {},
|
||||
)
|
||||
|
||||
private fun accountStatusList(totalFiatBalance: TotalFiatBalance): AccountStatusList = mockk {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue