diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/analytics/PortfolioAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/analytics/PortfolioAnalyticsEvent.kt new file mode 100644 index 0000000000..e69e15866b --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/analytics/PortfolioAnalyticsEvent.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.wallet.child.managetokens.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent + +internal sealed class PortfolioAnalyticsEvent( + event: String, +) : AnalyticsEvent(category = "Portfolio", event = event) { + + class ButtonAddManage : PortfolioAnalyticsEvent(event = "Button - Add Manage") + + class ButtonAddTokens : PortfolioAnalyticsEvent(event = "Button - Add tokens") + + class ButtonOrganizeTokens : PortfolioAnalyticsEvent(event = "Button - Organize Tokens") +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModel.kt index 9600a357f8..ce64ceba7a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModel.kt @@ -3,11 +3,13 @@ package com.tangem.feature.wallet.child.managetokens.model import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.models.account.AccountId import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent +import com.tangem.feature.wallet.child.managetokens.analytics.PortfolioAnalyticsEvent import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController @@ -21,6 +23,7 @@ internal class AddAndManageModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val portfolioFetcherFactory: PortfolioFetcher.Factory, + private val analyticsEventHandler: AnalyticsEventHandler, val portfolioSelectorController: PortfolioSelectorController, ) : Model() { @@ -45,6 +48,7 @@ internal class AddAndManageModel @Inject constructor( } fun onAddTokensClick() { + analyticsEventHandler.send(PortfolioAnalyticsEvent.ButtonAddTokens()) modelScope.launch { val data = portfolioFetcher.data.first() val isSingleAccount = data.isSingleChoice(params.userWalletId) @@ -65,6 +69,7 @@ internal class AddAndManageModel @Inject constructor( } fun onOrganizeTokensClick() { + analyticsEventHandler.send(PortfolioAnalyticsEvent.ButtonOrganizeTokens()) params.onDismiss() params.onOrganizeTokensClick() } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt index 71220bf8a9..0e34ecf2d4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt @@ -23,6 +23,7 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplySetShouldShowMainPromoUseCase +import com.tangem.feature.wallet.child.managetokens.analytics.PortfolioAnalyticsEvent import com.tangem.feature.wallet.presentation.account.AccountDependencies import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory @@ -123,6 +124,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( override fun onOrganizeTokensClick() { val userWalletId = stateHolder.getSelectedWalletId() if (walletFeatureToggles.isAddAndManageTokensEnabled) { + analyticsEventHandler.send(PortfolioAnalyticsEvent.ButtonAddManage()) router.openAddAndManageBottomSheet(userWalletId = userWalletId) } else { router.openOrganizeTokensScreen(userWalletId = userWalletId) diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModelTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModelTest.kt new file mode 100644 index 0000000000..73e1539274 --- /dev/null +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/managetokens/model/AddAndManageModelTest.kt @@ -0,0 +1,101 @@ +package com.tangem.feature.wallet.child.managetokens.model + +import com.google.common.truth.Truth.assertThat +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.decompose.model.MutableParamsContainer +import com.tangem.domain.models.account.AccountId +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent +import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher +import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +internal class AddAndManageModelTest { + + private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxed = true) + private val portfolioFetcher: PortfolioFetcher = mockk(relaxed = true) { + every { data } returns flowOf( + PortfolioFetcher.Data( + appCurrency = mockk(relaxed = true), + isBalanceHidden = false, + balances = emptyMap(), + ), + ) + } + private val portfolioFetcherFactory: PortfolioFetcher.Factory = mockk(relaxed = true) { + every { create(any(), any()) } returns portfolioFetcher + } + private val portfolioSelectorController: PortfolioSelectorController = mockk(relaxed = true) { + every { selectedAccount } returns flowOf(null) + } + + private val onDismiss: () -> Unit = mockk(relaxed = true) + private val onOrganizeTokensClick: () -> Unit = mockk(relaxed = true) + private val onManageTokensClick: (AccountId) -> Unit = mockk(relaxed = true) + + private val userWalletId = UserWalletId(stringValue = "0123456789ABCDEF") + + private val params = AddAndManageBottomSheetComponent.Params( + userWalletId = userWalletId, + onDismiss = onDismiss, + onOrganizeTokensClick = onOrganizeTokensClick, + onManageTokensClick = onManageTokensClick, + ) + + private fun createModel(): AddAndManageModel = AddAndManageModel( + paramsContainer = MutableParamsContainer(params), + dispatchers = TestingCoroutineDispatcherProvider(), + portfolioFetcherFactory = portfolioFetcherFactory, + analyticsEventHandler = analyticsEventHandler, + portfolioSelectorController = portfolioSelectorController, + ) + + @Test + fun `GIVEN bottom sheet model WHEN onAddTokensClick THEN sends ButtonAddTokens event with correct payload`() = + runTest { + val model = createModel() + val captured = slot() + + model.onAddTokensClick() + + verify(exactly = 1) { analyticsEventHandler.send(capture(captured)) } + assertThat(captured.captured.category).isEqualTo("Portfolio") + assertThat(captured.captured.event).isEqualTo("Button - Add tokens") + assertThat(captured.captured.params).isEmpty() + } + + @Test + fun `GIVEN bottom sheet model WHEN onOrganizeTokensClick THEN sends ButtonOrganizeTokens event with correct payload`() = + runTest { + val model = createModel() + val captured = slot() + + model.onOrganizeTokensClick() + + verify(exactly = 1) { analyticsEventHandler.send(capture(captured)) } + assertThat(captured.captured.category).isEqualTo("Portfolio") + assertThat(captured.captured.event).isEqualTo("Button - Organize Tokens") + assertThat(captured.captured.params).isEmpty() + } + + @Test + fun `GIVEN bottom sheet model WHEN onOrganizeTokensClick THEN dismisses bottom sheet and forwards to params callback`() = + runTest { + val model = createModel() + + model.onOrganizeTokensClick() + + verify(exactly = 1) { onDismiss() } + verify(exactly = 1) { onOrganizeTokensClick() } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntentsAnalyticsTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntentsAnalyticsTest.kt new file mode 100644 index 0000000000..d6a468a205 --- /dev/null +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntentsAnalyticsTest.kt @@ -0,0 +1,83 @@ +package com.tangem.feature.wallet.child.wallet.model.intents + +import com.google.common.truth.Truth.assertThat +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.feature.wallet.presentation.router.InnerWalletRouter +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +internal class WalletContentClickIntentsAnalyticsTest { + + private val stateHolder: WalletStateController = mockk(relaxed = true) + private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxed = true) + private val walletFeatureToggles: WalletFeatureToggles = mockk(relaxed = true) + private val router: InnerWalletRouter = mockk(relaxed = true) + + private val userWalletId = UserWalletId(stringValue = "0123456789ABCDEF") + + private fun createImplementor(): WalletContentClickIntentsImplementor { + every { stateHolder.getSelectedWalletId() } returns userWalletId + + val implementor = WalletContentClickIntentsImplementor( + stateHolder = stateHolder, + currencyActionsClickIntents = mockk(relaxed = true), + onrampStatusFactory = mockk(relaxed = true), + getUserWalletUseCase = mockk(relaxed = true), + singleAccountStatusListSupplier = mockk(relaxed = true), + getCryptoCurrencyActionsUseCase = mockk(relaxed = true), + getExplorerTransactionUrlUseCase = mockk(relaxed = true), + shouldShowMarketsTooltipUseCase = mockk(relaxed = true), + dispatchers = mockk(relaxed = true), + walletEventSender = mockk(relaxed = true), + analyticsEventHandler = analyticsEventHandler, + accountDependencies = mockk(relaxed = true), + yieldSupplySetShouldShowMainPromoUseCase = mockk(relaxed = true), + tokenListAnalyticsSender = mockk(relaxed = true), + uiMessageSender = mockk(relaxed = true), + walletFeatureToggles = walletFeatureToggles, + ) + implementor.initialize(router = router, coroutineScope = TestScope()) + return implementor + } + + @Test + fun `GIVEN add and manage toggle enabled WHEN onOrganizeTokensClick THEN sends ButtonAddManage event and opens bottom sheet`() = + runTest { + every { walletFeatureToggles.isAddAndManageTokensEnabled } returns true + val implementor = createImplementor() + val captured = slot() + + implementor.onOrganizeTokensClick() + + verify(exactly = 1) { analyticsEventHandler.send(capture(captured)) } + assertThat(captured.captured.category).isEqualTo("Portfolio") + assertThat(captured.captured.event).isEqualTo("Button - Add Manage") + assertThat(captured.captured.params).isEmpty() + verify(exactly = 1) { router.openAddAndManageBottomSheet(userWalletId = userWalletId) } + verify(exactly = 0) { router.openOrganizeTokensScreen(any()) } + } + + @Test + fun `GIVEN add and manage toggle disabled WHEN onOrganizeTokensClick THEN does not send analytics and opens organize screen`() = + runTest { + every { walletFeatureToggles.isAddAndManageTokensEnabled } returns false + val implementor = createImplementor() + + implementor.onOrganizeTokensClick() + + verify(exactly = 0) { analyticsEventHandler.send(any()) } + verify(exactly = 1) { router.openOrganizeTokensScreen(userWalletId = userWalletId) } + verify(exactly = 0) { router.openAddAndManageBottomSheet(any()) } + } +} \ No newline at end of file