From eff4bd033679c6f4cb902bf26af15801fd8a71bd Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Apr 2026 18:10:28 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../common/extensions/CustomAssertsExt.kt | 11 + .../com/tangem/scenarios/SendScenarios.kt | 14 + .../ChooseNetworkBottomSheetPageObject.kt | 32 ++ .../com/tangem/screens/DialogPageObject.kt | 5 + .../screens/SelectProviderPageObject.kt | 2 +- ...SelectSwapProviderBottomSheetPageObject.kt | 28 ++ .../tangem/screens/SendAddressPageObject.kt | 5 + .../tangem/screens/SendConfirmPageObject.kt | 16 + .../com/tangem/screens/SendPageObject.kt | 16 + .../tangem/screens/SendViaSwapPageObject.kt | 34 ++ .../screens/WarningBottomSheetPageObject.kt | 41 ++ .../tests/send/sendViaSwap/SendViaSwapTest.kt | 372 ++++++++++++++++++ .../message/MessageBottomSheet.kt | 8 +- .../ui/components/icons/HighlightedIcon.kt | 5 +- .../test/ChooseNetworkBottomSheetTestTags.kt | 8 + .../SelectSwapProviderBottomSheetTestTags.kt | 10 + .../core/ui/test/SendConfirmScreenTestTags.kt | 2 + .../tangem/core/ui/test/SendScreenTestTags.kt | 4 + .../ui/test/WarningBottomSheetTestTags.kt | 7 + .../amount/ui/SendAmountContent.kt | 5 +- .../v2/impl/amount/ui/SwapAmountContent.kt | 7 +- .../ui/SwapChooseProviderContent.kt | 8 +- .../chooseprovider/ui/SwapProviderItem.kt | 16 +- .../ui/SwapChooseTokenNetworkContent.kt | 14 +- 24 files changed, 656 insertions(+), 14 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/SelectSwapProviderBottomSheetPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/SendViaSwapPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/WarningBottomSheetPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/ChooseNetworkBottomSheetTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/SelectSwapProviderBottomSheetTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt b/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt index 2815c2cc17..079d2580b5 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt @@ -80,6 +80,17 @@ fun List.assertTrustScoresValid() { } } +/** + * Extracts the text content from a [KNode]'s semantics. + * Useful when the displayed value is dynamic and you need to capture it for later assertions. + */ +fun KNode.extractText(): String { + val node = delegate.interaction.semanticsNodeInteraction.fetchSemanticsNode( + "Failed to extract text from KNode", + ) + return extractText(node) ?: error("Node does not contain SemanticsProperties.Text") +} + /** * Extracts the text value from a semantic node's config. */ diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt index 066600df5e..f7bcef3905 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/SendScenarios.kt @@ -167,4 +167,18 @@ fun BaseTestCase.checkChangesInInputTextField(title: String, newValue: String, a step("Assert '$title' value: '$newValue + $addition'") { onSendSelectNetworkFeeBottomSheet { inputTextFieldValue(title).assertTextContains(newValue + addition) } } +} + +fun BaseTestCase.openSendConfirmScreenViaNextButton() { + step("Click on 'Next' button") { + onSendAddressScreen { + addressesShimmer.assertIsNotDisplayed() + nextButton.assertIsDisplayed() + nextButton.assertIsEnabled() + nextButton.performClick() + } + } + step("Assert 'Send' button on 'Send confirm' screen is displayed") { + onSendConfirmScreen { sendButton.assertIsDisplayed() } + } } \ 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 new file mode 100644 index 0000000000..1c179d6575 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/ChooseNetworkBottomSheetPageObject.kt @@ -0,0 +1,32 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.R +import com.tangem.core.ui.test.BaseBottomSheetTestTags +import com.tangem.core.ui.test.ChooseNetworkBottomSheetTestTags +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 ChooseNetworkBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val title: KNode = child { + hasTestTag(BaseBottomSheetTestTags.TITLE) + hasText(getResourceString(R.string.common_choose_network)) + useUnmergedTree = true + } + + fun networkItem(title: String, subtitle: String): KNode = child { + hasTestTag(ChooseNetworkBottomSheetTestTags.NETWORK_ITEM) + hasAnyDescendant(withText(title)) + hasAnyDescendant(withText(subtitle)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onChooseNetworkBottomSheet(function: ChooseNetworkBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt index 39fc2fd1fc..13eef6b87d 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt @@ -59,6 +59,11 @@ class DialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : hasTestTag(BaseButtonTestTags.BUTTON) hasText(getResourceString(R.string.common_ok)) } + + val changeButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_change)) + } } internal fun BaseTestCase.onDialog(function: DialogPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt index 0be8e56d9d..7036998886 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt @@ -67,7 +67,7 @@ class SelectProviderPageObject(semanticsProvider: SemanticsNodeInteractionsProvi val instantProcessingSpeedText: KNode = child { - hasText(getResourceString(com.tangem.core.ui.R.string.onramp_instant_status)) + hasText(getResourceString(R.string.onramp_instant_status)) useUnmergedTree = true } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SelectSwapProviderBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SelectSwapProviderBottomSheetPageObject.kt new file mode 100644 index 0000000000..336759c5dc --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SelectSwapProviderBottomSheetPageObject.kt @@ -0,0 +1,28 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.SelectSwapProviderBottomSheetTestTags +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 androidx.compose.ui.test.hasTestTag as withTestTag +import androidx.compose.ui.test.hasText as withText + +class SelectSwapProviderBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + fun providerItem(name: String, type: String, isBestRate: Boolean): KNode = child { + hasTestTag(SelectSwapProviderBottomSheetTestTags.PROVIDER_ITEM) + hasAnyDescendant(withText(name)) + hasAnyDescendant(withText(type)) + if (isBestRate) { + hasAnyDescendant(withTestTag(SelectSwapProviderBottomSheetTestTags.BEST_RATE_LABEL)) + } + useUnmergedTree = true + } + +} + +internal fun BaseTestCase.onSelectSwapProviderBottomSheet(function: SelectSwapProviderBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SendAddressPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SendAddressPageObject.kt index b963fa0d14..85d6640b23 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SendAddressPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SendAddressPageObject.kt @@ -23,6 +23,11 @@ class SendAddressPageObject(semanticsProvider: SemanticsNodeInteractionsProvider useUnmergedTree = true } + val closeButton: KNode = child { + hasTestTag(TopAppBarTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + val addressesShimmer: KNode = child { hasTestTag(SendAddressScreenTestTags.ADDRESSES_SHIMMER) useUnmergedTree = true diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SendConfirmPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SendConfirmPageObject.kt index 505b43899c..6f857ab540 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SendConfirmPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SendConfirmPageObject.kt @@ -96,6 +96,21 @@ class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider useUnmergedTree = true } + val provider: KNode = child { + hasText(getResourceString(R.string.express_provider)) + useUnmergedTree = true + } + + val providerName: KNode = child { + hasTestTag(SendConfirmScreenTestTags.PROVIDER_NAME) + useUnmergedTree = true + } + + val bestRateBadge: KNode = child { + hasTestTag(SendConfirmScreenTestTags.BEST_RATE_BADGE) + useUnmergedTree = true + } + val blockchainAddress: KNode = child { hasTestTag(SendConfirmScreenTestTags.BLOCKCHAIN_ADDRESS) useUnmergedTree = true @@ -134,6 +149,7 @@ class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider hasTestTag(BaseButtonTestTags.BUTTON) hasText(getResourceString(CoreUiR.string.warning_button_refresh)) } + } internal fun BaseTestCase.onSendConfirmScreen(function: SendConfirmPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SendPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SendPageObject.kt index 6cc240110b..dc63b4a2ed 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SendPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SendPageObject.kt @@ -74,6 +74,22 @@ class SendPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : useUnmergedTree = true } + val swapToAnotherTokenButton: KNode = child { + hasTestTag(SendScreenTestTags.SWAP_TO_ANOTHER_TOKEN_BUTTON) + useUnmergedTree = true + } + + fun swapTokenName(tokenName: String): KNode = child { + hasTestTag(SendScreenTestTags.SWAP_TOKEN_NAME) + hasText(tokenName) + useUnmergedTree = true + } + + val closeConvertIcon: KNode = child { + hasTestTag(SendScreenTestTags.CLOSE_CONVERT_ICON) + useUnmergedTree = true + } + val nextButton: KNode = child { hasTestTag(BaseButtonTestTags.BUTTON) hasAnyDescendant(withText(getResourceString(SendR.string.common_next))) diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SendViaSwapPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SendViaSwapPageObject.kt new file mode 100644 index 0000000000..8c8fe725ab --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SendViaSwapPageObject.kt @@ -0,0 +1,34 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.BaseSearchBarTestTags +import com.tangem.core.ui.test.ManageTokensScreenTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +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 androidx.compose.ui.test.hasText as withText + +class SendViaSwapPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val closeButton: KNode = child { + hasTestTag(TopAppBarTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + + + val searchField: KNode = child { + hasTestTag(BaseSearchBarTestTags.SEARCH_BAR) + } + + fun tokenItem(tokenName: String): KNode = child { + hasTestTag(ManageTokensScreenTestTags.NETWORK_NAME) + hasAnyChild(withText(tokenName)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onSendViaSwapScreen(function: SendViaSwapPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/WarningBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/WarningBottomSheetPageObject.kt new file mode 100644 index 0000000000..a0a7cf8c2a --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/WarningBottomSheetPageObject.kt @@ -0,0 +1,41 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.R +import com.tangem.core.ui.test.BaseButtonTestTags +import com.tangem.core.ui.test.WarningBottomSheetTestTags +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 + +class WarningBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val icon: KNode = child { + hasTestTag(WarningBottomSheetTestTags.ICON) + useUnmergedTree = true + } + + fun title(title: String): KNode = child { + hasTestTag(WarningBottomSheetTestTags.TITLE) + hasText(title) + useUnmergedTree = true + } + + fun message(message: String): KNode = child { + hasTestTag(WarningBottomSheetTestTags.MESSAGE) + hasText(message) + useUnmergedTree = true + } + + val gotItButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(R.string.warning_button_ok)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onWarningBottomSheet(function: WarningBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file 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 new file mode 100644 index 0000000000..22f4645cf3 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/send/sendViaSwap/SendViaSwapTest.kt @@ -0,0 +1,372 @@ +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.SOLANA_RECIPIENT_ADDRESS +import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG +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.screens.* +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 + +@HiltAndroidTest +class SendViaSwapTest : BaseTestCase() { + + @AllureId("3970") + @DisplayName("Send via Swap: check unsupported token error") + @Test + fun unsupportedTokenErrorTest() { + val tokenName = "Bitcoin" + val stellar = "Stellar" + val bitcoinBalanceScenarioName = "bitcoin_utxo" + val bitcoinBalanceScenarioState = "Balance" + val assetsScenarioName = "express_api_assets" + val assetsScenarioState = "BitcoinExchangeEnabled" + val warningTitle = getResourceString(R.string.express_swap_not_supported_title, stellar) + val warningMessage = getResourceString(R.string.express_swap_not_supported_text) + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(bitcoinBalanceScenarioName) + resetWireMockScenarioState(assetsScenarioName) + } + ).run { + + 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("Open 'Send' screen") { + openSendScreen(tokenName) + } + step("Click on 'Swap to another token button'") { + onSendScreen { swapToAnotherTokenButton.performClick() } + } + step("Click on token: '$stellar'") { + onSendViaSwapScreen { tokenItem(stellar).performClick() } + } + step("Assert warning bottom sheet icon is displayed") { + onWarningBottomSheet { icon.assertIsDisplayed() } + } + step("Assert warning bottom sheet title is displayed") { + onWarningBottomSheet { title(warningTitle).assertIsDisplayed() } + } + step("Assert warning bottom sheet message is displayed") { + onWarningBottomSheet { message(warningMessage).assertIsDisplayed() } + } + step("Click on 'Ok, Got it!' button") { + onWarningBottomSheet { gotItButton.performClick() } + } + } + } + + @AllureId("3968") + @DisplayName("Send via Swap: flow with token search and data changes") + @Test + fun sendViaSwapFlowWithTokenSearchAndDataChangesTest() { + val tokenName = "Bitcoin" + val swapTokenName = "Ethereum" + val main = "MAIN" + val firstInputAmount = "0.001" + val secondInputAmount = "0.002" + val bestRateProviderName = "SimpleSwap" + val regularProviderName = "ChangeHero" + val cexType = "CEX" + val fastSelectorItem = getResourceString(R.string.common_fee_selector_option_fast) + var capturedFastFee: String? = null + val bitcoinBalanceScenarioName = "bitcoin_utxo" + val bitcoinBalanceScenarioState = "Balance" + val assetsScenarioName = "express_api_assets" + val assetsScenarioState = "BitcoinExchangeEnabled" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(bitcoinBalanceScenarioName) + resetWireMockScenarioState(assetsScenarioName) + } + ).run { + + 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("Open 'Send' screen") { + openSendScreen(tokenName) + } + step("Type '$firstInputAmount' in text field") { + onSendScreen { amountInputTextField.performTextInput(firstInputAmount) } + } + step("Click on 'Swap to another token' button") { + onSendScreen { swapToAnotherTokenButton.performClick() } + } + step("Type '$swapTokenName' in search text field") { + onSendViaSwapScreen { + searchField.performClick() + searchField.performTextInput(swapTokenName) + } + } + step("Click on token: '$swapTokenName'") { + onSendViaSwapScreen { tokenItem(swapTokenName).performClick() } + } + step("Click on '$swapTokenName $main' network") { + onChooseNetworkBottomSheet { networkItem(swapTokenName, main).performClick() } + } + 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("Click on 'Provider'") { + onSendConfirmScreen { provider.performClick() } + } + step("Assert '$bestRateProviderName' is displayed") { + onSelectSwapProviderBottomSheet { + providerItem( + name = bestRateProviderName, + type = cexType, + isBestRate = true + ).assertIsDisplayed() + } + } + step("Click on '$regularProviderName'") { + onSelectSwapProviderBottomSheet { + providerItem( + name = regularProviderName, + type = cexType, + isBestRate = false + ).performClick() + } + } + step("Assert provider name is '$regularProviderName'") { + onSendConfirmScreen { providerName.assertTextContains(regularProviderName) } + } + step("Assert 'Best rate' badge is not displayed") { + onSendConfirmScreen { bestRateBadge.assertIsNotDisplayed() } + } + step("Assert provider name is '$regularProviderName'") { + onSendConfirmScreen { providerName.assertTextContains(regularProviderName) } + } + step("Click on fee selector icon") { + onSendConfirmScreen { feeSelectorIcon.performClick() } + } + step("Click on '$fastSelectorItem' selector item is displayed") { + onSendSelectNetworkFeeBottomSheet { regularFeeSelectorItem(fastSelectorItem).performClick() } + } + step("Capture fast fee value") { + onSendConfirmScreen { capturedFastFee = feeAmount.extractText() } + } + step("Click on send amount") { + onSendConfirmScreen { primaryAmount.performClick() } + } + step("Click on swap token '$tokenName'") { + onSendScreen { swapTokenName(tokenName).performClick() } + } + step("Type '$secondInputAmount' in text field") { + onSendScreen { amountInputTextField.performTextReplacement(secondInputAmount) } + } + step("Click on 'Continue' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { + continueButton.assertIsDisplayed() + continueButton.assertIsEnabled() + continueButton.performClick() + } + onSendConfirmScreen { sendButton.assertIsDisplayed() } + } + } + step("Assert 'Best rate' badge is displayed") { + onSendConfirmScreen { bestRateBadge.assertIsDisplayed() } + } + step("Assert provider name is '$bestRateProviderName'") { + onSendConfirmScreen { providerName.assertTextContains(bestRateProviderName) } + } + step("Assert fee is equal to previously captured fast fee") { + val expectedFee = requireNotNull(capturedFastFee) { "Fast fee was not captured" } + onSendConfirmScreen { feeAmount.assertTextContains(expectedFee) } + } + } + } + + @AllureId("3969") + @DisplayName("Send via Swap: flow cancel and return") + @Test + fun sendViaSwapCancelAndReturnFlowTest() { + val tokenName = "Bitcoin" + val firstSwapTokenName = "Ethereum" + val secondSwapTokenName = "Solana" + val main = "MAIN" + val inputAmount = "0.001" + val bitcoinBalanceScenarioName = "bitcoin_utxo" + val bitcoinBalanceScenarioState = "Balance" + val assetsScenarioName = "express_api_assets" + val assetsScenarioState = "BitcoinExchangeEnabled" + val dialogTitle = getResourceString(R.string.send_with_swap_change_token_alert_title) + val dialogMessage = getResourceString(R.string.send_with_swap_change_token_alert_message) + val addressHint = getResourceString(R.string.send_enter_address_field_ens) + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(bitcoinBalanceScenarioName) + resetWireMockScenarioState(assetsScenarioName) + } + ).run { + + 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("Open 'Send' screen") { + openSendScreen(tokenName) + } + step("Type '$inputAmount' in text field") { + onSendScreen { amountInputTextField.performTextInput(inputAmount) } + } + step("Click on 'Swap to another token' button") { + onSendScreen { swapToAnotherTokenButton.performClick() } + } + step("Click on token: '$firstSwapTokenName'") { + onSendViaSwapScreen { tokenItem(firstSwapTokenName).performClick() } + } + step("Click on '$firstSwapTokenName $main' network") { + onChooseNetworkBottomSheet { networkItem(firstSwapTokenName, main).performClick() } + } + step("Click on 'Close convert' button") { + onSendScreen { closeConvertIcon.performClick() } + } + step("Assert 'Swap to another token' button is displayed") { + onSendScreen { swapToAnotherTokenButton.assertIsDisplayed() } + } + step("Assert amount: '$inputAmount'") { + onSendScreen { amountInputTextField.assertTextContains(inputAmount) } + } + step("Click on 'Swap to another token' button") { + onSendScreen { swapToAnotherTokenButton.performClick() } + } + step("Click on token: '$secondSwapTokenName'") { + onSendViaSwapScreen { tokenItem(secondSwapTokenName).performClick() } + } + step("Click on '$secondSwapTokenName $main' network") { + onChooseNetworkBottomSheet { networkItem(secondSwapTokenName, main).performClick() } + } + step("Click on token '$tokenName'") { + onSendScreen { swapTokenName(tokenName).performClick() } + } + 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(SOLANA_RECIPIENT_ADDRESS) } + } + step("Open 'Send confirm' screen via 'Next' button") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + openSendConfirmScreenViaNextButton() + } + } + step("Click on 'Amount to receive'") { + onSendConfirmScreen { secondaryAmount.performClick() } + } + step("Click on token '$secondSwapTokenName' to open 'Send via Swap' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { swapTokenName(secondSwapTokenName).performClick() } + onSendViaSwapScreen { tokenItem(firstSwapTokenName).assertIsDisplayed() } + } + } + step("Click on token: '$firstSwapTokenName'") { + onSendViaSwapScreen { tokenItem(firstSwapTokenName).performClick() } + } + step("Click on '$firstSwapTokenName $main' network") { + onChooseNetworkBottomSheet { networkItem(firstSwapTokenName, main).performClick() } + } + step("Assert '$dialogTitle' dialog title is displayed") { + onDialog { title.assertTextEquals(dialogTitle) } + } + step("Assert '$dialogMessage' dialog message is displayed") { + onDialog { text.assertTextEquals(dialogMessage) } + } + step("Click on 'Cancel' button") { + onDialog { cancelButton.performClick() } + } + step("Click on 'Close' button") { + onSendViaSwapScreen { closeButton.performClick() } + } + step("Click on 'Continue' button") { + onSendScreen { continueButton.performClick() } + } + step("Assert recipient address is displayed") { + onSendConfirmScreen { recipientAddress(SOLANA_RECIPIENT_ADDRESS).assertIsDisplayed() } + } + step("Click on 'Amount to receive'") { + onSendConfirmScreen { secondaryAmount.performClick() } + } + step("Click on token '$secondSwapTokenName' to open 'Send via Swap' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { swapTokenName(secondSwapTokenName).performClick() } + onSendViaSwapScreen { tokenItem(firstSwapTokenName).assertIsDisplayed() } + } + } + step("Click on token: $firstSwapTokenName") { + onSendViaSwapScreen { tokenItem(firstSwapTokenName).performClick() } + } + step("Click on '$firstSwapTokenName $main' network") { + onChooseNetworkBottomSheet { networkItem(firstSwapTokenName, main).performClick() } + } + step("Click on 'Change' button") { + onDialog { changeButton.performClick() } + } + step("Assert '$firstSwapTokenName' is displayed") { + onSendScreen { swapTokenName(firstSwapTokenName).assertIsDisplayed() } + } + step("Click on 'Next' button to open 'Send address' screen") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onSendScreen { + nextButton.assertIsEnabled() + nextButton.performClick() + } + onSendAddressScreen { container.assertIsDisplayed() } + } + } + step("Assert address text field is empty and hint is displayed") { + onSendAddressScreen { addressTextFieldHint.assertTextContains(addressHint) } + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheet.kt index a3a620e80d..c32033908e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheet.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment 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.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -32,6 +33,7 @@ import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.WarningBottomSheetTestTags import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList @@ -99,7 +101,8 @@ private fun ContentContainer(state: MessageBottomSheetUM.InfoBlock, modifier: Mo Text( modifier = Modifier .fillMaxWidth() - .padding(top = TangemTheme.dimens.spacing24), + .padding(top = TangemTheme.dimens.spacing24) + .testTag(WarningBottomSheetTestTags.TITLE), text = title.resolveReference(), style = TangemTheme.typography.h3, color = TangemTheme.colors.text.primary1, @@ -110,7 +113,8 @@ private fun ContentContainer(state: MessageBottomSheetUM.InfoBlock, modifier: Mo Text( modifier = Modifier .fillMaxWidth() - .padding(top = TangemTheme.dimens.spacing8), + .padding(top = TangemTheme.dimens.spacing8) + .testTag(WarningBottomSheetTestTags.MESSAGE), text = body.resolveAnnotatedReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.secondary, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/icons/HighlightedIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/icons/HighlightedIcon.kt index a94578a5ff..5253043f18 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/icons/HighlightedIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/icons/HighlightedIcon.kt @@ -11,8 +11,10 @@ import androidx.compose.ui.Alignment 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 com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.WarningBottomSheetTestTags @Composable fun HighlightedIcon( @@ -25,7 +27,8 @@ fun HighlightedIcon( modifier = modifier .size(TangemTheme.dimens.size56) .clip(CircleShape) - .background(backgroundColor.copy(alpha = 0.1F)), + .background(backgroundColor.copy(alpha = 0.1F)) + .testTag(WarningBottomSheetTestTags.ICON), contentAlignment = Alignment.Center, content = { Icon( diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/ChooseNetworkBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/ChooseNetworkBottomSheetTestTags.kt new file mode 100644 index 0000000000..0d3f565b90 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/ChooseNetworkBottomSheetTestTags.kt @@ -0,0 +1,8 @@ +package com.tangem.core.ui.test + +object ChooseNetworkBottomSheetTestTags { + const val NETWORK_ITEM = "CHOOSE_NETWORK_BOTTOM_SHEET_NETWORK_ITEM" + const val NETWORK_ICON = "CHOOSE_NETWORK_BOTTOM_SHEET_NETWORK_ICON" + const val NETWORK_TITLE = "CHOOSE_NETWORK_BOTTOM_SHEET_NETWORK_TITLE" + const val NETWORK_SUBTITLE = "CHOOSE_NETWORK_BOTTOM_SHEET_NETWORK_SUBTITLE" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SelectSwapProviderBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SelectSwapProviderBottomSheetTestTags.kt new file mode 100644 index 0000000000..9482e08662 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SelectSwapProviderBottomSheetTestTags.kt @@ -0,0 +1,10 @@ +package com.tangem.core.ui.test + +object SelectSwapProviderBottomSheetTestTags { + + const val PROVIDER_NAME = "SELECT_SWAP_PROVIDER_BOTTOM_SHEET_PROVIDER_NAME" + const val PROVIDER_TYPE = "SELECT_SWAP_PROVIDER_BOTTOM_SHEET_PROVIDER_TYPE" + const val BEST_RATE_LABEL = "SELECT_SWAP_PROVIDER_BOTTOM_SHEET_BEST_RATE_LABEL" + const val AMOUNT = "SELECT_SWAP_PROVIDER_BOTTOM_SHEET_AMOUNT" + const val PROVIDER_ITEM = "SELECT_SWAP_PROVIDER_BOTTOM_SHEET_PROVIDER_ITEM" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SendConfirmScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SendConfirmScreenTestTags.kt index f28a0cae01..61d9bf119f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/SendConfirmScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SendConfirmScreenTestTags.kt @@ -5,4 +5,6 @@ object SendConfirmScreenTestTags { const val RECIPIENT_ADDRESS = "SEND_CONFIRM_SCREEN_RECIPIENT_ADDRESS" const val BLOCKCHAIN_ADDRESS = "SEND_CONFIRM_SCREEN_BLOCKCHAIN_ADDRESS" const val RECIPIENT_ADDRESS_ICON = "SEND_CONFIRM_SCREEN_RECIPIENT_ADDRESS_ICON" + const val PROVIDER_NAME = "SEND_CONFIRM_SCREEN_PROVIDER_NAME" + const val BEST_RATE_BADGE = "SEND_CONFIRM_SCREEN_BEST_RATE_BADGE" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SendScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SendScreenTestTags.kt index 928a782a86..32ae4abd9f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/SendScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SendScreenTestTags.kt @@ -14,4 +14,8 @@ object SendScreenTestTags { const val MAX_BUTTON = "SEND_SCREEN_MAX_BUTTON" const val PREVIOUS_BUTTON = "SEND_SCREEN_PREVIOUS_BUTTON" + + const val SWAP_TO_ANOTHER_TOKEN_BUTTON = "SEND_SCREEN_SWAP_TO_ANOTHER_TOKEN_BUTTON" + const val SWAP_TOKEN_NAME = "SEND_SCREEN_SWAP_TOKEN_NAME" + const val CLOSE_CONVERT_ICON = "SEND_SCREEN_CLOSE_CONVERT_ICON" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt new file mode 100644 index 0000000000..f4e58b8056 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.test + +object WarningBottomSheetTestTags { + const val ICON = "BASE_WARNING_BOTTOM_SHEET_ICON" + const val TITLE = "BASE_WARNING_BOTTOM_SHEET_TITLE" + const val MESSAGE = "BASE_WARNING_BOTTOM_SHEET_MESSAGE" +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/ui/SendAmountContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/ui/SendAmountContent.kt index 87d931d616..d0a645e42f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/ui/SendAmountContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/ui/SendAmountContent.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -23,6 +24,7 @@ import com.tangem.core.ui.components.SpacerH 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.SendScreenTestTags import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountClickIntents import com.tangem.features.send.v2.subcomponents.amount.ui.preview.SendAmountClickIntentsStub @@ -65,7 +67,8 @@ private fun SendConvertTokenButton(onConvertToAnother: () -> Unit) { Row( modifier = Modifier .padding(12.dp) - .align(Alignment.Center), + .align(Alignment.Center) + .testTag(SendScreenTestTags.SWAP_TO_ANOTHER_TOKEN_BUTTON), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt index 69d9cb7e19..ff48cf706a 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -39,6 +40,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.SendScreenTestTags import com.tangem.domain.express.models.ExpressRateType import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM @@ -131,7 +133,9 @@ private fun SwapAmountBlockSeparator(onClick: () -> Unit, modifier: Modifier = M ), tint = TangemTheme.colors.icon.informative, contentDescription = null, - modifier = Modifier.size(16.dp), + modifier = Modifier + .size(16.dp) + .testTag(SendScreenTestTags.CLOSE_CONVERT_ICON), ) } } @@ -333,6 +337,7 @@ private fun SwapAmountInfoMain( style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.primary1, maxLines = 1, + modifier = Modifier.testTag(SendScreenTestTags.SWAP_TOKEN_NAME), ) } else { TextShimmer( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt index a853552187..3d3a01d22e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview @@ -40,6 +41,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.SendConfirmScreenTestTags import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.express.models.ExpressRateType @@ -171,7 +173,8 @@ private fun ProviderInfo( top.linkTo(parent.top) bottom.linkTo(parent.bottom) end.linkTo(iconRef.start, goneMargin = 12.dp) - }, + } + .testTag(SendConfirmScreenTestTags.PROVIDER_NAME), ) Icon( painter = rememberVectorPainter( @@ -287,7 +290,8 @@ private fun ConstraintLayoutScope.BestRateBadge( tint = TangemTheme.colors.icon.constant, modifier = Modifier .padding(iconHorizontalPaddings, iconVerticalPaddings) - .size(iconSize), + .size(iconSize) + .testTag(SendConfirmScreenTestTags.BEST_RATE_BADGE), ) AnimatedVisibility( visibleState = animateState, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt index 4b8df6d2b6..4dab153ea7 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -25,6 +26,7 @@ import com.tangem.core.ui.extensions.stringReference 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.SelectSwapProviderBottomSheetTestTags import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM @@ -45,7 +47,9 @@ internal fun SwapProviderItem(state: SwapProviderState, modifier: Modifier = Mod @Composable private fun ProviderContentState(state: SwapProviderState.Content, modifier: Modifier = Modifier) { Row( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .testTag(SelectSwapProviderBottomSheetTestTags.PROVIDER_ITEM), verticalAlignment = Alignment.CenterVertically, ) { SubcomposeAsyncImage( @@ -67,12 +71,15 @@ private fun ProviderContentState(state: SwapProviderState.Content, modifier: Mod text = state.name, style = TangemTheme.typography.caption1, color = TangemTheme.colors.text.primary1, + modifier = Modifier.testTag(SelectSwapProviderBottomSheetTestTags.PROVIDER_NAME), ) Text( text = state.type, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, - modifier = Modifier.padding(start = 4.dp), + modifier = Modifier + .padding(start = 4.dp) + .testTag(SelectSwapProviderBottomSheetTestTags.PROVIDER_TYPE), ) val badgeModifier = Modifier.padding(start = 4.dp) when (state.additionalBadge) { @@ -91,6 +98,7 @@ private fun ProviderContentState(state: SwapProviderState.Content, modifier: Mod color = TangemTheme.colors.text.tertiary, overflow = TextOverflow.Ellipsis, maxLines = 1, + modifier = Modifier.testTag(SelectSwapProviderBottomSheetTestTags.AMOUNT), ) if (state.diffPercent is SwapQuoteUM.Content.DifferencePercent.Diff) { @@ -143,7 +151,9 @@ private fun BestTradeItem(modifier: Modifier = Modifier) { text = stringResourceSafe(R.string.express_provider_best_rate), style = TangemTheme.typography.caption1, color = TangemTheme.colors.icon.accent, - modifier = Modifier.padding(horizontal = 6.dp), + modifier = Modifier + .padding(horizontal = 6.dp) + .testTag(SelectSwapProviderBottomSheetTestTags.BEST_RATE_LABEL), maxLines = 1, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt index 9a6c3072c5..d9b1884ed6 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +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 @@ -31,6 +32,7 @@ import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.ChooseNetworkBottomSheetTestTags import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseNetworkUM import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM @@ -116,15 +118,20 @@ private fun SwapChooseTokenNetworkContentList(swapNetworks: ImmutableList