Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-04 19:55:49 +03:00
parent 5c74278e3d
commit d9d8e9b500
7 changed files with 86 additions and 150 deletions

View file

@ -147,6 +147,7 @@ abstract class BaseTestCase : TestCase(
toggleStates = mapOf( toggleStates = mapOf(
"SWAP_REDESIGN_ENABLED" to false, "SWAP_REDESIGN_ENABLED" to false,
"ACCOUNTS_FEATURE_ENABLED" to true, "ACCOUNTS_FEATURE_ENABLED" to true,
"GASLESS_APPROVAL_ENABLED" to true,
) )
) )
} }

View file

@ -18,3 +18,12 @@ fun KNode.assertTextContainsSafe(
) )
} }
fun KNode.isDisplayedSafely(): Boolean {
return try {
assertIsDisplayed()
true
} catch (_: AssertionError) {
false
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.scenarios
import androidx.compose.ui.test.click import androidx.compose.ui.test.click
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.isDisplayedSafely
import com.tangem.screens.* import com.tangem.screens.*
import io.github.kakaocup.kakao.common.utilities.getResourceString import io.github.kakaocup.kakao.common.utilities.getResourceString
import io.qameta.allure.kotlin.Allure.step import io.qameta.allure.kotlin.Allure.step
@ -195,6 +196,51 @@ fun BaseTestCase.selectFeeType(feeType: FeeType, selectedFeeAmount: String) {
} }
} }
fun BaseTestCase.selectFeeTypeWithGasless(feeType: FeeType, selectedFeeAmount: String) {
step("Click on 'Select fee' icon") {
onSwapTokenScreen { selectFeeIcon.performClick() }
}
when (feeType) {
FeeType.Market -> selectMarketFee(selectedFeeAmount)
FeeType.Fast -> selectFastFee(selectedFeeAmount)
}
}
private fun BaseTestCase.selectMarketFee(selectedFeeAmount: String) {
step("Deselect current fee and select 'Market'") {
onSwapSelectNetworkFeeBottomSheet {
if (fastSelectorItem.isDisplayedSafely()) {
fastSelectorItem.performClick()
}
marketSelectorItem.performClick()
}
}
step("Click on 'Apply' button") {
onSwapSelectNetworkFeeBottomSheet { applyButton.performClick() }
}
step("Assert fee amount is equal to 'Market' fee:'$selectedFeeAmount'") {
onSwapTokenScreen { feeAmount.assertTextContains(selectedFeeAmount, substring = true) }
}
}
private fun BaseTestCase.selectFastFee(selectedFeeAmount: String) {
step("Deselect current fee and select 'Fast'") {
onSwapSelectNetworkFeeBottomSheet {
if (marketSelectorItem.isDisplayedSafely()) {
marketSelectorItem.performClick()
}
fastSelectorItem.performClick()
}
}
step("Click on 'Apply' button") {
onSwapSelectNetworkFeeBottomSheet { applyButton.performClick() }
}
step("Assert fee amount is equal to 'Fast' fee:'$selectedFeeAmount'") {
onSwapTokenScreen { feeAmount.assertTextContains(selectedFeeAmount, substring = true) }
}
}
fun BaseTestCase.chackUnableToCoverFeeNotification(networkName: String, currencySymbol: String) { fun BaseTestCase.chackUnableToCoverFeeNotification(networkName: String, currencySymbol: String) {
step("Assert 'Unable to cover '$networkName' fee notification title is displayed'") { step("Assert 'Unable to cover '$networkName' fee notification title is displayed'") {
onSwapTokenScreen { unableToCoverFeeNotificationTitle(networkName).assertIsDisplayed() } onSwapTokenScreen { unableToCoverFeeNotificationTitle(networkName).assertIsDisplayed() }
@ -207,6 +253,9 @@ fun BaseTestCase.chackUnableToCoverFeeNotification(networkName: String, currency
).assertIsDisplayed() ).assertIsDisplayed()
} }
} }
step("Assert 'Unable to cover '$networkName' fee notification icon is displayed'") {
onSwapTokenScreen { unableToCoverFeeNotificationIcon(networkName).assertIsDisplayed() }
}
} }
sealed class SwapEntryPoint { sealed class SwapEntryPoint {

View file

@ -2,17 +2,16 @@ package com.tangem.tests.actionButtons
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
import com.tangem.common.extensions.SwipeDirection
import com.tangem.common.extensions.assertIsDimmed import com.tangem.common.extensions.assertIsDimmed
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.swipeVertical
import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.scenarios.checkQrCodeBottomSheetScenario import com.tangem.scenarios.checkQrCodeBottomSheetScenario
import com.tangem.scenarios.goToQrCodeBottomSheet import com.tangem.scenarios.goToQrCodeBottomSheet
import com.tangem.scenarios.openMainScreen import com.tangem.scenarios.openMainScreen
import com.tangem.scenarios.synchronizeAddresses import com.tangem.scenarios.synchronizeAddresses
import com.tangem.screens.* import com.tangem.screens.onMainScreen
import com.tangem.screens.onSwapStoriesScreen
import com.tangem.screens.onSwapTokenScreen
import com.tangem.screens.onTokenDetailsScreen
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import io.qameta.allure.kotlin.AllureId import io.qameta.allure.kotlin.AllureId
import io.qameta.allure.kotlin.junit4.DisplayName import io.qameta.allure.kotlin.junit4.DisplayName
@ -124,97 +123,6 @@ class TokenDetailsScreenActionButtonsTest : BaseTestCase() {
} }
} }
@AllureId("4460")
@DisplayName("Action buttons (token details screen): check 'Swap' button (provider error)")
@Test
fun checkSwapButtonProviderErrorTest() {
val tokenTitle = "POL (ex-MATIC)"
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses()
}
step("Swipe up") {
swipeVertical(SwipeDirection.UP)
}
step("Click on token with name: '$tokenTitle'") {
waitForIdle()
onMainScreen { tokenWithTitleAndAddress(tokenTitle).performClick() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed() }
}
step("Click on 'Swap' button") {
onTokenDetailsScreen { swapButton().performClick() }
}
step("Assert swapping $tokenTitle is not supported dialog text is displayed") {
onSwapIsNotSupportedDialog { text(tokenTitle).assertIsDisplayed() }
}
step("Assert 'Ok' button is displayed") {
onSwapIsNotSupportedDialog { okButton.assertIsDisplayed() }
}
step("Click on 'Ok' button") {
onSwapIsNotSupportedDialog { okButton.performClick() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed() }
}
}
}
@AllureId("4461")
@DisplayName("Action buttons (token details screen): check 'Swap' button (Express error)")
@Test
fun checkSwapButtonExpressErrorTest() {
val tokenTitle = "Polygon"
val scenarioName = "express_api_assets"
val scenarioState = "Error"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName, scenarioState)
}
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses()
}
step("Swipe up") {
swipeVertical(SwipeDirection.UP)
}
step("Click on token with name: '$tokenTitle'") {
waitForIdle()
onMainScreen { tokenWithTitleAndAddress(tokenTitle).performClick() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed() }
}
step("Click on 'Swap' button") {
onTokenDetailsScreen { swapButton().performClick() }
}
step("Assert operation is unavailable dialog text is displayed") {
onOperationIsUnavailableDialog { text.assertIsDisplayed() }
}
step("Assert 'Ok' button is displayed") {
onOperationIsUnavailableDialog { okButton.assertIsDisplayed() }
}
step("Click on 'Ok' button") {
onOperationIsUnavailableDialog { okButton.performClick() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed() }
}
}
}
@AllureId("3590") @AllureId("3590")
@DisplayName("Action buttons (token details screen): validate UI") @DisplayName("Action buttons (token details screen): validate UI")
@Test @Test

