Updated on 2026-08-14
This commit is contained in:
parent
d14835df83
commit
44fc5b9610
7 changed files with 260 additions and 122 deletions
|
|
@ -1,38 +1,61 @@
|
|||
package com.tangem.features.swap.v2.impl.amount.analytics
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticsErrorMessages
|
||||
|
||||
internal class SwapAmountAnalyticsSender(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
|
||||
private var lastSentErrorMessage: String? = null
|
||||
private var lastSentEvent: AnalyticsEvent? = null
|
||||
|
||||
fun sendErrorIfNeeded(quotes: List<SwapQuoteUM>, selectedQuote: SwapQuoteUM?) {
|
||||
val errorMessage = resolveErrorMessage(quotes, selectedQuote)
|
||||
if (errorMessage == lastSentErrorMessage) return
|
||||
lastSentErrorMessage = errorMessage
|
||||
if (errorMessage != null) {
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.SendWithSwapError(
|
||||
errorScreen = SendWithSwapAnalyticEvents.ErrorScreen.Amount,
|
||||
message = errorMessage,
|
||||
),
|
||||
fun sendErrorIfNeeded(
|
||||
quotes: List<SwapQuoteUM>,
|
||||
selectedQuote: SwapQuoteUM?,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
hasInsufficientBalance: Boolean,
|
||||
) {
|
||||
val event = resolveEvent(
|
||||
quotes = quotes,
|
||||
selectedQuote = selectedQuote,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
hasInsufficientBalance = hasInsufficientBalance,
|
||||
)
|
||||
if (event?.event == lastSentEvent?.event && event?.params == lastSentEvent?.params) return
|
||||
lastSentEvent = event
|
||||
if (event != null) {
|
||||
analyticsEventHandler.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveEvent(
|
||||
quotes: List<SwapQuoteUM>,
|
||||
selectedQuote: SwapQuoteUM?,
|
||||
fromToken: CryptoCurrency,
|
||||
toToken: CryptoCurrency,
|
||||
hasInsufficientBalance: Boolean,
|
||||
): AnalyticsEvent? {
|
||||
if (hasInsufficientBalance) {
|
||||
return SendWithSwapAnalyticEvents.ErrorInsufficientBalance(fromToken = fromToken)
|
||||
}
|
||||
if (quotes.isEmpty()) return null
|
||||
val error = (selectedQuote as? SwapQuoteUM.Error)?.expressError ?: return null
|
||||
return when (error) {
|
||||
is ExpressError.AmountError.TooSmallError ->
|
||||
SendWithSwapAnalyticEvents.ErrorMinAmount(fromToken = fromToken)
|
||||
is ExpressError.AmountError.TooBigError ->
|
||||
SendWithSwapAnalyticEvents.ErrorMaxAmount(fromToken = fromToken)
|
||||
else -> SendWithSwapAnalyticEvents.ErrorExpressQuote(
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
errorDescription = "code=${error.code}",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveErrorMessage(quotes: List<SwapQuoteUM>, selectedQuote: SwapQuoteUM?): String? {
|
||||
if (quotes.isEmpty()) return SendWithSwapAnalyticsErrorMessages.EXPRESS_QUOTE_NO_PROVIDERS
|
||||
val error = (selectedQuote as? SwapQuoteUM.Error)?.expressError ?: return null
|
||||
return when (error) {
|
||||
is ExpressError.AmountError.TooSmallError -> SendWithSwapAnalyticsErrorMessages.MIN_AMOUNT
|
||||
is ExpressError.AmountError.TooBigError -> SendWithSwapAnalyticsErrorMessages.MAX_AMOUNT
|
||||
else -> "${SendWithSwapAnalyticsErrorMessages.EXPRESS_QUOTE}: code=${error.code}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,6 @@ import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
|||
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents.NoticeFixedRate.toAnalyticsRateType
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticsErrorMessages
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.Debouncer
|
||||
import com.tangem.utils.coroutines.PeriodicTask
|
||||
|
|
@ -616,18 +615,35 @@ internal class SwapAmountModel @Inject constructor(
|
|||
| Secondary -> $secondaryStatus
|
||||
""".trimIndent(),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.SendWithSwapError(
|
||||
errorScreen = SendWithSwapAnalyticEvents.ErrorScreen.Amount,
|
||||
message = "${SendWithSwapAnalyticsErrorMessages.INVALID_CRYPTOCURRENCIES_STATUS}: " +
|
||||
"primary=$primaryStatus, secondary=$secondaryStatus",
|
||||
),
|
||||
)
|
||||
showErrorAlert(errorMessage = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendAmountErrorAnalyticsIfNeeded(quotes: List<SwapQuoteUM>) {
|
||||
val content = uiState.value as? SwapAmountUM.Content ?: return
|
||||
val toCurrency = content.secondaryCryptoCurrencyStatus?.currency ?: return
|
||||
amountAnalyticsSender.sendErrorIfNeeded(
|
||||
quotes = quotes,
|
||||
selectedQuote = content.selectedQuote,
|
||||
fromToken = content.primaryCryptoCurrencyStatus.currency,
|
||||
toToken = toCurrency,
|
||||
hasInsufficientBalance = hasInsufficientBalance(content),
|
||||
)
|
||||
}
|
||||
|
||||
private fun hasInsufficientBalance(content: SwapAmountUM.Content): Boolean {
|
||||
val primaryBalance = content.primaryCryptoCurrencyStatus.value.amount ?: return false
|
||||
val fromAmount = when (content.selectedAmountType) {
|
||||
SwapAmountType.To -> (content.selectedQuote as? SwapQuoteUM.Content)?.fromAmount
|
||||
SwapAmountType.From -> {
|
||||
val field = content.primaryAmount as? SwapAmountFieldUM.Content
|
||||
(field?.amountField as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
}
|
||||
} ?: return false
|
||||
return fromAmount > primaryBalance
|
||||
}
|
||||
|
||||
private fun sendAmountScreenOpenedIfNeeded(secondaryStatus: CryptoCurrencyStatus) {
|
||||
if (params !is SwapAmountComponentParams.AmountParams) return
|
||||
if (isAmountScreenOpenedSent) return
|
||||
|
|
@ -711,7 +727,9 @@ internal class SwapAmountModel @Inject constructor(
|
|||
val isAmountScreen = params is SwapAmountComponentParams.AmountParams
|
||||
val isAmountError = amountField?.amountTextField?.isError == true || amountValue.isNullOrZero()
|
||||
if (isAmountScreen && isAmountError) {
|
||||
uiState.transformerUpdate(SwapQuoteEmptyStateTransformer); return
|
||||
uiState.transformerUpdate(SwapQuoteEmptyStateTransformer)
|
||||
sendAmountErrorAnalyticsIfNeeded(quotes = emptyList())
|
||||
return
|
||||
}
|
||||
|
||||
val rateType = when (state.selectedAmountType) {
|
||||
|
|
@ -783,8 +801,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
if (params is SwapAmountComponentParams.AmountParams) {
|
||||
val selectedQuote = (uiState.value as? SwapAmountUM.Content)?.selectedQuote
|
||||
amountAnalyticsSender.sendErrorIfNeeded(quotes, selectedQuote)
|
||||
sendAmountErrorAnalyticsIfNeeded(quotes)
|
||||
}
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent
|
|||
import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsUpdateListener
|
||||
import com.tangem.features.swap.v2.impl.notifications.entity.SwapNotificationUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticsErrorMessages
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -46,7 +45,7 @@ internal class SwapNotificationsModel @Inject constructor(
|
|||
private val params: SwapNotificationsComponent.Params = paramsContainer.require()
|
||||
|
||||
private var notificationData = params.swapNotificationData
|
||||
private var lastSentErrorMessages: Set<String> = emptySet()
|
||||
private var lastSentErrorKeys: Set<Pair<String, Map<String, String>>> = emptySet()
|
||||
|
||||
val uiState: StateFlow<ImmutableList<NotificationUM>>
|
||||
field = MutableStateFlow<ImmutableList<NotificationUM>>(persistentListOf())
|
||||
|
|
@ -203,32 +202,32 @@ internal class SwapNotificationsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun sendErrorAnalyticsIfNeeded(notifications: List<NotificationUM>) {
|
||||
val currentErrors = notifications.mapNotNull { notification ->
|
||||
val fromToken = notificationData.fromCryptoCurrency ?: return
|
||||
val toToken = notificationData.toCryptoCurrencyStatus?.currency
|
||||
|
||||
val events = notifications.mapNotNull { notification ->
|
||||
when (notification) {
|
||||
is SwapNotificationUM.Error.InsufficientFunds ->
|
||||
SendWithSwapAnalyticsErrorMessages.INSUFFICIENT_BALANCE
|
||||
SendWithSwapAnalyticEvents.ErrorInsufficientBalance(fromToken = fromToken)
|
||||
is SwapNotificationUM.Error.MinimalAmountError ->
|
||||
SendWithSwapAnalyticsErrorMessages.MIN_AMOUNT
|
||||
SendWithSwapAnalyticEvents.ErrorMinAmount(fromToken = fromToken)
|
||||
is SwapNotificationUM.Error.MaximumAmountError ->
|
||||
SendWithSwapAnalyticsErrorMessages.MAX_AMOUNT
|
||||
is SwapNotificationUM.Warning.ExpressGeneralError ->
|
||||
"${SendWithSwapAnalyticsErrorMessages.EXPRESS_QUOTE}: code=${notification.expressError.code}"
|
||||
is NotificationUM.Error.DestinationTagRequired ->
|
||||
SendWithSwapAnalyticsErrorMessages.DESTINATION_TAG_REQUIRED
|
||||
SendWithSwapAnalyticEvents.ErrorMaxAmount(fromToken = fromToken)
|
||||
is SwapNotificationUM.Warning.ExpressGeneralError -> toToken?.let { receiveToken ->
|
||||
SendWithSwapAnalyticEvents.ErrorExpressQuote(
|
||||
fromToken = fromToken,
|
||||
toToken = receiveToken,
|
||||
errorDescription = "code=${notification.expressError.code}",
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
val newErrors = currentErrors - lastSentErrorMessages
|
||||
lastSentErrorMessages = currentErrors
|
||||
|
||||
newErrors.forEach { errorMessage ->
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.SendWithSwapError(
|
||||
errorScreen = SendWithSwapAnalyticEvents.ErrorScreen.Confirm,
|
||||
message = errorMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val currentKeys = events.map { it.event to it.params }.toSet()
|
||||
val newEvents = events.filter { it.event to it.params !in lastSentErrorKeys }
|
||||
lastSentErrorKeys = currentKeys
|
||||
|
||||
newEvents.forEach(analyticsEventHandler::send)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACCOUNT_DERIVATION_FROM
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACCOUNT_DERIVATION_TO
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_MESSAGE
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_DESCRIPTION
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.RATE_TYPE
|
||||
|
|
@ -120,19 +120,51 @@ internal sealed class SendWithSwapAnalyticEvents(
|
|||
params = emptyMap(),
|
||||
)
|
||||
|
||||
data class SendWithSwapError(
|
||||
val errorScreen: ErrorScreen,
|
||||
val message: String,
|
||||
data class ErrorInsufficientBalance(
|
||||
val fromToken: CryptoCurrency,
|
||||
) : SendWithSwapAnalyticEvents(
|
||||
event = when (errorScreen) {
|
||||
ErrorScreen.Amount -> "Send With Swap Amount Screen Error"
|
||||
ErrorScreen.Confirm -> "Send With Swap Confirm Screen Error"
|
||||
},
|
||||
event = "Error - Insufficient balance",
|
||||
params = mapOf(
|
||||
ERROR_MESSAGE to message,
|
||||
SEND_TOKEN to fromToken.symbol,
|
||||
SEND_BLOCKCHAIN to fromToken.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ErrorMinAmount(
|
||||
val fromToken: CryptoCurrency,
|
||||
) : SendWithSwapAnalyticEvents(
|
||||
event = "Error - Min amount",
|
||||
params = mapOf(
|
||||
SEND_TOKEN to fromToken.symbol,
|
||||
SEND_BLOCKCHAIN to fromToken.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ErrorMaxAmount(
|
||||
val fromToken: CryptoCurrency,
|
||||
) : SendWithSwapAnalyticEvents(
|
||||
event = "Error - Max amount",
|
||||
params = mapOf(
|
||||
SEND_TOKEN to fromToken.symbol,
|
||||
SEND_BLOCKCHAIN to fromToken.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ErrorExpressQuote(
|
||||
val fromToken: CryptoCurrency,
|
||||
val toToken: CryptoCurrency,
|
||||
val errorDescription: String? = null,
|
||||
) : SendWithSwapAnalyticEvents(
|
||||
event = "Error - Express quote",
|
||||
params = buildMap {
|
||||
put(SEND_TOKEN, fromToken.symbol)
|
||||
put(SEND_BLOCKCHAIN, fromToken.network.name)
|
||||
put(RECEIVE_TOKEN, toToken.symbol)
|
||||
put(RECEIVE_BLOCKCHAIN, toToken.network.name)
|
||||
if (errorDescription != null) put(ERROR_DESCRIPTION, errorDescription)
|
||||
},
|
||||
)
|
||||
|
||||
class HighPriceImpact(
|
||||
val sendToken: String,
|
||||
val receiveToken: String,
|
||||
|
|
@ -168,11 +200,6 @@ internal sealed class SendWithSwapAnalyticEvents(
|
|||
),
|
||||
)
|
||||
|
||||
enum class ErrorScreen {
|
||||
Amount,
|
||||
Confirm,
|
||||
}
|
||||
|
||||
enum class RateType {
|
||||
Float,
|
||||
Fixed,
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.analytics
|
||||
|
||||
internal object SendWithSwapAnalyticsErrorMessages {
|
||||
const val INSUFFICIENT_BALANCE = "Error - Insufficient balance"
|
||||
const val MIN_AMOUNT = "Error - Min amount"
|
||||
const val MAX_AMOUNT = "Error - Max amount"
|
||||
const val EXPRESS_QUOTE_NO_PROVIDERS = "Error - Express quote no providers found"
|
||||
const val EXPRESS_QUOTE = "Error - Express quote"
|
||||
const val DESTINATION_TAG_REQUIRED = "Error - Destination tag required"
|
||||
const val INVALID_CRYPTOCURRENCIES_STATUS = "Error - Invalid cryptocurrencies status"
|
||||
}
|
||||
|
|
@ -322,12 +322,17 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
isAmountSubtractAvailable = isAmountSubtractAvailable,
|
||||
onExpressError = { expressError ->
|
||||
uiState.transformerUpdate(SendWithSwapConfirmSendingStateTransformer(false))
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.SendWithSwapError(
|
||||
errorScreen = SendWithSwapAnalyticEvents.ErrorScreen.Confirm,
|
||||
message = "Express error: $expressError",
|
||||
),
|
||||
)
|
||||
val fromCurrency = confirmData.fromCryptoCurrencyStatus?.currency
|
||||
val toCurrency = confirmData.toCryptoCurrencyStatus?.currency
|
||||
if (fromCurrency != null && toCurrency != null) {
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.ErrorExpressQuote(
|
||||
fromToken = fromCurrency,
|
||||
toToken = toCurrency,
|
||||
errorDescription = "code=${expressError.code}",
|
||||
),
|
||||
)
|
||||
}
|
||||
swapAlertFactory.getGenericErrorState(
|
||||
expressError = expressError,
|
||||
onFailedTxEmailClick = {
|
||||
|
|
@ -345,12 +350,6 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
},
|
||||
onSendError = { error ->
|
||||
uiState.transformerUpdate(SendWithSwapConfirmSendingStateTransformer(false))
|
||||
analyticsEventHandler.send(
|
||||
SendWithSwapAnalyticEvents.SendWithSwapError(
|
||||
errorScreen = SendWithSwapAnalyticEvents.ErrorScreen.Confirm,
|
||||
message = "Send error: ${error?.toString().orEmpty()}",
|
||||
),
|
||||
)
|
||||
swapAlertFactory.getSendTransactionErrorState(
|
||||
error = error,
|
||||
onFailedTxEmailClick = { _ ->
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
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.models.network.Network
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticsErrorMessages
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
|
|
@ -21,6 +22,21 @@ class SwapAmountAnalyticsSenderTest {
|
|||
private val analyticsEventHandler = mockk<AnalyticsEventHandler>(relaxed = true)
|
||||
private val sender = SwapAmountAnalyticsSender(analyticsEventHandler)
|
||||
|
||||
private val fromNetwork = mockk<Network>(relaxed = true).also {
|
||||
every { it.name } returns "Ethereum"
|
||||
}
|
||||
private val toNetwork = mockk<Network>(relaxed = true).also {
|
||||
every { it.name } returns "Bitcoin"
|
||||
}
|
||||
private val fromToken = mockk<CryptoCurrency>(relaxed = true).also {
|
||||
every { it.symbol } returns "ETH"
|
||||
every { it.network } returns fromNetwork
|
||||
}
|
||||
private val toToken = mockk<CryptoCurrency>(relaxed = true).also {
|
||||
every { it.symbol } returns "BTC"
|
||||
every { it.network } returns toNetwork
|
||||
}
|
||||
|
||||
private val testProvider = ExpressProvider(
|
||||
providerId = "test",
|
||||
name = "Test Provider",
|
||||
|
|
@ -31,21 +47,41 @@ class SwapAmountAnalyticsSenderTest {
|
|||
slippage = null,
|
||||
)
|
||||
|
||||
private fun send(
|
||||
quotes: List<SwapQuoteUM> = emptyList(),
|
||||
selectedQuote: SwapQuoteUM? = null,
|
||||
hasInsufficientBalance: Boolean = false,
|
||||
) = sender.sendErrorIfNeeded(
|
||||
quotes = quotes,
|
||||
selectedQuote = selectedQuote,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
hasInsufficientBalance = hasInsufficientBalance,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN empty quotes WHEN sendErrorIfNeeded THEN send no providers error`() {
|
||||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
fun `GIVEN empty quotes WHEN sendErrorIfNeeded THEN do not send analytics`() {
|
||||
send(quotes = emptyList(), selectedQuote = null)
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = emptyList(), selectedQuote = null)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured as SendWithSwapAnalyticEvents.SendWithSwapError
|
||||
assertThat(event.errorScreen).isEqualTo(SendWithSwapAnalyticEvents.ErrorScreen.Amount)
|
||||
assertThat(event.message).isEqualTo(SendWithSwapAnalyticsErrorMessages.EXPRESS_QUOTE_NO_PROVIDERS)
|
||||
verify(exactly = 0) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN too small error quote WHEN sendErrorIfNeeded THEN send min amount error`() {
|
||||
fun `GIVEN insufficient balance WHEN sendErrorIfNeeded THEN send ErrorInsufficientBalance with from token params`() {
|
||||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
|
||||
send(hasInsufficientBalance = true)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured
|
||||
assertThat(event).isInstanceOf(SendWithSwapAnalyticEvents.ErrorInsufficientBalance::class.java)
|
||||
assertThat(event.event).isEqualTo("Error - Insufficient balance")
|
||||
assertThat(event.params).containsExactly("Send Token", "ETH", "Send Blockchain", "Ethereum")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN insufficient balance and express error WHEN sendErrorIfNeeded THEN insufficient balance takes priority`() {
|
||||
val errorQuote = SwapQuoteUM.Error(
|
||||
provider = testProvider,
|
||||
expressError = ExpressError.AmountError.TooSmallError(code = 1001, amount = BigDecimal("0.01")),
|
||||
|
|
@ -53,15 +89,32 @@ class SwapAmountAnalyticsSenderTest {
|
|||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote, hasInsufficientBalance = true)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured as SendWithSwapAnalyticEvents.SendWithSwapError
|
||||
assertThat(event.message).isEqualTo(SendWithSwapAnalyticsErrorMessages.MIN_AMOUNT)
|
||||
assertThat(eventSlot.captured).isInstanceOf(SendWithSwapAnalyticEvents.ErrorInsufficientBalance::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN too big error quote WHEN sendErrorIfNeeded THEN send max amount error`() {
|
||||
fun `GIVEN too small error quote WHEN sendErrorIfNeeded THEN send ErrorMinAmount with from token params`() {
|
||||
val errorQuote = SwapQuoteUM.Error(
|
||||
provider = testProvider,
|
||||
expressError = ExpressError.AmountError.TooSmallError(code = 1001, amount = BigDecimal("0.01")),
|
||||
)
|
||||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured
|
||||
assertThat(event).isInstanceOf(SendWithSwapAnalyticEvents.ErrorMinAmount::class.java)
|
||||
assertThat(event.event).isEqualTo("Error - Min amount")
|
||||
assertThat(event.params).containsExactly("Send Token", "ETH", "Send Blockchain", "Ethereum")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN too big error quote WHEN sendErrorIfNeeded THEN send ErrorMaxAmount with from token params`() {
|
||||
val errorQuote = SwapQuoteUM.Error(
|
||||
provider = testProvider,
|
||||
expressError = ExpressError.AmountError.TooBigError(code = 1002, amount = BigDecimal("1000")),
|
||||
|
|
@ -69,15 +122,17 @@ class SwapAmountAnalyticsSenderTest {
|
|||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured as SendWithSwapAnalyticEvents.SendWithSwapError
|
||||
assertThat(event.message).isEqualTo(SendWithSwapAnalyticsErrorMessages.MAX_AMOUNT)
|
||||
val event = eventSlot.captured
|
||||
assertThat(event).isInstanceOf(SendWithSwapAnalyticEvents.ErrorMaxAmount::class.java)
|
||||
assertThat(event.event).isEqualTo("Error - Max amount")
|
||||
assertThat(event.params).containsExactly("Send Token", "ETH", "Send Blockchain", "Ethereum")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unknown express error WHEN sendErrorIfNeeded THEN send express quote error with code`() {
|
||||
fun `GIVEN unknown express error WHEN sendErrorIfNeeded THEN send ErrorExpressQuote with both tokens and code`() {
|
||||
val errorQuote = SwapQuoteUM.Error(
|
||||
provider = testProvider,
|
||||
expressError = ExpressError.InternalError(code = 500),
|
||||
|
|
@ -85,18 +140,26 @@ class SwapAmountAnalyticsSenderTest {
|
|||
val eventSlot = slot<AnalyticsEvent>()
|
||||
every { analyticsEventHandler.send(capture(eventSlot)) } returns Unit
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
val event = eventSlot.captured as SendWithSwapAnalyticEvents.SendWithSwapError
|
||||
assertThat(event.message).isEqualTo("${SendWithSwapAnalyticsErrorMessages.EXPRESS_QUOTE}: code=500")
|
||||
val event = eventSlot.captured
|
||||
assertThat(event).isInstanceOf(SendWithSwapAnalyticEvents.ErrorExpressQuote::class.java)
|
||||
assertThat(event.event).isEqualTo("Error - Express quote")
|
||||
assertThat(event.params).containsExactly(
|
||||
"Send Token", "ETH",
|
||||
"Send Blockchain", "Ethereum",
|
||||
"Receive Token", "BTC",
|
||||
"Receive Blockchain", "Bitcoin",
|
||||
"Error Description", "code=500",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN content quote WHEN sendErrorIfNeeded THEN do not send analytics`() {
|
||||
val contentQuote = mockk<SwapQuoteUM.Content>()
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(contentQuote), selectedQuote = contentQuote)
|
||||
send(quotes = listOf(contentQuote), selectedQuote = contentQuote)
|
||||
|
||||
verify(exactly = 0) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
|
@ -108,8 +171,16 @@ class SwapAmountAnalyticsSenderTest {
|
|||
expressError = ExpressError.AmountError.TooSmallError(code = 1001, amount = BigDecimal("0.01")),
|
||||
)
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN insufficient balance twice WHEN sendErrorIfNeeded THEN send analytics only once`() {
|
||||
send(hasInsufficientBalance = true)
|
||||
send(hasInsufficientBalance = true)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
|
@ -125,8 +196,21 @@ class SwapAmountAnalyticsSenderTest {
|
|||
expressError = ExpressError.AmountError.TooBigError(code = 1002, amount = BigDecimal("1000")),
|
||||
)
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(smallError), selectedQuote = smallError)
|
||||
sender.sendErrorIfNeeded(quotes = listOf(bigError), selectedQuote = bigError)
|
||||
send(quotes = listOf(smallError), selectedQuote = smallError)
|
||||
send(quotes = listOf(bigError), selectedQuote = bigError)
|
||||
|
||||
verify(exactly = 2) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN express error then insufficient balance WHEN sendErrorIfNeeded THEN send analytics twice`() {
|
||||
val smallError = SwapQuoteUM.Error(
|
||||
provider = testProvider,
|
||||
expressError = ExpressError.AmountError.TooSmallError(code = 1001, amount = BigDecimal("0.01")),
|
||||
)
|
||||
|
||||
send(quotes = listOf(smallError), selectedQuote = smallError)
|
||||
send(quotes = listOf(smallError), selectedQuote = smallError, hasInsufficientBalance = true)
|
||||
|
||||
verify(exactly = 2) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
|
@ -139,9 +223,9 @@ class SwapAmountAnalyticsSenderTest {
|
|||
)
|
||||
val contentQuote = mockk<SwapQuoteUM.Content>()
|
||||
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
sender.sendErrorIfNeeded(quotes = listOf(contentQuote), selectedQuote = contentQuote)
|
||||
sender.sendErrorIfNeeded(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
send(quotes = listOf(contentQuote), selectedQuote = contentQuote)
|
||||
send(quotes = listOf(errorQuote), selectedQuote = errorQuote)
|
||||
|
||||
verify(exactly = 2) { analyticsEventHandler.send(any()) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue