From edee792251690438a27b9bf5af52b66ed5bb30bf Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Jul 2026 16:11:38 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../foryou/impl/model/ForYouModelTest.kt | 102 ++++++++++++++++-- .../EarnOpportunitiesTestFactory.kt | 5 +- .../ForYouEarnOpportunitiesConverterTest.kt | 62 ++++++++++- ...uEarnOpportunitiesNoTokensConverterTest.kt | 6 ++ ...ortunitiesPotentialRewardsConverterTest.kt | 34 ++++++ ...uEarnOpportunitiesTokenRowConverterTest.kt | 35 +++++- ...nOpportunitiesTokensActiveConverterTest.kt | 14 ++- ...rnOpportunitiesTopTokenRowConverterTest.kt | 24 ++++- .../SetPortfolioReviewTransformerTest.kt | 1 + 9 files changed, 265 insertions(+), 18 deletions(-) diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt index 2dd1deb193..b021e7f4b2 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt @@ -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, @@ -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) diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/EarnOpportunitiesTestFactory.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/EarnOpportunitiesTestFactory.kt index 9bbc02f746..d765af9a28 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/EarnOpportunitiesTestFactory.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/EarnOpportunitiesTestFactory.kt @@ -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, @@ -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 } \ No newline at end of file diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesConverterTest.kt index 25a6c7a14d..264b3e8276 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesConverterTest.kt @@ -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 = 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") diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesNoTokensConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesNoTokensConverterTest.kt index b34fbcc926..bdcfafadfd 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesNoTokensConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesNoTokensConverterTest.kt @@ -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 diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesPotentialRewardsConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesPotentialRewardsConverterTest.kt index 32d1e4ed99..6d58b8df40 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesPotentialRewardsConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesPotentialRewardsConverterTest.kt @@ -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? = 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 = 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 = {}, ) } \ No newline at end of file diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokenRowConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokenRowConverterTest.kt index 2c7840246c..4bd1b21747 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokenRowConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokenRowConverterTest.kt @@ -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? = 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 diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokensActiveConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokensActiveConverterTest.kt index 11341b1c94..32093d1bf2 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokensActiveConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTokensActiveConverterTest.kt @@ -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 diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTopTokenRowConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTopTokenRowConverterTest.kt index 8bd359680d..6a15de523f 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTopTokenRowConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/earnOpportunities/ForYouEarnOpportunitiesTopTokenRowConverterTest.kt @@ -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? = 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 diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt index 0b2cbd80b5..c8c458b12a 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt @@ -145,6 +145,7 @@ internal class SetPortfolioReviewTransformerTest { subtitleRes = 0, potentialReward = null, potentialRewardType = null, + onAllEarnTokensClick = {}, ) private fun accountStatusList(totalFiatBalance: TotalFiatBalance): AccountStatusList = mockk {