View file

@ -86,12 +86,7 @@ class SwapChooseTokenScreenTest : BaseTestCase() {
onSwapChooseTokenScreen { tokenWithTitle(bitcoin).assertIsNotDisplayed() } onSwapChooseTokenScreen { tokenWithTitle(bitcoin).assertIsNotDisplayed() }
} }
step("Assert '$jesusCoin' is displayed and unavailable for swap") { step("Assert '$jesusCoin' is displayed and unavailable for swap") {
onSwapChooseTokenScreen { onSwapChooseTokenScreen { tokenWithTitle(tokenTitle = jesusCoin).assertIsDisplayed() }
tokenWithTitle(
tokenTitle = jesusCoin,
availableForSwap = false
).assertIsDisplayed()
}
} }
step("Assert custom token without backend id '$salam' is not displayed") { step("Assert custom token without backend id '$salam' is not displayed") {
onSwapChooseTokenScreen { tokenWithTitle(salam).assertIsNotDisplayed() } onSwapChooseTokenScreen { tokenWithTitle(salam).assertIsNotDisplayed() }

View file

@ -461,8 +461,6 @@ class SwapTokenScreenTest : BaseTestCase() {
val polygon = "Polygon" val polygon = "Polygon"
val bitcoin = "Bitcoin" val bitcoin = "Bitcoin"
val salam = "Salam" val salam = "Salam"
val jesusCoin = "Jesus Coin"
val myria = "Myria"
val scenarioState = "CustomTokenAndJesusAdded" val scenarioState = "CustomTokenAndJesusAdded"
setupHooks( setupHooks(
@ -505,30 +503,6 @@ class SwapTokenScreenTest : BaseTestCase() {
step("Assert 'Swap' button is dimmed") { step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed(true) } onTokenDetailsScreen { swapButton().assertIsDimmed(true) }
} }
step("Press 'Back' button") {
device.uiDevice.pressBack()
}
step("Swipe up") {
onMainScreen { tokenWithTitleAndAddress(jesusCoin).assertIsDisplayed() }
waitForIdle()
swipeVertical(SwipeDirection.UP)
}
step("Click on token with name: '$jesusCoin' in 'Ethereum' network. Swap unavailable") {
waitForIdle()
onMainScreen { tokenWithTitleAndAddress(jesusCoin).clickWithAssertion() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed(true) }
}
step("Press 'Back' button") {
device.uiDevice.pressBack()
}
step("Click on custom token with 'exchangeAvailable=true': '$myria'. Swap unavailable") {
onMainScreen { tokenWithTitleAndAddress(myria).clickWithAssertion() }
}
step("Assert 'Swap' button is dimmed") {
onTokenDetailsScreen { swapButton().assertIsDimmed(true) }
}
} }
} }
@ -592,10 +566,9 @@ class SwapTokenScreenTest : BaseTestCase() {
fun unableToCoverMarketAndFastFeeTest() { fun unableToCoverMarketAndFastFeeTest() {
val tokenName = "POL (ex-MATIC)" val tokenName = "POL (ex-MATIC)"
val inputAmount = "0.0001" val inputAmount = "0.0001"
val market = "Market" val marketFeeType = "Market"
val fast = "Fast" val fastFeeType = "Fast"
val marketFeeAmount = "$18,932" val feeAmount = "$"
val fastFeeAmount = "$24,139"
val scenarioName = "eth_network_balance" val scenarioName = "eth_network_balance"
val scenarioState = "LessThanDollar" val scenarioState = "LessThanDollar"
val networkName = "Ethereum" val networkName = "Ethereum"
@ -633,9 +606,9 @@ class SwapTokenScreenTest : BaseTestCase() {
textInput.performTextReplacement(inputAmount) textInput.performTextReplacement(inputAmount)
} }
} }
step("Select '$market' fee type") { step("Select '$marketFeeType' fee type") {
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
selectFeeType(FeeType.Market, selectedFeeAmount = marketFeeAmount) selectFeeType(feeType = FeeType.Market, feeAmount)
} }
} }
step("Check 'Unable to cover '$networkName' fee notification") { step("Check 'Unable to cover '$networkName' fee notification") {
@ -644,9 +617,9 @@ class SwapTokenScreenTest : BaseTestCase() {
step("Assert 'Swap' button is disabled") { step("Assert 'Swap' button is disabled") {
onSwapTokenScreen { swapButton.assertIsNotEnabled() } onSwapTokenScreen { swapButton.assertIsNotEnabled() }
} }
step("Select '$fast' fee type") { step("Select '$fastFeeType' fee type") {
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
selectFeeType(FeeType.Fast, selectedFeeAmount = fastFeeAmount) selectFeeType(feeType = FeeType.Fast, feeAmount)
} }
} }
step("Check 'Unable to cover '$networkName' fee notification") { step("Check 'Unable to cover '$networkName' fee notification") {
@ -658,9 +631,6 @@ class SwapTokenScreenTest : BaseTestCase() {
} }
} }
@ApiEnv(
ApiEnvConfig(ApiConfig.ID.Express, ApiEnvironment.PROD)
)
@AllureId("8537") @AllureId("8537")
@DisplayName("Swap: check switch fee type (unable to cover 'Fast' fee)") @DisplayName("Swap: check switch fee type (unable to cover 'Fast' fee)")
@Test @Test
@ -716,26 +686,25 @@ class SwapTokenScreenTest : BaseTestCase() {
} }
step("Select '$fastFeeType' fee type") { step("Select '$fastFeeType' fee type") {
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
selectFeeType(FeeType.Fast, selectedFeeAmount = fastFeeAmount) selectFeeTypeWithGasless(feeType = FeeType.Fast, fastFeeAmount)
} }
} }
step("Assert fee amount is equal to '$fastFeeType' fee:'$fastFeeAmount'") { step("Assert fee amount is equal to '$fastFeeType' fee:'$fastFeeAmount'") {
onSwapTokenScreen { feeAmount.assertTextContains(fastFeeAmount, substring = true) } onSwapTokenScreen { feeAmount.assertTextContains(fastFeeAmount, substring = true) }
} }
step("Check 'Unable to cover '$networkName' fee notification") { step("Check 'Unable to cover '$networkName' fee notification") {
chackUnableToCoverFeeNotification(networkName = networkName, currencySymbol = currencySymbol) flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
chackUnableToCoverFeeNotification(networkName = networkName, currencySymbol = currencySymbol)
}
} }
step("Assert 'Swap' button is disabled") { step("Assert 'Swap' button is disabled") {
onSwapTokenScreen { swapButton.assertIsNotEnabled() } onSwapTokenScreen { swapButton.assertIsNotEnabled() }
} }
step("Select '$marketFeeType' fee type") { step("Select '$marketFeeType' fee type") {
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
selectFeeType(FeeType.Market, selectedFeeAmount = marketFeeAmount) selectFeeTypeWithGasless(feeType = FeeType.Market, marketFeeAmount)
} }
} }
step("Assert fee amount is equal to '$marketFeeType' fee:'$marketFeeAmount'") {
onSwapTokenScreen { feeAmount.assertTextContains(marketFeeAmount, substring = true) }
}
step("Assert 'Swap' button is enabled") { step("Assert 'Swap' button is enabled") {
waitForIdle() waitForIdle()
onSwapTokenScreen { swapButton.assertIsEnabled() } onSwapTokenScreen { swapButton.assertIsEnabled() }

View file

@ -6,6 +6,7 @@ import com.tangem.common.annotations.ApiEnv
import com.tangem.common.annotations.ApiEnvConfig import com.tangem.common.annotations.ApiEnvConfig
import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO 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.USER_TOKENS_API_SCENARIO
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
import com.tangem.common.extensions.* import com.tangem.common.extensions.*
import com.tangem.common.utils.resetWireMockScenarioState import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.setWireMockScenarioState import com.tangem.common.utils.setWireMockScenarioState
@ -126,10 +127,9 @@ class SwapTokenScreenWarningsTest : BaseTestCase() {
} }
} }
step("Check 'Unable to cover '$networkName' fee notification") { step("Check 'Unable to cover '$networkName' fee notification") {
chackUnableToCoverFeeNotification(networkName = networkName, currencySymbol = currencySymbol) flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
} chackUnableToCoverFeeNotification(networkName = networkName, currencySymbol = currencySymbol)
step("Assert 'Unable to cover '$networkName' fee notification icon is displayed'") { }
onSwapTokenScreen { unableToCoverFeeNotificationIcon(networkName).assertIsDisplayed() }
} }
step("Assert 'Swap' button is disabled") { step("Assert 'Swap' button is disabled") {
onSwapTokenScreen { swapButton.assertIsNotEnabled() } onSwapTokenScreen { swapButton.assertIsNotEnabled() }
@ -202,7 +202,12 @@ class SwapTokenScreenWarningsTest : BaseTestCase() {
} }
} }
step("Assert fiat amount with warning is displayed") { step("Assert fiat amount with warning is displayed") {
onSwapTokenScreen { receiveFiatAmountWithPriceImpactWarning.assertTextContains("%", substring = true) } flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
onSwapTokenScreen {
waitForIdle()
receiveFiatAmountWithPriceImpactWarning.assertTextContains("%", substring = true)
}
}
} }
step("Assert receive amount information icon is displayed") { step("Assert receive amount information icon is displayed") {
onSwapTokenScreen { receiveFiatAmountInformationIcon.assertIsDisplayed() } onSwapTokenScreen { receiveFiatAmountInformationIcon.assertIsDisplayed() }