Updated on 2026-08-14
This commit is contained in:
commit
a9026f9766
9 changed files with 356 additions and 24 deletions
|
|
@ -517,6 +517,7 @@
|
|||
<string name="dynamic_addresses_notification_funds_found_description">Foram encontrados fundos em endereços adicionais. Ative os Endereços Dinâmicos para acessá-los.</string>
|
||||
<string name="dynamic_addresses_notification_funds_found_title">Fundos encontrados em endereços adicionais</string>
|
||||
<string name="dynamic_addresses_receive_badge">Endereço dinâmico</string>
|
||||
<string name="dynamic_addresses_unavailability_reason_pending_transaction_send">O gerenciamento de endereços dinâmicos estará disponível assim que as transações pendentes estiverem na rede. %@ está completo</string>
|
||||
<string name="earn_best_opportunities">Melhores oportunidades</string>
|
||||
<string name="earn_clear_filter">Limpar filtro</string>
|
||||
<string name="earn_empty">A lista está temporariamente vazia, pois está sendo atualizada. Volte daqui a pouco.</string>
|
||||
|
|
@ -595,6 +596,7 @@
|
|||
<string name="express_provider_min_amount">Disponível em %s</string>
|
||||
<string name="express_provider_not_available">Indisponível para este par</string>
|
||||
<string name="express_provider_permission_needed">Permissão necessária</string>
|
||||
<string name="express_provider_permission_needed_v2">É necessária permissão.</string>
|
||||
<string name="express_provider_recommended">Recomendado</string>
|
||||
<string name="express_status_bought">Comprado %s</string>
|
||||
<string name="express_status_buying">Comprando %s</string>
|
||||
|
|
@ -641,6 +643,7 @@
|
|||
<string name="give_permission_staking_footer">A função Aprovar é necessária para conceder permissão a outro endereço para usar uma quantidade específica de seus tokens. Por definição, os contratos inteligentes não podem acessar seus tokens a menos que você aprove. Ao \"desbloquear\" seus tokens, você autoriza o contrato inteligente StakeKit a usá-los. Os mineradores da rede recebem uma taxa de gás (paga por você) para registrar essa ação no blockchain. Você pode fazer staking de seus tokens após conceder a aprovação.</string>
|
||||
<string name="give_permission_staking_subtitle">Para continuar, você precisa permitir que o contrato inteligente da Polygon use seus dados. %s</string>
|
||||
<string name="give_permission_swap_subtitle" formatted="false">Para continuar, conceda %1s permissão de contratos inteligentes para usar seu %2s</string>
|
||||
<string name="give_permission_swap_subtitle_v2">As corretoras descentralizadas exigem permissão para interagir com sua carteira. %1s</string>
|
||||
<string name="give_permission_title">Conceder permissão</string>
|
||||
<string name="give_permission_unlimited">Ilimitado</string>
|
||||
<string name="hardware_wallet_backup_feature_description">Os endereços são gerados diretamente na sua carteira de hardware Tangem — prontos para usar e totalmente protegidos.</string>
|
||||
|
|
@ -1115,6 +1118,14 @@
|
|||
<string name="onramp_error_transaction_already_processed">Esta transação já foi processada. Nenhuma ação adicional é necessária.</string>
|
||||
<string name="onramp_fetching_best_rates">Obtendo as melhores taxas...</string>
|
||||
<string name="onramp_instant_status">Instantâneo</string>
|
||||
<string name="onramp_kyc_verification_bullet_free">A verificação é gratuita e geralmente leva de 1 a 2 minutos.</string>
|
||||
<string name="onramp_kyc_verification_bullet_privacy">A Tangem não terá acesso às suas informações de identidade; você compartilha os dados diretamente com o provedor regulamentado.</string>
|
||||
<string name="onramp_kyc_verification_bullet_unlocks">A verificação desbloqueia o acesso total a transações futuras com este fornecedor.</string>
|
||||
<string name="onramp_kyc_verification_choose_another">Escolha outro método</string>
|
||||
<string name="onramp_kyc_verification_subtitle">Para cumprir os requisitos regulamentares locais %@ Requer verificação de identidade.</string>
|
||||
<string name="onramp_kyc_verification_title">Verificação de identidade exigida pelo provedor de pagamento</string>
|
||||
<string name="onramp_kyc_verification_verify_button">Verificar</string>
|
||||
<string name="onramp_kyc_verification_whats_important">O que é importante</string>
|
||||
<string name="onramp_legal">Ao utilizar a funcionalidade de acesso prioritário, você concorda com os termos do provedor. %1$s e %2$s</string>
|
||||
<string name="onramp_legal_text">O serviço é fornecido por um provedor externo. A Tangem não se responsabiliza por ele.</string>
|
||||
<string name="onramp_max_amount_restriction">O valor da compra não deve ser superior a %s</string>
|
||||
|
|
@ -1181,6 +1192,8 @@
|
|||
<string name="quick_action_buy_description">Cartão de crédito ou conta bancária</string>
|
||||
<string name="quick_action_receive_description">Compartilhe seu endereço ou código QR.</string>
|
||||
<string name="quick_action_swap_description">Entre seus portfólios</string>
|
||||
<string name="quick_top_up_chip_other">Outro</string>
|
||||
<string name="quick_top_up_title">Recarga rápida</string>
|
||||
<string name="receive_bottom_sheet_no_memo_required_message">Não é necessário memorando</string>
|
||||
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) sobre %3$s rede</string>
|
||||
<string name="receive_bottom_sheet_warning_message_compact">%1$s sobre %2$s rede</string>
|
||||
|
|
|
|||
|
|
@ -293,11 +293,13 @@
|
|||
<string name="common_delete">Удалить</string>
|
||||
<string name="common_disable">Отключить</string>
|
||||
<string name="common_disabled">Отключено</string>
|
||||
<string name="common_disabling">Выключение</string>
|
||||
<string name="common_disconnect">Отключить</string>
|
||||
<string name="common_done">Готово</string>
|
||||
<string name="common_edit">Изменить</string>
|
||||
<string name="common_enable">Включить</string>
|
||||
<string name="common_enabled">Включено</string>
|
||||
<string name="common_enabling">Включение</string>
|
||||
<string name="common_error">Ошибка</string>
|
||||
<string name="common_estimated_fee">Комиссия сети</string>
|
||||
<string name="common_exchange">Обменять</string>
|
||||
|
|
@ -347,6 +349,7 @@
|
|||
<item quantity="other">%dмин назад</item>
|
||||
</plurals>
|
||||
<string name="common_month">месяц</string>
|
||||
<string name="common_more">Еще</string>
|
||||
<string name="common_network_fee_title">Комиссия сети</string>
|
||||
<string name="common_network_fee_warning_content">Сумма отправки будет уменьшена на %1$s (%2$s) для покрытия выбранного уровня комиссии</string>
|
||||
<plurals name="common_networks_count">
|
||||
|
|
|
|||
|
|
@ -596,6 +596,7 @@
|
|||
<string name="express_provider_min_amount">Available from %s</string>
|
||||
<string name="express_provider_not_available">Unavailable for this pair</string>
|
||||
<string name="express_provider_permission_needed">Permission Required</string>
|
||||
<string name="express_provider_permission_needed_v2">Permission needed</string>
|
||||
<string name="express_provider_recommended">Recommended</string>
|
||||
<string name="express_status_bought">Bought %s</string>
|
||||
<string name="express_status_buying">Buying %s</string>
|
||||
|
|
@ -643,6 +644,7 @@
|
|||
<string name="give_permission_staking_footer">The Approve function is needed to grant permission to another address to use a specific amount of your tokens. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the StakeKit smart contract to use them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can stake your token after giving approval.</string>
|
||||
<string name="give_permission_staking_subtitle">To continue you need to allow Polygon smart contract to use your %s</string>
|
||||
<string name="give_permission_swap_subtitle" formatted="false">To continue, grant %1s smart contracts permission to use your %2s</string>
|
||||
<string name="give_permission_swap_subtitle_v2">Decentralized exchanges require permission to interact with your wallet. %1s</string>
|
||||
<string name="give_permission_title">Give Permission</string>
|
||||
<string name="give_permission_unlimited">Unlimited</string>
|
||||
<string name="hardware_wallet_backup_feature_description">Addresses are generated directly on your Tangem hardware wallet — ready to use and fully protected.</string>
|
||||
|
|
@ -670,6 +672,8 @@
|
|||
<string name="hw_backup_banner_description">Keeps your crypto safe and offline. Slim as a credit card, safer than a bank vault.</string>
|
||||
<string name="hw_backup_close_description">If you do, you\'ll need to start over.</string>
|
||||
<string name="hw_backup_google_drive_description">Recover existing wallet via Google Drive backup</string>
|
||||
<string name="hw_backup_google_drive_dialog_message">We\'re working on Google Drive backup to make wallet recovery even easier.</string>
|
||||
<string name="hw_backup_google_drive_dialog_title">Google Drive backup is coming soon</string>
|
||||
<string name="hw_backup_google_drive_title">Google Drive backup</string>
|
||||
<string name="hw_backup_hardware_create_description">Create a secure wallet and transfer your funds for extra protection.</string>
|
||||
<string name="hw_backup_hardware_create_title">Create new wallet</string>
|
||||
|
|
|
|||
|
|
@ -64,14 +64,18 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
event = "Button - Recovery phrase",
|
||||
)
|
||||
|
||||
class ButtonGoogleDriveBackup : WalletSettingsAnalyticEvents(
|
||||
event = "Button - Cloud Backup",
|
||||
)
|
||||
|
||||
data class NoticeBackupFirst(
|
||||
val source: String,
|
||||
val action: Action,
|
||||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Notice - Backup First",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source,
|
||||
AnalyticsParam.Key.ACTION to action.value,
|
||||
AnalyticsParam.SOURCE to source,
|
||||
ACTION to action.value,
|
||||
),
|
||||
) {
|
||||
enum class Action(val value: String) {
|
||||
|
|
@ -111,8 +115,8 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Recovery Phrase Screen Info",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source,
|
||||
AnalyticsParam.Key.ACTION to action,
|
||||
AnalyticsParam.SOURCE to source,
|
||||
ACTION to action,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -122,8 +126,8 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Recovery Phrase Screen",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source,
|
||||
AnalyticsParam.Key.ACTION to action,
|
||||
AnalyticsParam.SOURCE to source,
|
||||
ACTION to action,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -133,8 +137,8 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Recovery Phrase Check",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source,
|
||||
AnalyticsParam.Key.ACTION to action,
|
||||
AnalyticsParam.SOURCE to source,
|
||||
ACTION to action,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -144,8 +148,8 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Backup Complete Screen",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source,
|
||||
AnalyticsParam.Key.ACTION to action,
|
||||
AnalyticsParam.SOURCE to source,
|
||||
ACTION to action,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -153,14 +157,14 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
val source: String,
|
||||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Access Code Screen Opened",
|
||||
params = mapOf(AnalyticsParam.Key.SOURCE to source),
|
||||
params = mapOf(AnalyticsParam.SOURCE to source),
|
||||
)
|
||||
|
||||
data class ReEnterAccessCodeScreen(
|
||||
val source: String,
|
||||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Re-enter Access Code Screen",
|
||||
params = mapOf(AnalyticsParam.Key.SOURCE to source),
|
||||
params = mapOf(AnalyticsParam.SOURCE to source),
|
||||
)
|
||||
|
||||
class ButtonStartUpgrade : WalletSettingsAnalyticEvents(
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ android {
|
|||
namespace = "com.tangem.features.hotwallet.impl"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/** Api */
|
||||
implementation(projects.features.hotWallet.api)
|
||||
|
|
@ -78,4 +82,11 @@ dependencies {
|
|||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Test */
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.test.coroutine)
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ internal data class WalletBackupUM(
|
|||
val onRecoveryPhraseClick: () -> Unit,
|
||||
val onGoogleDriveClick: () -> Unit,
|
||||
val onHardwareWalletClick: () -> Unit,
|
||||
val backedUp: Boolean,
|
||||
val isBackedUp: Boolean,
|
||||
)
|
||||
|
||||
internal sealed class BackupStatus {
|
||||
|
|
@ -7,10 +7,13 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
|
|
@ -19,9 +22,9 @@ import com.tangem.features.hotwallet.WalletBackupComponent
|
|||
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
|
||||
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -35,6 +38,7 @@ internal class WalletBackupModel @Inject constructor(
|
|||
private val router: Router,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
private val params: WalletBackupComponent.Params = paramsContainer.require()
|
||||
|
|
@ -59,9 +63,9 @@ internal class WalletBackupModel @Inject constructor(
|
|||
),
|
||||
googleDriveStatus = BackupStatus.ComingSoon,
|
||||
onRecoveryPhraseClick = ::onRecoveryPhraseClick,
|
||||
onGoogleDriveClick = { },
|
||||
onGoogleDriveClick = ::onGoogleDriveBackupClick,
|
||||
onHardwareWalletClick = ::onHardwareWalletClick,
|
||||
backedUp = false,
|
||||
isBackedUp = false,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -116,15 +120,16 @@ internal class WalletBackupModel @Inject constructor(
|
|||
)
|
||||
},
|
||||
googleDriveOption = LabelUM(
|
||||
text = resourceReference(R.string.common_coming_soon),
|
||||
style = LabelStyle.REGULAR,
|
||||
text = resourceReference(R.string.hw_backup_no_backup),
|
||||
style = LabelStyle.WARNING,
|
||||
),
|
||||
backedUp = userWallet.backedUp,
|
||||
isBackedUp = userWallet.backedUp,
|
||||
googleDriveStatus = BackupStatus.NoBackup,
|
||||
)
|
||||
|
||||
private fun onRecoveryPhraseClick() {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonRecoveryPhrase())
|
||||
if (uiState.value.backedUp) {
|
||||
if (uiState.value.isBackedUp) {
|
||||
getUserWalletUseCase.invoke(params.userWalletId)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
|
|
@ -150,6 +155,20 @@ internal class WalletBackupModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onGoogleDriveBackupClick() {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup())
|
||||
uiMessageSender.send(
|
||||
DialogMessage(
|
||||
title = resourceReference(id = R.string.hw_backup_google_drive_dialog_title),
|
||||
message = resourceReference(id = R.string.hw_backup_google_drive_dialog_message),
|
||||
firstAction = EventMessageAction(
|
||||
title = resourceReference(id = R.string.common_ok),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSeedPhrase(hotWallet: UserWallet.Hot) {
|
||||
modelScope.launch {
|
||||
unlockHotWalletContextualUseCase.invoke(hotWallet.hotWalletId)
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
|||
onRecoveryPhraseClick = {},
|
||||
onGoogleDriveClick = {},
|
||||
onHardwareWalletClick = {},
|
||||
backedUp = false,
|
||||
isBackedUp = false,
|
||||
),
|
||||
WalletBackupUM(
|
||||
hardwareWalletOption = LabelUM(
|
||||
|
|
@ -152,7 +152,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
|||
onRecoveryPhraseClick = {},
|
||||
onGoogleDriveClick = {},
|
||||
onHardwareWalletClick = {},
|
||||
backedUp = false,
|
||||
isBackedUp = false,
|
||||
),
|
||||
WalletBackupUM(
|
||||
hardwareWalletOption = LabelUM(
|
||||
|
|
@ -172,7 +172,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
|||
onRecoveryPhraseClick = {},
|
||||
onGoogleDriveClick = {},
|
||||
onHardwareWalletClick = {},
|
||||
backedUp = false,
|
||||
isBackedUp = false,
|
||||
),
|
||||
WalletBackupUM(
|
||||
hardwareWalletOption = null,
|
||||
|
|
@ -189,7 +189,7 @@ private class WalletBackupUMProvider : CollectionPreviewParameterProvider<Wallet
|
|||
onRecoveryPhraseClick = {},
|
||||
onGoogleDriveClick = {},
|
||||
onHardwareWalletClick = {},
|
||||
backedUp = false,
|
||||
isBackedUp = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
package com.tangem.features.hotwallet.walletbackup.model
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
|
||||
import com.tangem.features.hotwallet.WalletBackupComponent
|
||||
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.hot.sdk.model.UnlockHotWallet
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class WalletBackupModelTest {
|
||||
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase = mockk()
|
||||
private val unlockHotWalletContextualUseCase: UnlockHotWalletContextualUseCase = mockk()
|
||||
private val router: Router = mockk(relaxUnitFun = true)
|
||||
private val trackingContextProxy: TrackingContextProxy = mockk(relaxUnitFun = true)
|
||||
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxUnitFun = true)
|
||||
private val uiMessageSender: UiMessageSender = mockk(relaxUnitFun = true)
|
||||
private val paramsContainer: ParamsContainer = mockk()
|
||||
|
||||
private val walletId = UserWalletId("011")
|
||||
private val hotWalletId: HotWalletId = mockk()
|
||||
private val params = WalletBackupComponent.Params(
|
||||
userWalletId = walletId,
|
||||
isColdWalletOptionShown = true,
|
||||
)
|
||||
private val hotWalletNotBackedUp: UserWallet.Hot = mockk {
|
||||
every { walletId } returns this@WalletBackupModelTest.walletId
|
||||
every { hotWalletId } returns this@WalletBackupModelTest.hotWalletId
|
||||
every { backedUp } returns false
|
||||
}
|
||||
private val hotWalletBackedUp: UserWallet.Hot = mockk {
|
||||
every { walletId } returns this@WalletBackupModelTest.walletId
|
||||
every { hotWalletId } returns this@WalletBackupModelTest.hotWalletId
|
||||
every { backedUp } returns true
|
||||
}
|
||||
private val coldWallet: UserWallet.Cold = mockk {
|
||||
every { walletId } returns this@WalletBackupModelTest.walletId
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
every { paramsContainer.require<WalletBackupComponent.Params>() } returns params
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletNotBackedUp.right())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN hot wallet WHEN model is created THEN context added AND BackupScreenOpened sent AND state updated`() =
|
||||
runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletNotBackedUp.right())
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { trackingContextProxy.addHotWalletContext() }
|
||||
verify {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.BackupScreenOpened(isBackedUp = false))
|
||||
}
|
||||
val state = model.uiState.value
|
||||
Assertions.assertEquals(false, state.isBackedUp)
|
||||
Assertions.assertEquals(BackupStatus.NoBackup, state.googleDriveStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN cold wallet WHEN model is created THEN context added AND BackupScreenOpened not sent AND state untouched`() =
|
||||
runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(coldWallet.right())
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { trackingContextProxy.addHotWalletContext() }
|
||||
verify(exactly = 0) {
|
||||
analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.BackupScreenOpened> { true })
|
||||
}
|
||||
val state = model.uiState.value
|
||||
Assertions.assertEquals(false, state.isBackedUp)
|
||||
Assertions.assertEquals(BackupStatus.ComingSoon, state.googleDriveStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN error WHEN model is created THEN context added AND BackupScreenOpened not sent`() = runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(GetUserWalletError.UserWalletNotFound.left())
|
||||
|
||||
createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { trackingContextProxy.addHotWalletContext() }
|
||||
verify(exactly = 0) {
|
||||
analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.BackupScreenOpened> { true })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onDestroy THEN trackingContextProxy removeContext is called`() = runTest {
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.onDestroy()
|
||||
|
||||
verify { trackingContextProxy.removeContext() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN backed up hot wallet AND unlock success WHEN onRecoveryPhraseClick THEN ViewPhrase pushed`() = runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
||||
coEvery { unlockHotWalletContextualUseCase.invoke(hotWalletId) } returns mockk<UnlockHotWallet>().right()
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onRecoveryPhraseClick()
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
|
||||
coVerify { unlockHotWalletContextualUseCase.invoke(hotWalletId) }
|
||||
verify { router.push(route = AppRoute.ViewPhrase(userWalletId = walletId), onComplete = any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN backed up hot wallet AND unlock failure WHEN onRecoveryPhraseClick THEN ViewPhrase not pushed`() =
|
||||
runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||
every { getUserWalletUseCase.invoke(walletId) } returns hotWalletBackedUp.right()
|
||||
coEvery { unlockHotWalletContextualUseCase.invoke(hotWalletId) } returns Throwable("error").left()
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onRecoveryPhraseClick()
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
|
||||
coVerify { unlockHotWalletContextualUseCase.invoke(hotWalletId) }
|
||||
verify(exactly = 0) {
|
||||
router.push(route = AppRoute.ViewPhrase(userWalletId = walletId), onComplete = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN backed up cold wallet WHEN onRecoveryPhraseClick THEN no navigation AND no unlock`() = runTest {
|
||||
every { getUserWalletUseCase.invokeFlow(walletId) } returns flowOf(hotWalletBackedUp.right())
|
||||
every { getUserWalletUseCase.invoke(walletId) } returns coldWallet.right()
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onRecoveryPhraseClick()
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
|
||||
coVerify(exactly = 0) { unlockHotWalletContextualUseCase.invoke(any()) }
|
||||
verify(exactly = 0) { router.push(route = any(), onComplete = any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN not backed up wallet WHEN onRecoveryPhraseClick THEN WalletActivation pushed`() = runTest {
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onRecoveryPhraseClick()
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonRecoveryPhrase> { true }) }
|
||||
verify(exactly = 0) { getUserWalletUseCase.invoke(walletId) }
|
||||
coVerify(exactly = 0) { unlockHotWalletContextualUseCase.invoke(any()) }
|
||||
verify {
|
||||
router.push(
|
||||
route = AppRoute.WalletActivation(userWalletId = walletId, isBackupExists = false),
|
||||
onComplete = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onHardwareWalletClick THEN ButtonHardwareUpdate sent AND WalletHardwareBackup pushed`() = runTest {
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onHardwareWalletClick()
|
||||
|
||||
verify { analyticsEventHandler.send(match<WalletSettingsAnalyticEvents.ButtonHardwareUpdate> { true }) }
|
||||
verify {
|
||||
router.push(
|
||||
route = AppRoute.WalletHardwareBackup(userWalletId = walletId),
|
||||
onComplete = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onGoogleDriveClick THEN DialogMessage sent AND analytics sent`() = runTest {
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onGoogleDriveClick()
|
||||
|
||||
verify {
|
||||
analyticsEventHandler.send(
|
||||
event = match<WalletSettingsAnalyticEvents.ButtonGoogleDriveBackup> { true }
|
||||
)
|
||||
}
|
||||
verify {
|
||||
uiMessageSender.send(
|
||||
match<DialogMessage> {
|
||||
val isTitleCorrect = it.title == resourceReference(
|
||||
id = R.string.hw_backup_google_drive_dialog_title
|
||||
)
|
||||
val isMessageCorrect = it.message == resourceReference(
|
||||
id = R.string.hw_backup_google_drive_dialog_message
|
||||
)
|
||||
isTitleCorrect && isMessageCorrect
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN onBackClick THEN router pop is called`() = runTest {
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
model.uiState.value.onBackClick()
|
||||
|
||||
verify { router.pop(onComplete = any()) }
|
||||
}
|
||||
|
||||
private fun createModel(testScope: TestScope): WalletBackupModel {
|
||||
return WalletBackupModel(
|
||||
paramsContainer = paramsContainer,
|
||||
dispatchers = testScope.createTestingCoroutineDispatcherProvider(),
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
unlockHotWalletContextualUseCase = unlockHotWalletContextualUseCase,
|
||||
router = router,
|
||||
trackingContextProxy = trackingContextProxy,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
uiMessageSender = uiMessageSender,
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestScope.createTestingCoroutineDispatcherProvider(): TestingCoroutineDispatcherProvider {
|
||||
val testDispatcher = StandardTestDispatcher(testScheduler)
|
||||
return TestingCoroutineDispatcherProvider(
|
||||
main = testDispatcher,
|
||||
mainImmediate = testDispatcher,
|
||||
io = testDispatcher,
|
||||
default = testDispatcher,
|
||||
single = testDispatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue