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 4b2d416965..0974d1c8fb 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt @@ -32,10 +32,12 @@ object TestConstants { const val DOGECOIN_RECIPIENT_ADDRESS = "DJQR3bdhBKcFGMHX2BkMCkrMFApNWNzr6V" const val DOGECOIN_ADDRESS = "DJ2TaZ5vvp3mBLugUpKjVM3pRBLi4uYaqz" const val TERRA_RECIPIENT_ADDRESS = "terra148dmp5ccazcwdmrcpvqz5rprnn886kemqen3tj" + const val POLYGON_RECIPIENT_ADDRESS = "0x742d35cc6634c0532925a3b844bc9e7595f2bd18" const val WAIT_UNTIL_TIMEOUT = 20_000L const val WAIT_UNTIL_TIMEOUT_LONG = 30_000L const val WAIT_UNTIL_TIMEOUT_VERY_LONG = 60_000L + const val HOLD_DURATION_MS = 2_000L const val MARKETS_MAIN_NETWORK_SUFFIX = "MAIN" @@ -55,4 +57,5 @@ object TestConstants { "cable meadow add game meat rigid pride" const val SEED_PHRASE_24 = "force visit fresh brown razor target ill scissors figure cave feel genre cargo category " + "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" } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt index 6f2931a598..343231d121 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt @@ -1,6 +1,8 @@ package com.tangem.scenarios +import androidx.compose.ui.test.longClick import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.HOLD_DURATION_MS import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG @@ -184,4 +186,70 @@ fun BaseTestCase.openSendConfirmScreenViaNextButton() { step("Assert 'Send' button on 'Send confirm' screen is displayed") { onSendConfirmScreen { sendButton.assertIsDisplayed() } } +} + +fun BaseTestCase.openSendSuccessScreenViaLongClickOnSendButton() { + step("Long click on 'Send' button") { + onSendConfirmScreen { + waitForIdle() + sendButton.assertIsEnabled() + sendButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) } + } + } + step("Assert 'Transaction sent' screen is displayed") { + onSendSuccessScreen { container.assertIsDisplayed() } + } +} + +fun BaseTestCase.checkSendViaSwapSuccessScreen() { + step("Assert 'Transaction sent' title is displayed") { + onSendSuccessScreen { title.assertIsDisplayed() } + } + step("Assert 'Transaction date' is displayed") { + onSendSuccessScreen { transactionDate.assertIsDisplayed() } + } + step("Assert 'Send from' block is displayed") { + onSendSuccessScreen { sendFromBlock.assertIsDisplayed() } + } + step("Assert 'Amount to receive' block is displayed") { + onSendSuccessScreen { sendToAmountBlock.assertIsDisplayed() } + } + step("Assert 'Provider' block is displayed") { + onSendSuccessScreen { providerBlock.assertIsDisplayed() } + } + step("Assert 'Recipient address' block is displayed") { + onSendSuccessScreen { recipientAddressBlock.assertIsDisplayed() } + } + step("Assert 'Network fee' block is displayed") { + onSendSuccessScreen { feeBlock.assertIsDisplayed() } + } + step("Assert 'Explore' button is displayed") { + onSendSuccessScreen { exploreButton.assertIsDisplayed() } + } + step("Assert 'Share' button is displayed") { + onSendSuccessScreen { shareButton.assertIsDisplayed() } + } + step("Assert 'Close' button is displayed") { + onSendSuccessScreen { closeButton.assertIsDisplayed() } + } +} + +fun BaseTestCase.selectTokenToSendViaSwap( + swapTokenName: String, + networkName: String, + networkType: String? = null, +) { + step("Click on 'Send' button") { + onTokenDetailsScreen { sendButton().performClick() } + } + step("Click on 'Swap to another token' button") { + onSendScreen { swapToAnotherTokenButton.performClick() } + } + step("Click on token: '$swapTokenName'") { + onSendViaSwapScreen { tokenItem(swapTokenName).performClick() } + } + val networkLabel = if (networkType.isNullOrBlank()) networkName else "$networkName $networkType" + step("Click on '$networkLabel' network") { + onChooseNetworkBottomSheet { networkItem(networkName, networkType).performClick() } + } } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt index 1c179d6575..4a200f8033 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt @@ -20,10 +20,12 @@ class ChooseNetworkBottomSheetPageObject(semanticsProvider: SemanticsNodeInterac useUnmergedTree = true } - fun networkItem(title: String, subtitle: String): KNode = child { + fun networkItem(name: String, type: String? = null): KNode = child { hasTestTag(ChooseNetworkBottomSheetTestTags.NETWORK_ITEM) - hasAnyDescendant(withText(title)) - hasAnyDescendant(withText(subtitle)) + hasAnyDescendant(withText(name)) + if (!type.isNullOrBlank()) { + hasAnyDescendant(withText(type)) + } useUnmergedTree = true } } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SendSuccessPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SendSuccessPageObject.kt new file mode 100644 index 0000000000..c593a2566d --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SendSuccessPageObject.kt @@ -0,0 +1,77 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.common.R +import com.tangem.core.ui.test.BaseButtonTestTags +import com.tangem.core.ui.test.TransactionSuccessScreenTestTags +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasText as withText + +class SendSuccessPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val container: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.CONTAINER) + } + + val title: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.TITLE) + useUnmergedTree = true + } + + val transactionDate: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.TRANSACTION_DATE) + useUnmergedTree = true + } + + val sendFromBlock: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.AMOUNT_BLOCK) + hasAnyDescendant(withText(getResourceString(R.string.send_from_title))) + useUnmergedTree = true + } + + val sendToAmountBlock: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.AMOUNT_BLOCK) + hasAnyDescendant( + withText(getResourceString(R.string.send_with_swap_recipient_amount_success_title)) + ) + useUnmergedTree = true + } + + val providerBlock: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.PROVIDER_BLOCK) + useUnmergedTree = true + } + + val recipientAddressBlock: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.RECIPIENT_BLOCK) + useUnmergedTree = true + } + + val feeBlock: KNode = child { + hasTestTag(TransactionSuccessScreenTestTags.FEE_BLOCK) + useUnmergedTree = true + } + + val exploreButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_explore)) + } + + val closeButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_close)) + } + + val shareButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_share)) + } +} + +internal fun BaseTestCase.onSendSuccessScreen(function: SendSuccessPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt index dff98cfcee..4996f7db80 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt @@ -16,6 +16,7 @@ import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onCompose import io.github.kakaocup.compose.node.element.KNode import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasTestTag as withTestTag import androidx.compose.ui.test.hasText as withText class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : @@ -192,6 +193,20 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide hasText(getResourceString(R.string.common_buy_currency, feeCurrencySymbol)) useUnmergedTree = true } + + fun expressStatusItem(title: String): KNode = child { + hasTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM) + hasAnyDescendant( + withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TITLE) and withText(title) + ) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_FROM_ICON)) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_FROM_AMOUNT)) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_SWAP_ICON)) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TO_ICON)) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TO_AMOUNT)) + hasAnyDescendant(withTestTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_CHEVRON_ICON)) + useUnmergedTree = true + } } internal fun BaseTestCase.onTokenDetailsScreen(function: TokenDetailsPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/tests/FeedbackTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/FeedbackTest.kt index 528a6535fb..6f103dd4b6 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/FeedbackTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/FeedbackTest.kt @@ -32,7 +32,6 @@ import com.tangem.tap.domain.sdk.mocks.MockProvider import dagger.hilt.android.testing.HiltAndroidTest import io.qameta.allure.kotlin.AllureId import io.qameta.allure.kotlin.junit4.DisplayName -import org.junit.Ignore import org.junit.Test import javax.inject.Inject @@ -73,7 +72,6 @@ class FeedbackTest : BaseTestCase() { } } - @Ignore("TODO: [REDACTED_JIRA]") @AllureId("893") @DisplayName("Send feedback: failed transaction") @Test diff --git a/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt index 2cb635f684..1f795cadce 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt @@ -21,7 +21,6 @@ import com.tangem.tap.domain.sdk.mocks.content.TwinsMockContent import dagger.hilt.android.testing.HiltAndroidTest import io.qameta.allure.kotlin.AllureId import io.qameta.allure.kotlin.junit4.DisplayName -import org.junit.Ignore import org.junit.Test @HiltAndroidTest @@ -462,7 +461,6 @@ class MainScreenActionButtonsTest : BaseTestCase() { } } - @Ignore("TODO: [REDACTED_JIRA]") @AllureId("4396") @DisplayName("Action buttons (main screen): click on buttons without data") @Test diff --git a/app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt index 22f4645cf3..1280e102da 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt @@ -2,14 +2,18 @@ package com.tangem.tests.send.sendViaSwap import com.tangem.common.BaseTestCase import com.tangem.common.R -import com.tangem.common.extensions.extractText import com.tangem.common.constants.TestConstants.ETHEREUM_RECIPIENT_ADDRESS +import com.tangem.common.constants.TestConstants.POLYGON_RECIPIENT_ADDRESS +import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO import com.tangem.common.constants.TestConstants.SOLANA_RECIPIENT_ADDRESS +import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12 +import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.extensions.extractText import com.tangem.common.utils.resetWireMockScenarioState import com.tangem.common.utils.setWireMockScenarioState -import com.tangem.scenarios.openSendConfirmScreenViaNextButton -import com.tangem.scenarios.openSendScreen +import com.tangem.scenarios.* import com.tangem.screens.* import dagger.hilt.android.testing.HiltAndroidTest import io.github.kakaocup.kakao.common.utilities.getResourceString @@ -369,4 +373,278 @@ class SendViaSwapTest : BaseTestCase() { } } } + + @AllureId("3967") + @DisplayName("Send via Swap: full successful send via swap flow") + @Test + fun sendViaSwapSuccessfulFlowTest() { + val tokenName = "Bitcoin" + val swapTokenName = "Ethereum" + val main = "MAIN" + val inputAmount = "0.001" + val providerName = "SimpleSwap" + val expressStatusItemTitle = getResourceString(R.string.express_exchange_by, providerName) + val bitcoinBalanceScenarioName = "bitcoin_utxo" + val bitcoinBalanceScenarioState = "BalanceHotWalletSvS" + val assetsScenarioName = "express_api_assets" + val assetsScenarioState = "BitcoinExchangeEnabled" + val hotWalletScenarioState = "HotWalletSvS" + val providersScenarioName = "networks_providers" + val providersScenarioState = "HotWalletSvS" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(bitcoinBalanceScenarioName) + resetWireMockScenarioState(assetsScenarioName) + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(QUOTES_API_SCENARIO) + resetWireMockScenarioState(providersScenarioName) + } + ).run { + + step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$tokenName'") { + setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = tokenName) + } + step("Set WireMock scenario: '$bitcoinBalanceScenarioName' to state: '$bitcoinBalanceScenarioState'") { + setWireMockScenarioState(scenarioName = bitcoinBalanceScenarioName, state = bitcoinBalanceScenarioState) + } + step("Set WireMock scenario: '$assetsScenarioName' to state: '$assetsScenarioState'") { + setWireMockScenarioState(scenarioName = assetsScenarioName, state = assetsScenarioState) + } + step("Set WireMock scenario: '$providersScenarioName' to state: '$providersScenarioState'") { + setWireMockScenarioState(scenarioName = providersScenarioName, state = providersScenarioState) + } + + step("Open 'Main Screen' with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12) + } + step("Click on token with name: '$tokenName'") { + onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() } + } + step("Select token to Send via Swap") { + selectTokenToSendViaSwap(swapTokenName = swapTokenName, networkName = swapTokenName, networkType = main) + } + step("Type '$inputAmount' in text field") { + onSendScreen { amountInputTextField.performTextInput(inputAmount) } + } + step("Click on 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { + nextButton.assertIsEnabled() + nextButton.performClick() + } + } + } + step("Type recipient address") { + onSendAddressScreen { addressTextField.performTextReplacement(ETHEREUM_RECIPIENT_ADDRESS) } + } + step("Open 'Send confirm' screen via 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendConfirmScreenViaNextButton() + } + } + step("Assert 'Best rate' badge is displayed") { + onSendConfirmScreen { bestRateBadge.assertIsDisplayed() } + } + step("Open 'Send via swap success' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendSuccessScreenViaLongClickOnSendButton() + } + } + step("Check 'Send via swap' screen") { + checkSendViaSwapSuccessScreen() + } + step("Click on 'Explore' button") { + onSendSuccessScreen { exploreButton.performClick() } + } + step("Assert Chrome Browser is opened") { + ThirdPartyAppPageObject { assertChromeIsOpened() } + } + step("Press 'Back' button to close 'Chrome' browser") { + device.uiDevice.pressBack() + } + step("Click on 'Close' button") { + onSendSuccessScreen { closeButton.performClick() } + } + step("Assert 'Express status' item is displayed with title: '$expressStatusItemTitle'") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTokenDetailsScreen { expressStatusItem(expressStatusItemTitle).assertIsDisplayed() } + } + } + } + } + + @AllureId("4017") + @DisplayName("Send via Swap: send same token in different network") + @Test + fun sendSameTokenInDifferentNetworkTest() { + val tokenName = "Tether" + val swapTokenName = "Tether" + val networkName = "Polygon" + val inputAmount = "0.001" + val ethCallScenarioName = "eth_call_api" + val ethCallScenarioState = "Started" + val hotWalletScenarioState = "USDTHotWalletSvS" + val ethNetworkBalanceScenarioName = "eth_network_balance" + val ethNetworkBalanceScenarioState = "Started" + val providerName = "Changelly" + val expressStatusItemTitle = getResourceString(R.string.express_exchange_by, providerName) + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(ethCallScenarioName) + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(QUOTES_API_SCENARIO) + resetWireMockScenarioState(ethNetworkBalanceScenarioName) + } + ).run { + + step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$ethCallScenarioName' to state: '$ethCallScenarioState'") { + setWireMockScenarioState(scenarioName = ethCallScenarioName, state = ethCallScenarioState) + } + step("Set WireMock scenario: '$ethNetworkBalanceScenarioName' to state: '$ethNetworkBalanceScenarioState'") { + setWireMockScenarioState(scenarioName = ethNetworkBalanceScenarioName, state = ethNetworkBalanceScenarioState) + } + + step("Open 'Main Screen' with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12) + } + step("Click on token with name: '$tokenName'") { + onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() } + } + step("Select token to Send via Swap") { + selectTokenToSendViaSwap(swapTokenName = swapTokenName, networkName = networkName) + } + step("Type '$inputAmount' in text field") { + onSendScreen { amountInputTextField.performTextInput(inputAmount) } + } + step("Click on 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { + nextButton.assertIsEnabled() + nextButton.performClick() + } + } + } + step("Type recipient address") { + onSendAddressScreen { addressTextField.performTextReplacement(POLYGON_RECIPIENT_ADDRESS) } + } + step("Open 'Send confirm' screen via 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendConfirmScreenViaNextButton() + } + } + step("Assert 'Best rate' badge is displayed") { + onSendConfirmScreen { bestRateBadge.assertIsDisplayed() } + } + step("Open 'Send via swap success' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendSuccessScreenViaLongClickOnSendButton() + } + } + step("Check 'Send via swap' screen") { + checkSendViaSwapSuccessScreen() + } + step("Click on 'Close' button") { + onSendSuccessScreen { closeButton.performClick() } + } + step("Assert 'Express status' item is displayed with title: '$expressStatusItemTitle'") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTokenDetailsScreen { expressStatusItem(expressStatusItemTitle).assertIsDisplayed() } + } + } + } + } + + @AllureId("4545") + @DisplayName("Send via Swap: token with tag/memo send via swap flow") + @Test + fun sendViaSwapTokenWithTagTest() { + val tokenName = "XRP Ledger" + val swapTokenName = "Ethereum" + val main = "MAIN" + val inputAmount = "0.001" + val providerName = "Changelly" + val expressStatusItemTitle = getResourceString(R.string.express_exchange_by, providerName) + val hotWalletScenarioState = "XRPHotWalletSvS" + val xrpExchangeQuoteScenarioName = "xrp_exchange_quote" + val xrpExchangeDataScenarioName = "xrp_exchange_data" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(USER_TOKENS_API_SCENARIO) + resetWireMockScenarioState(QUOTES_API_SCENARIO) + resetWireMockScenarioState(xrpExchangeQuoteScenarioName) + resetWireMockScenarioState(xrpExchangeDataScenarioName) + } + ).run { + + step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$xrpExchangeQuoteScenarioName' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = xrpExchangeQuoteScenarioName, state = hotWalletScenarioState) + } + step("Set WireMock scenario: '$xrpExchangeDataScenarioName' to state: '$hotWalletScenarioState'") { + setWireMockScenarioState(scenarioName = xrpExchangeDataScenarioName, state = hotWalletScenarioState) + } + + step("Open 'Main Screen' with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12) + } + step("Click on token with name: '$tokenName'") { + onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() } + } + step("Select token to Send via Swap") { + selectTokenToSendViaSwap(swapTokenName = swapTokenName, networkName = swapTokenName, networkType = main) + } + step("Type '$inputAmount' in text field") { + onSendScreen { amountInputTextField.performTextInput(inputAmount) } + } + step("Click on 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { + nextButton.assertIsEnabled() + nextButton.performClick() + } + } + } + step("Type recipient address") { + onSendAddressScreen { addressTextField.performTextReplacement(ETHEREUM_RECIPIENT_ADDRESS) } + } + step("Open 'Send confirm' screen via 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendConfirmScreenViaNextButton() + } + } + step("Open 'Send via swap success' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendSuccessScreenViaLongClickOnSendButton() + } + } + step("Check 'Send via swap' screen") { + checkSendViaSwapSuccessScreen() + } + step("Click on 'Close' button") { + onSendSuccessScreen { closeButton.performClick() } + } + step("Assert 'Express status' item is displayed with title: '$expressStatusItemTitle'") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTokenDetailsScreen { expressStatusItem(expressStatusItemTitle).assertIsDisplayed() } + } + } + } + } } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/swap/SwapMainScreenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/swap/SwapMainScreenTest.kt new file mode 100644 index 0000000000..678973c9d2 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/swap/SwapMainScreenTest.kt @@ -0,0 +1,38 @@ +package com.tangem.tests.swap + +import com.tangem.common.BaseTestCase +import com.tangem.common.annotations.ApiEnv +import com.tangem.common.annotations.ApiEnvConfig +import com.tangem.datasource.api.common.config.ApiConfig +import com.tangem.datasource.api.common.config.ApiEnvironment +import com.tangem.domain.models.scan.ProductType +import com.tangem.scenarios.openMainScreen +import com.tangem.screens.onMainScreen +import dagger.hilt.android.testing.HiltAndroidTest +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test + +@HiltAndroidTest +class SwapMainScreenTest : BaseTestCase() { + + @ApiEnv( + ApiEnvConfig(ApiConfig.ID.TangemTech, ApiEnvironment.PROD), + ApiEnvConfig(ApiConfig.ID.Express, ApiEnvironment.PROD) + ) + @AllureId("574") + @DisplayName("Swap: 'Swap' button is not displayed for single currency card") + @Test + fun singleTokenNoteCardScanTest() { + val cardType: ProductType = ProductType.Note + + setupHooks().run { + step("Open 'Main Screen' on '${cardType.name}' card") { + openMainScreen(cardType) + } + step("Assert 'Swap' button is not displayed") { + onMainScreen { swapButton.assertIsNotDisplayed() } + } + } + } +} \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt index 25b2c3389d..e44c2a673b 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/ExpressStatusItem.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -28,6 +29,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TokenDetailsScreenTestTags @Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod", "LongParameterList") @Composable @@ -50,7 +52,8 @@ internal fun ExpressStatusItem( .clip(TangemTheme.shapes.roundedCornersXMedium) .background(TangemTheme.colors.background.primary) .clickable { onClick() } - .padding(TangemTheme.dimens.spacing12), + .padding(TangemTheme.dimens.spacing12) + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM), ) { val (titleRef, iconRef, infoIconRef, swapIconRef, fromRef, toRef, fromIconRef, toIconRef) = createRefs() val padding6 = TangemTheme.dimens.spacing6 @@ -59,10 +62,12 @@ internal fun ExpressStatusItem( text = title.resolveReference(), style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, - modifier = Modifier.constrainAs(titleRef) { - start.linkTo(parent.start) - top.linkTo(parent.top) - }, + modifier = Modifier + .constrainAs(titleRef) { + start.linkTo(parent.start) + top.linkTo(parent.top) + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TITLE), ) CurrencyIcon( state = fromTokenIconState, @@ -73,20 +78,23 @@ internal fun ExpressStatusItem( start.linkTo(parent.start) top.linkTo(titleRef.bottom, padding6) bottom.linkTo(parent.bottom) - }, + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_FROM_ICON), ) EllipsisText( text = fromAmount.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.primary1, ellipsis = TextEllipsis.OffsetEnd(fromSymbol.length), - modifier = Modifier.constrainAs(fromRef) { - start.linkTo(fromIconRef.end, padding6) - top.linkTo(titleRef.bottom, padding6) - end.linkTo(swapIconRef.start) - bottom.linkTo(parent.bottom) - width = Dimension.fillToConstraints.atMostWrapContent - }, + modifier = Modifier + .constrainAs(fromRef) { + start.linkTo(fromIconRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + end.linkTo(swapIconRef.start) + bottom.linkTo(parent.bottom) + width = Dimension.fillToConstraints.atMostWrapContent + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_FROM_AMOUNT), ) Icon( painter = painterResource(id = R.drawable.ic_forward_24), @@ -99,7 +107,8 @@ internal fun ExpressStatusItem( top.linkTo(titleRef.bottom, padding6) end.linkTo(toIconRef.start) bottom.linkTo(parent.bottom) - }, + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_SWAP_ICON), ) CurrencyIcon( state = toTokenIconState, @@ -111,20 +120,23 @@ internal fun ExpressStatusItem( top.linkTo(titleRef.bottom, padding6) end.linkTo(toRef.start) bottom.linkTo(parent.bottom) - }, + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TO_ICON), ) EllipsisText( text = toAmount.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.primary1, ellipsis = TextEllipsis.OffsetEnd(toSymbol.length), - modifier = Modifier.constrainAs(toRef) { - start.linkTo(toIconRef.end, padding6) - top.linkTo(titleRef.bottom, padding6) - end.linkTo(infoIconRef.start, padding6, padding6) - bottom.linkTo(parent.bottom) - width = Dimension.fillToConstraints.atLeastWrapContent - }, + modifier = Modifier + .constrainAs(toRef) { + start.linkTo(toIconRef.end, padding6) + top.linkTo(titleRef.bottom, padding6) + end.linkTo(infoIconRef.start, padding6, padding6) + bottom.linkTo(parent.bottom) + width = Dimension.fillToConstraints.atLeastWrapContent + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_TO_AMOUNT), ) Icon( painter = painterResource(id = infoIconRes ?: R.drawable.ic_alert_triangle_20), @@ -153,7 +165,8 @@ internal fun ExpressStatusItem( end.linkTo(parent.end) top.linkTo(parent.top) bottom.linkTo(parent.bottom) - }, + } + .testTag(TokenDetailsScreenTestTags.EXPRESS_STATUS_ITEM_CHEVRON_ICON), ) } } diff --git a/core/datasource/src/main/java/com/tangem/datasource/crypto/MockDataSignatureVerifier.kt b/core/datasource/src/main/java/com/tangem/datasource/crypto/MockDataSignatureVerifier.kt new file mode 100644 index 0000000000..79f0b6cb63 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/crypto/MockDataSignatureVerifier.kt @@ -0,0 +1,6 @@ +package com.tangem.datasource.crypto + +internal class MockDataSignatureVerifier : DataSignatureVerifier { + + override fun verifySignature(signature: String, data: String): Boolean = true +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SecurityModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SecurityModule.kt index c04614c1f7..49ba336022 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/SecurityModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/SecurityModule.kt @@ -1,7 +1,9 @@ package com.tangem.datasource.di +import com.tangem.datasource.BuildConfig import com.tangem.datasource.api.common.config.managers.ApiConfigsManager import com.tangem.datasource.crypto.DataSignatureVerifier +import com.tangem.datasource.crypto.MockDataSignatureVerifier import com.tangem.datasource.crypto.Sha256SignatureVerifier import com.tangem.datasource.local.config.environment.EnvironmentConfig import dagger.Module @@ -20,6 +22,10 @@ internal object SecurityModule { environmentConfig: EnvironmentConfig, apiConfigsManager: ApiConfigsManager, ): DataSignatureVerifier { - return Sha256SignatureVerifier(environmentConfig, apiConfigsManager) + return if (BuildConfig.MOCK_DATA_SOURCE) { + MockDataSignatureVerifier() + } else { + Sha256SignatureVerifier(environmentConfig, apiConfigsManager) + } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemHoldToConfirmButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemHoldToConfirmButton.kt index 3ee514559a..9e5c8386c2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemHoldToConfirmButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemHoldToConfirmButton.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -58,6 +59,7 @@ import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.res.LocalHapticManager import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.BaseButtonTestTags import kotlin.coroutines.coroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -145,6 +147,7 @@ internal fun TangemHoldToConfirmButton( Surface( modifier = modifier .heightIn(min = buttonHeight) + .testTag(BaseButtonTestTags.BUTTON) .graphicsLayer { scaleX = state.scaleProgress.value scaleY = state.scaleProgress.value @@ -442,10 +445,12 @@ private fun HoldToConfirmButtonContent( ) } else { Text( - modifier = Modifier.graphicsLayer { - translationX = state.shakeOffset.value - alpha = state.textAlpha.value - }, + modifier = Modifier + .testTag(BaseButtonTestTags.TEXT) + .graphicsLayer { + translationX = state.shakeOffset.value + alpha = state.textAlpha.value + }, text = if (state.isHintVisible) textConfig.hintText else textConfig.text, style = textConfig.style, color = color.contentColor, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt index 1dea3f423a..fb2f6005b4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowBestRate.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -30,6 +31,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TransactionSuccessScreenTestTags /** * [Input Row Best Rate](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-889&mode=dev) @@ -61,7 +63,8 @@ fun InputRowBestRate( Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .padding(TangemTheme.dimens.spacing12), + .padding(TangemTheme.dimens.spacing12) + .testTag(TransactionSuccessScreenTestTags.PROVIDER_BLOCK), ) { InputRowAsyncImage(imageUrl = imageUrl, modifier = Modifier.size(TangemTheme.dimens.spacing40)) Column( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt index 0a5cc124d1..584f323bf4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -21,6 +22,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TransactionSuccessScreenTestTags /** * Common transaction done screen title @@ -46,7 +48,8 @@ fun TransactionDoneTitle(title: TextReference, subtitle: TextReference, modifier style = TangemTheme.typography.h3, color = TangemTheme.colors.text.primary1, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing16), + .padding(top = TangemTheme.dimens.spacing16) + .testTag(TransactionSuccessScreenTestTags.TITLE), ) Text( text = subtitle.resolveReference(), @@ -54,7 +57,8 @@ fun TransactionDoneTitle(title: TextReference, subtitle: TextReference, modifier color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.Center, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing4), + .padding(top = TangemTheme.dimens.spacing4) + .testTag(TransactionSuccessScreenTestTags.TRANSACTION_DATE), ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TokenDetailsScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TokenDetailsScreenTestTags.kt index f9e953824d..9079c600f0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/TokenDetailsScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TokenDetailsScreenTestTags.kt @@ -15,4 +15,13 @@ object TokenDetailsScreenTestTags { const val STAKING_TOKEN_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_TOKEN_AMOUNT" const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE" const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON" + + const val EXPRESS_STATUS_ITEM = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM" + const val EXPRESS_STATUS_ITEM_TITLE = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_TITLE" + const val EXPRESS_STATUS_ITEM_FROM_ICON = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_FROM_ICON" + const val EXPRESS_STATUS_ITEM_FROM_AMOUNT = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_FROM_AMOUNT" + const val EXPRESS_STATUS_ITEM_SWAP_ICON = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_SWAP_ICON" + const val EXPRESS_STATUS_ITEM_TO_ICON = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_TO_ICON" + const val EXPRESS_STATUS_ITEM_TO_AMOUNT = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_TO_AMOUNT" + const val EXPRESS_STATUS_ITEM_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_CHEVRON_ICON" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TransactionSuccessScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TransactionSuccessScreenTestTags.kt new file mode 100644 index 0000000000..2f2ca91b51 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TransactionSuccessScreenTestTags.kt @@ -0,0 +1,11 @@ +package com.tangem.core.ui.test + +object TransactionSuccessScreenTestTags { + const val CONTAINER = "TRANSACTION_SUCCESS_SCREEN_CONTAINER" + const val TITLE = "TRANSACTION_SUCCESS_SCREEN_TITLE" + const val TRANSACTION_DATE = "TRANSACTION_SUCCESS_SCREEN_DATE" + const val AMOUNT_BLOCK = "TRANSACTION_SUCCESS_SCREEN_AMOUNT_BLOCK" + const val FEE_BLOCK = "TRANSACTION_SUCCESS_SCREEN_FEE_BLOCK" + const val PROVIDER_BLOCK = "TRANSACTION_SUCCESS_SCREEN_PROVIDER_BLOCK" + const val RECIPIENT_BLOCK = "TRANSACTION_SUCCESS_SCREEN_RECIPIENT_BLOCK" +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/success/ui/SendWithSwapSuccessContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/success/ui/SendWithSwapSuccessContent.kt index 834289a971..8acb32e4a7 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/success/ui/SendWithSwapSuccessContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/success/ui/SendWithSwapSuccessContent.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.blockchain.common.transaction.Fee @@ -36,6 +37,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TransactionSuccessScreenTestTags import com.tangem.core.ui.utils.DateTimeFormatters import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.express.models.ExpressProvider @@ -68,7 +70,8 @@ internal fun SendWithSwapSuccessContent(sendWithSwapUM: SendWithSwapUM) { Box( modifier = Modifier .weight(1f) - .background(TangemTheme.colors.background.tertiary), + .background(TangemTheme.colors.background.tertiary) + .testTag(TransactionSuccessScreenTestTags.CONTAINER), ) { SuccessContent( sendWithSwapUM = sendWithSwapUM, @@ -162,7 +165,8 @@ private fun AmountBlock( modifier = modifier .clip(TangemTheme.shapes.roundedCornersXMedium) .background(TangemTheme.colors.background.action) - .padding(12.dp), + .padding(12.dp) + .testTag(TransactionSuccessScreenTestTags.AMOUNT_BLOCK), ) { AccountTitle(accountTitleUM) Row( @@ -212,7 +216,8 @@ private fun FeeBlock(feeSelectorUM: FeeSelectorUM.Content) { .fillMaxWidth() .clip(TangemTheme.shapes.roundedCornersXMedium) .background(TangemTheme.colors.background.action) - .padding(TangemTheme.dimens.spacing12), + .padding(TangemTheme.dimens.spacing12) + .testTag(TransactionSuccessScreenTestTags.FEE_BLOCK), ) { Text( text = stringResourceSafe(R.string.common_network_fee_title), @@ -260,7 +265,8 @@ private fun DestinationBlock( .fillMaxWidth() .clip(TangemTheme.shapes.roundedCornersXMedium) .background(TangemTheme.colors.background.action) - .padding(12.dp), + .padding(12.dp) + .testTag(TransactionSuccessScreenTestTags.RECIPIENT_BLOCK), ) { Text( text = stringResourceSafe(R.string.send_recipient),