diff --git a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt index 15e6bfcafc..455df5348b 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt @@ -183,6 +183,7 @@ abstract class BaseTestCase : TestCase( "GASLESS_APPROVAL_ENABLED" to true, "MAIN_SCREEN_QR_SCANNING_ENABLED" to true, "ADD_AND_MANAGE_TOKENS_ENABLED" to true, + "ASSETS_DISCOVERY_ENABLED" to true, "VISA_ONBOARDING_ENABLED" to true, "AND_15101_TANGEM_PAY_HOT_WALLET_ONBOARDING" to true, "AND_15310_ADD_FUNDS_STAGE1" to true, diff --git a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt index fc4d42796b..e9cf254d43 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt @@ -48,6 +48,10 @@ object TestConstants { const val USER_TOKENS_API_SCENARIO = "user_tokens_api" const val REFERRAL_API_SCENARIO = "referral_api" const val QUOTES_API_SCENARIO = "quotes_api" + const val CREATE_USER_WALLET_API_SCENARIO = "create_user_wallet_api" + const val WALLET_TOKENS_API_SCENARIO = "wallet_tokens_api" + const val MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO = "moralis_evm_token_balances_api" + const val PROVIDERS_API_SCENARIO = "networks_providers" const val SEED_PHRASE_12 = "they cram join fantasy unfair observe true theory buffalo bus exchange walk" const val SEED_PHRASE_15 = "genuine try deer upset connect sausage diary rule price shallow fit faculty leopard " + @@ -60,6 +64,9 @@ object TestConstants { "bread much nature basic fun iron benefit egg error prosper" const val SVS_SEED_PHRASE_12 = "diagram thunder merit soup muscle amused refuse usual ring couch popular wash" + const val SEED_PHRASE_HAPPY_PATH = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + const val TANGEM_PAY_ELIGIBILITY_SCENARIO = "tangem_pay_eligibility" const val TANGEM_PAY_ACCESS_CODE = "517384" } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt index ce7201883e..82bc1fb24d 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.swipeUp import com.tangem.common.BaseTestCase @@ -23,7 +24,7 @@ import androidx.compose.ui.test.hasText as withText import com.tangem.core.res.R as CoreResR import com.tangem.core.ui.R as CoreUiR -class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : +class MainScreenPageObject(private val semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen(semanticsProvider = semanticsProvider) { private val lazyList = KLazyListNode( @@ -100,6 +101,22 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) } } + val restoringProgressText: KNode = child { + hasTestTag(MainScreenTestTags.SYNC_PROGRESS_TEXT) + useUnmergedTree = true + } + + val walletImportedBanner: KNode = child { + hasTestTag(WalletNotificationTestTags.ASSETS_DISCOVERY_BANNER) + useUnmergedTree = true + } + + val walletImportedBannerCheckHereButton: KNode = child { + hasAnyAncestor(withTestTag(WalletNotificationTestTags.ASSETS_DISCOVERY_BANNER)) + hasText(getResourceString(CoreResR.string.main_manage_tokens)) + useUnmergedTree = true + } + @OptIn(ExperimentalTestApi::class) fun marketPriceBlock(): LazyListItemNode { collapseHeader() @@ -253,6 +270,15 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) } } + @OptIn(ExperimentalTestApi::class) + fun tokenRowWithTitle(tokenTitle: String): LazyListItemNode { + return lazyList.childWith { + hasTestTag(MainScreenTestTags.TOKEN_LIST_ITEM) + hasText(tokenTitle) + useUnmergedTree = true + } + } + /** * Find token list item with title and address */ @@ -350,6 +376,12 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) } } } + + fun assertTokensCount(expectedCount: Int) { + semanticsProvider + .onAllNodes(withTestTag(TokenElementsTestTags.TOKEN_PRICE)) + .assertCountEquals(expectedCount) + } } internal fun BaseTestCase.onMainScreen(function: MainScreenPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/tests/hotWallet/AssetsDiscoveryTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/hotWallet/AssetsDiscoveryTest.kt new file mode 100644 index 0000000000..c96032df3a --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/hotWallet/AssetsDiscoveryTest.kt @@ -0,0 +1,251 @@ +package com.tangem.tests.hotWallet + +import androidx.test.InstrumentationRegistry.getTargetContext +import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.CREATE_USER_WALLET_API_SCENARIO +import com.tangem.common.constants.TestConstants.MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO +import com.tangem.common.constants.TestConstants.PROVIDERS_API_SCENARIO +import com.tangem.common.constants.TestConstants.SEED_PHRASE_12 +import com.tangem.common.constants.TestConstants.SEED_PHRASE_HAPPY_PATH +import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO +import com.tangem.common.constants.TestConstants.WALLET_TOKENS_API_SCENARIO +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.extensions.restartApp +import com.tangem.common.utils.resetWireMockScenarioState +import com.tangem.common.utils.setWireMockScenarioState +import com.tangem.scenarios.openMainScreenWithExistingHotWallet +import com.tangem.screens.* +import com.tangem.screens.accounts.onAccountDetailsScreen +import dagger.hilt.android.testing.HiltAndroidTest +import io.github.kakaocup.kakao.common.utilities.getResourceString +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test +import com.tangem.core.ui.R as CoreUiR + +@HiltAndroidTest +class AssetsDiscoveryTest : BaseTestCase() { + + private companion object { + const val DISCOVERY_TIMEOUT_MILLIS = 120_000L + + const val SCENARIO_STATE_STARTED = "Started" + const val SCENARIO_STATE_EMPTY = "Empty" + const val SCENARIO_STATE_ALREADY_EXISTS = "AlreadyExists" + const val SCENARIO_STATE_ASSETS_DISCOVERY_REDIRECT = "AssetsDiscoveryRedirect" + const val SCENARIO_STATE_ASSETS_DISCOVERY_HAPPY_PATH = "AssetsDiscoveryHappyPath" + const val SCENARIO_STATE_NON_ZERO_EVM_BALANCES = "NonZeroEvmBalances" + const val SCENARIO_STATE_NON_ZERO_EVM_BALANCES_SLOW = "NonZeroEvmBalancesSlow" + + val EXPECTED_DISCOVERED_TOKENS = listOf( + "Ethereum", + "Polygon", + "Tether", + ) + + val TOKENS_THAT_MUST_NOT_APPEAR = listOf( + "Solana", + "USDC", + ) + + val BACKEND_PRE_POPULATED_TOKENS = listOf( + "Bitcoin", + "Ethereum", + "Polygon", + ) + } + + @AllureId("9280") + @DisplayName("Hot wallet: new import — Discovery → Sync → Banner → Check here happy path") + @Test + fun newHotWalletImportHappyPathTest() { + val packageName = getTargetContext().packageName + + setupHooks( + additionalBeforeAppLaunchSection = { + setWireMockScenarioState(PROVIDERS_API_SCENARIO, state = SCENARIO_STATE_ASSETS_DISCOVERY_REDIRECT) + setWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO, state = SCENARIO_STATE_STARTED) + setWireMockScenarioState(USER_TOKENS_API_SCENARIO, state = SCENARIO_STATE_ASSETS_DISCOVERY_HAPPY_PATH) + setWireMockScenarioState(WALLET_TOKENS_API_SCENARIO, state = SCENARIO_STATE_STARTED) + setWireMockScenarioState(MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO, state = SCENARIO_STATE_NON_ZERO_EVM_BALANCES) + }, + additionalAfterSection = { + resetWireMockScenarioState(PROVIDERS_API_SCENARIO) + resetWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO) + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(WALLET_TOKENS_API_SCENARIO) + resetWireMockScenarioState(MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO) + }, + ).run { + step("Import a new hot wallet from seed phrase") { + openMainScreenWithExistingHotWallet(SEED_PHRASE_HAPPY_PATH) + } + step("Assert 'Restoring' progress loader is shown (discovery is in flight)") { + onMainScreen { restoringProgressText.assertIsDisplayed() } + } + step("Wait for 'Wallet successfully imported' banner (discovery completes)") { + flakySafely(timeoutMs = DISCOVERY_TIMEOUT_MILLIS) { + onMainScreen { walletImportedBanner.assertIsDisplayed() } + } + } + step("Assert expected discovered tokens are visible in the assets list") { + onMainScreen { + EXPECTED_DISCOVERED_TOKENS.forEach { token -> + tokenRowWithTitle(token).assertIsDisplayed() + } + } + } + step("Tap 'Check here' (Manage tokens) on the banner") { + onMainScreen { walletImportedBannerCheckHereButton.clickWithAssertion() } + } + step("Assert 'Manage Tokens' screen is opened") { + onManageTokensScreen { searchField.assertIsDisplayed() } + } + step("Return to main screen") { + device.uiDevice.pressBack() + waitForIdle() + } + step("Assert banner is hidden after navigating into Manage Tokens") { + onMainScreen { walletImportedBanner.assertIsNotDisplayed() } + } + step("Force-close and re-launch the app") { + restartApp(packageName) + } + step("Assert banner is NOT shown again after relaunch") { + onMainScreen { walletImportedBanner.assertIsNotDisplayed() } + } + step("Assert previously discovered tokens still appear in the assets list") { + onMainScreen { + EXPECTED_DISCOVERED_TOKENS.forEach { token -> + tokenRowWithTitle(token).assertIsDisplayed() + } + } + } + step("Assert zero-balance and spam tokens are NOT shown in the assets list") { + onMainScreen { + TOKENS_THAT_MUST_NOT_APPEAR.forEach { token -> + assertTokenDoesNotExist(token) + } + } + } + } + } + + @AllureId("9284") + @DisplayName("Hot wallet: token added manually during Discovery — no duplicate created") + @Test + fun manualTokenAddDuringDiscoveryNoDuplicateTest() { + val tetherTitle = "Tether" + val ethereumNetworkTitle = "ETHEREUM" + val accountName = getResourceString(CoreUiR.string.account_main_account_title) + val expectedTokensCount = 4 + + setupHooks( + additionalBeforeAppLaunchSection = { + setWireMockScenarioState(PROVIDERS_API_SCENARIO, state = SCENARIO_STATE_ASSETS_DISCOVERY_REDIRECT) + setWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO, state = SCENARIO_STATE_STARTED) + setWireMockScenarioState(USER_TOKENS_API_SCENARIO, state = SCENARIO_STATE_ASSETS_DISCOVERY_HAPPY_PATH) + setWireMockScenarioState(WALLET_TOKENS_API_SCENARIO, state = SCENARIO_STATE_STARTED) + setWireMockScenarioState( + MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO, + state = SCENARIO_STATE_NON_ZERO_EVM_BALANCES_SLOW, + ) + }, + additionalAfterSection = { + resetWireMockScenarioState(PROVIDERS_API_SCENARIO) + resetWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO) + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(WALLET_TOKENS_API_SCENARIO) + resetWireMockScenarioState(MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO) + }, + ).run { + step("Import a new hot wallet from seed phrase") { + openMainScreenWithExistingHotWallet(SEED_PHRASE_HAPPY_PATH) + } + step("Assert 'Restoring' progress loader is shown (discovery is in flight)") { + onMainScreen { restoringProgressText.assertIsDisplayed() } + } + step("Open wallet details from top bar") { + onMainScreenTopBar { moreButton.clickWithAssertion() } + } + step("Open 'Wallet settings'") { + onDetailsScreen { walletNameButton.performClick() } + } + step("Open account: '$accountName'") { + onWalletSettingsScreen { accountItem(accountName).performClick() } + } + step("Open 'Manage Tokens' from account details") { + onAccountDetailsScreen { manageTokensButton.performClick() } + } + step("Search for '$tetherTitle' in Manage Tokens") { + onManageTokensScreen { + searchField.performClick() + searchField.performTextInput(tetherTitle) + } + device.uiDevice.pressBack() + waitForIdle() + } + step("Expand '$tetherTitle'") { + onManageTokensScreen { tokenItem(tetherTitle).clickWithAssertion() } + waitForIdle() + } + step("Enable the $ethereumNetworkTitle network") { + onManageTokensScreen { networkSwitch(ethereumNetworkTitle).clickWithAssertion() } + } + step("Save Manage Tokens changes") { + onManageTokensScreen { saveButton.clickWithAssertion() } + waitForIdle() + } + step("Navigate back to main screen") { + repeat(times = 3) { + device.uiDevice.pressBack() + waitForIdle() + } + } + step("Wait for 'Wallet successfully imported' banner (discovery completes after delay)") { + flakySafely(timeoutMs = DISCOVERY_TIMEOUT_MILLIS) { + onMainScreen { walletImportedBanner.assertIsDisplayed() } + } + } + step("Assert '$tetherTitle' is in the assets list (manual add + discovery merged)") { + onMainScreen { tokenRowWithTitle(tetherTitle).assertIsDisplayed() } + } + step("Assert assets list contains exactly $expectedTokensCount tokens (no duplicate after merge)") { + onMainScreen { assertTokensCount(expectedTokensCount) } + } + } + } + + @AllureId("9282") + @DisplayName("Hot wallet: re-import existing wallet — 200 OK, no Discovery, tokens from backend") + @Test + fun reimportExistingHotWalletTest() { + setupHooks( + additionalBeforeAppLaunchSection = { + setWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO, state = SCENARIO_STATE_ALREADY_EXISTS) + setWireMockScenarioState(USER_TOKENS_API_SCENARIO, state = SCENARIO_STATE_STARTED) + setWireMockScenarioState(MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO, state = SCENARIO_STATE_EMPTY) + }, + additionalAfterSection = { + resetWireMockScenarioState(CREATE_USER_WALLET_API_SCENARIO) + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(MORALIS_EVM_TOKEN_BALANCES_API_SCENARIO) + }, + ).run { + step("Import an existing hot wallet from seed phrase") { + openMainScreenWithExistingHotWallet(SEED_PHRASE_12) + } + step("Assert tokens from backend are displayed immediately") { + BACKEND_PRE_POPULATED_TOKENS.forEach { token -> + onMainScreen { tokenRowWithTitle(token).assertIsDisplayed() } + } + } + step("Assert 'Restoring' loader is NOT displayed (discovery did not start)") { + onMainScreen { restoringProgressText.assertIsNotDisplayed() } + } + step("Assert 'Wallet successfully imported' banner is NOT displayed") { + onMainScreen { walletImportedBanner.assertIsNotDisplayed() } + } + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt index 4451945383..430e41140a 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt @@ -14,21 +14,39 @@ class WireMockRedirectInterceptor : Interceptor { val request = chain.request() val url = request.url.toString() + val host = request.url.host + val sanitizedOverride = override.trimEnd('/') - if (url.contains(WIREMOCK_REMOTE_URL)) { - val newUrl = url.replace(WIREMOCK_REMOTE_URL, override.trimEnd('/')) + if (host == WIREMOCK_REMOTE_HOST) { + val newUrl = url.replace(WIREMOCK_REMOTE_URL, sanitizedOverride) TangemLogger.d("WireMockRedirect: $url -> $newUrl") - val newRequest = request.newBuilder() - .url(newUrl) - .build() - return chain.proceed(newRequest) + return chain.proceed(request.newBuilder().url(newUrl).build()) + } + + if (host in REDIRECTABLE_THIRD_PARTY_HOSTS) { + val newUrl = url.replace("https://$host", "$sanitizedOverride/$host") + TangemLogger.d("WireMockRedirect (3p): $url -> $newUrl") + return chain.proceed(request.newBuilder().url(newUrl).build()) } return chain.proceed(request) } companion object { - private const val WIREMOCK_REMOTE_URL = "[REDACTED_ENV_URL]" + private const val WIREMOCK_REMOTE_HOST = "wiremock.tests-d.com" + private const val WIREMOCK_REMOTE_URL = "https://$WIREMOCK_REMOTE_HOST" + + /** + * Upstream hosts that have no other override knob and should be funnelled into WireMock + * when [overriddenBaseUrl] is set. Each matched URL becomes `//`, + * so mock mappings should live under that host-prefixed path in tangem-api-mocks. Matching + * is done against the request's parsed host (exact equality) — substring matching would + * incorrectly redirect look-alikes such as `deep-index.moralis.io.evil.example`. + */ + private val REDIRECTABLE_THIRD_PARTY_HOSTS = setOf( + "deep-index.moralis.io", + "solana-gateway.moralis.io", + ) /** * Override base URL for WireMock requests. diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt index b8d5867f2e..c12417b238 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt @@ -11,6 +11,7 @@ object MainScreenTestTags { const val CARD_TITLE = "MAIN_SCREEN_CARD_TITLE" const val CARD_IMAGE = "MAIN_SCREEN_CARD_IMAGE" const val DEVICES_COUNT = "MAIN_SCREEN_DEVICES_COUNT" + const val SYNC_PROGRESS_TEXT = "MAIN_SCREEN_SYNC_PROGRESS_TEXT" const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE" const val TOTAL_BALANCE_MENU_ITEM = "MAIN_SCREEN_TOTAL_BALANCE_MENU_ITEM" diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/WalletNotificationTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/WalletNotificationTestTags.kt new file mode 100644 index 0000000000..a2ea41d594 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/WalletNotificationTestTags.kt @@ -0,0 +1,5 @@ +package com.tangem.core.ui.test + +object WalletNotificationTestTags { + const val ASSETS_DISCOVERY_BANNER = "WALLET_NOTIFICATION_ASSETS_DISCOVERY_BANNER" +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 9f7899e8e9..2da96f8bd2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -337,6 +337,7 @@ private fun AdditionalInfo( id = R.string.initial_wallet_sync_restore_progress, formatArgs = wrappedList(animatedContent.progressPercent), ), + testTag = MainScreenTestTags.SYNC_PROGRESS_TEXT, ) CircularProgressIndicator( modifier = Modifier.size(TangemTheme.dimens.size16), @@ -353,14 +354,14 @@ private fun AdditionalInfo( } @Composable -private fun AdditionalInfoText(text: TextReference) { +private fun AdditionalInfoText(text: TextReference, testTag: String = MainScreenTestTags.DEVICES_COUNT) { Text( text = text.resolveReference(), color = TangemTheme.colors.text.tertiary, maxLines = 1, overflow = TextOverflow.Ellipsis, style = TangemTheme.typography.caption2, - modifier = Modifier.testTag(MainScreenTestTags.DEVICES_COUNT), + modifier = Modifier.testTag(testTag), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index 4459d2403c..765a96d132 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -1,9 +1,11 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common +import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString @@ -16,6 +18,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.ForceDarkTheme import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.WalletNotificationTestTags import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import kotlinx.collections.immutable.ImmutableList @@ -73,26 +76,35 @@ internal fun LazyListScope.notifications(configs: ImmutableList { - Notification( - config = item.config, - modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null), - iconTint = when (item) { - is WalletNotification.Critical -> TangemTheme.colors.icon.warning - is WalletNotification.Informational -> TangemTheme.colors.icon.accent - is WalletNotification.RateApp -> TangemTheme.colors.icon.attention - is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1 - is WalletNotification.UsedOutdatedData -> TangemTheme.colors.text.attention - else -> null - }, - subtitleColor = TangemTheme.colors.text.secondary, - ) - } + else -> DefaultWalletNotification(item = item, modifier = modifier) } }, ) } +@Composable +private fun LazyItemScope.DefaultWalletNotification(item: WalletNotification, modifier: Modifier = Modifier) { + val itemModifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null) + val taggedModifier = when (item) { + is WalletNotification.AssetsDiscoveryCompleted -> + itemModifier.testTag(WalletNotificationTestTags.ASSETS_DISCOVERY_BANNER) + else -> itemModifier + } + Notification( + config = item.config, + modifier = taggedModifier, + iconTint = when (item) { + is WalletNotification.Critical -> TangemTheme.colors.icon.warning + is WalletNotification.Informational -> TangemTheme.colors.icon.accent + is WalletNotification.RateApp -> TangemTheme.colors.icon.attention + is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1 + is WalletNotification.UsedOutdatedData -> TangemTheme.colors.text.attention + else -> null + }, + subtitleColor = TangemTheme.colors.text.secondary, + ) +} + @Composable private fun yieldBoostPromoTitle(): AnnotatedString { val accent = TangemTheme.colors.text.accent