Updated on 2026-08-14
This commit is contained in:
commit
52d1ed4312
5 changed files with 304 additions and 0 deletions
|
|
@ -7,9 +7,13 @@ 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
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.extractText
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.core.ui.R
|
||||
import com.kaspersky.kaspresso.testcases.core.testcontext.TestContext
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
fun BaseTestCase.openSendScreen(
|
||||
|
|
@ -248,6 +252,90 @@ fun BaseTestCase.checkSendViaSwapSuccessScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
/** From the token details screen, open the transfer bottom sheet and reach the send amount input. */
|
||||
fun BaseTestCase.openSendFromTokenDetails() {
|
||||
step("Click on 'Transfer' button") {
|
||||
onTokenDetailsScreen { transferButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Send' button in bottom sheet") {
|
||||
onTransferBottomSheet { sendButton.clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
|
||||
/** Open an existing hot wallet and reach the send amount input for [tokenName]. */
|
||||
fun BaseTestCase.openSendScreenWithHotWallet(seedPhrase: String, tokenName: String) {
|
||||
step("Open 'Main' screen with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase)
|
||||
}
|
||||
step("Click on token with name: '$tokenName'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
|
||||
}
|
||||
openSendFromTokenDetails()
|
||||
}
|
||||
|
||||
fun BaseTestCase.getNetworkFeeAmount(): String {
|
||||
var fee = ""
|
||||
step("Read current network fee amount") {
|
||||
onSendConfirmScreen { fee = feeAmount.extractText() }
|
||||
}
|
||||
return fee
|
||||
}
|
||||
|
||||
fun BaseTestCase.switchFeeToFastAndApply() {
|
||||
val fastOption = getResourceString(R.string.common_fee_selector_option_fast)
|
||||
step("Click on fee selector icon") {
|
||||
onSendConfirmScreen { feeSelectorIcon.performClick() }
|
||||
}
|
||||
// Selecting a non-custom speed auto-applies and closes the fee selector — no 'Done' step.
|
||||
step("Click on '$fastOption' fee option") {
|
||||
onSendSelectNetworkFeeBottomSheet { regularFeeSelectorItem(fastOption).performClick() }
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.assertNetworkFeeChanged(previousFee: String) {
|
||||
step("Assert network fee changed from '$previousFee'") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
runCatching {
|
||||
var current = previousFee
|
||||
onSendConfirmScreen { current = feeAmount.extractText() }
|
||||
current != previousFee
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads the network fee amount on the 'Send confirm' screen (empty while it shows a loading shimmer). */
|
||||
fun BaseTestCase.readNetworkFeeAmount(): String {
|
||||
var fee = ""
|
||||
onSendConfirmScreen { fee = feeAmount.extractText() }
|
||||
return fee
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the network fee value stops changing across two checks — the send button stays disabled
|
||||
* (and the hold-to-confirm gesture is swallowed) until the fee re-fetch settles. The hold button has
|
||||
* no enabled/disabled semantics, so waiting on the fee value is the only reliable readiness signal.
|
||||
*/
|
||||
fun TestContext<Unit>.waitUntilNetworkFeeIsStable(readFee: () -> String) {
|
||||
step("Wait for the network fee to finish loading") {
|
||||
var previousFee: String? = null
|
||||
flakySafely(timeoutMs = WAIT_UNTIL_TIMEOUT_LONG, intervalMs = FEE_STABILITY_INTERVAL_MS) {
|
||||
val currentFee = readFee()
|
||||
val isStable = currentFee.isNotEmpty() && currentFee == previousFee
|
||||
previousFee = currentFee
|
||||
if (!isStable) throw AssertionError("Network fee is still settling (current='$currentFee')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val FEE_STABILITY_INTERVAL_MS = 750L
|
||||
|
||||
fun BaseTestCase.assertNetworkFeeContains(currencySymbol: String) {
|
||||
step("Assert network fee contains '$currencySymbol'") {
|
||||
onSendConfirmScreen { feeAmount.assertTextContains(currencySymbol, substring = true) }
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.selectTokenToSendViaSwap(
|
||||
swapTokenName: String,
|
||||
networkName: String,
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ package com.tangem.tests.actionButtons
|
|||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.checkQrCodeBottomSheetScenario
|
||||
import com.tangem.scenarios.goToQrCodeBottomSheet
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.openSendFromTokenDetails
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.onAddFundsBottomSheet
|
||||
import com.tangem.screens.onMainScreen
|
||||
import com.tangem.screens.onSendScreen
|
||||
import com.tangem.screens.onSwapStoriesScreen
|
||||
import com.tangem.screens.onSwapTokenScreen
|
||||
import com.tangem.screens.onTokenDetailsScreen
|
||||
|
|
@ -199,4 +203,58 @@ class TokenDetailsScreenActionButtonsTest : BaseTestCase() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("591")
|
||||
@DisplayName("Action buttons (token details screen): send available for funded token, unavailable for empty token")
|
||||
@Test
|
||||
fun checkSendAvailabilityForFundedAndEmptyTokenTest() {
|
||||
val emptyTokenTitle = "Polygon"
|
||||
val fundedTokenTitle = "Ethereum"
|
||||
val polygonBalanceScenarioName = "polygon_coin_balance"
|
||||
val polygonBalanceScenarioState = "ZeroBalance"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(polygonBalanceScenarioName)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$polygonBalanceScenarioName' to state: '$polygonBalanceScenarioState'") {
|
||||
setWireMockScenarioState(polygonBalanceScenarioName, polygonBalanceScenarioState)
|
||||
}
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on token with name: '$emptyTokenTitle'") {
|
||||
waitForIdle()
|
||||
onMainScreen { tokenWithTitleAndAddress(emptyTokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Transfer' button is not displayed for the empty token") {
|
||||
onTokenDetailsScreen { transferButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Go back to 'Main Screen'") {
|
||||
device.uiDevice.pressBack()
|
||||
}
|
||||
step("Assert 'Main Screen' is displayed") {
|
||||
onMainScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on token with name: '$fundedTokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(fundedTokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Transfer' button is displayed for the funded token") {
|
||||
onTokenDetailsScreen { transferButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Open the send flow from token details") {
|
||||
openSendFromTokenDetails()
|
||||
}
|
||||
step("Assert 'Send' screen is displayed") {
|
||||
onSendScreen { amountInputTextField.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.BaseTestCase
|
|||
import com.tangem.common.constants.TestConstants.ETHEREUM_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.POLKADOT_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO
|
||||
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
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
|
|
@ -326,4 +327,42 @@ class SendConfirmScreenTest : BaseTestCase() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("557")
|
||||
@DisplayName("Send (Confirm screen): send a second transaction while the first is still pending")
|
||||
@Test
|
||||
fun sendSecondTransactionWhileFirstActiveTest() {
|
||||
val tokenName = "Ethereum"
|
||||
val inputAmount = "0.001"
|
||||
|
||||
setupHooks().run {
|
||||
step("Open the send flow for '$tokenName' on an existing hot wallet") {
|
||||
openSendScreenWithHotWallet(seedPhrase = SVS_SEED_PHRASE_12, tokenName = tokenName)
|
||||
}
|
||||
step("Enter amount '$inputAmount' and open the 'Send confirm' screen") {
|
||||
enterAmountAndOpenSendConfirm(amount = inputAmount, recipientAddress = ETHEREUM_RECIPIENT_ADDRESS)
|
||||
}
|
||||
// Hold-to-confirm is swallowed while the fee is still settling — wait for it to load first.
|
||||
waitUntilNetworkFeeIsStable { readNetworkFeeAmount() }
|
||||
step("Sign, send and open the 'Transaction sent' screen") {
|
||||
openSendSuccessScreenViaLongClickOnSendButton()
|
||||
}
|
||||
step("Click on 'Close' button") {
|
||||
onSendSuccessScreen { closeButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Open the send flow again from token details") {
|
||||
openSendFromTokenDetails()
|
||||
}
|
||||
step("Enter amount '$inputAmount' and open the 'Send confirm' screen") {
|
||||
enterAmountAndOpenSendConfirm(amount = inputAmount, recipientAddress = ETHEREUM_RECIPIENT_ADDRESS)
|
||||
}
|
||||
waitUntilNetworkFeeIsStable { readNetworkFeeAmount() }
|
||||
step("Sign, send and open the 'Transaction sent' screen") {
|
||||
openSendSuccessScreenViaLongClickOnSendButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.constants.TestConstants.BITCOIN_RECIPIENT_ADDRESS
|
|||
import com.tangem.common.constants.TestConstants.ETHEREUM_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.POLKADOT_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO
|
||||
import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12
|
||||
import com.tangem.common.constants.TestConstants.TERRA_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
|
|
@ -443,4 +444,30 @@ class SendFeeScreenTest : BaseTestCase() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("547")
|
||||
@DisplayName("Send (Fee screen): network fee recalculates on speed switch and sends")
|
||||
@Test
|
||||
fun recalculateFeeOnSpeedSwitchAndSendTest() {
|
||||
val tokenName = "Ethereum"
|
||||
val inputAmount = "0.8"
|
||||
|
||||
setupHooks().run {
|
||||
step("Open the send flow for '$tokenName' on an existing hot wallet") {
|
||||
openSendScreenWithHotWallet(seedPhrase = SVS_SEED_PHRASE_12, tokenName = tokenName)
|
||||
}
|
||||
step("Enter amount '$inputAmount' and open the 'Send confirm' screen") {
|
||||
enterAmountAndOpenSendConfirm(amount = inputAmount, recipientAddress = ETHEREUM_RECIPIENT_ADDRESS)
|
||||
}
|
||||
waitUntilNetworkFeeIsStable { readNetworkFeeAmount() }
|
||||
val marketFee = getNetworkFeeAmount()
|
||||
step("Switch the network fee to 'Fast'") {
|
||||
switchFeeToFastAndApply()
|
||||
}
|
||||
assertNetworkFeeChanged(marketFee)
|
||||
step("Sign, send and open the 'Transaction sent' screen") {
|
||||
openSendSuccessScreenViaLongClickOnSendButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.tests.send.feeScreen
|
||||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.ETHEREUM_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.QUOTES_API_SCENARIO
|
||||
import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12
|
||||
import com.tangem.common.constants.TestConstants.TERRA_RECIPIENT_ADDRESS
|
||||
import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.*
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Completing a send paid with a fee in the token itself (no native fee coin), on a hot wallet:
|
||||
* VeChain's VeThor and Terra Classic's TerraClassicUSD.
|
||||
*/
|
||||
@HiltAndroidTest
|
||||
class SendTokenFeeTest : BaseTestCase() {
|
||||
|
||||
private val tokenAmount = "1"
|
||||
|
||||
@AllureId("4907")
|
||||
@DisplayName("Send (Fee in token): send VeThor and complete the transaction")
|
||||
@Test
|
||||
fun sendVeThorWithFeeInTokenTest() {
|
||||
val tokenName = "VeThor"
|
||||
val scenarioState = "Vechain"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario '$USER_TOKENS_API_SCENARIO' to '$scenarioState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = scenarioState)
|
||||
}
|
||||
step("Set WireMock scenario '$QUOTES_API_SCENARIO' to '$scenarioState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = scenarioState)
|
||||
}
|
||||
step("Open the send flow for '$tokenName' on an existing hot wallet") {
|
||||
openSendScreenWithHotWallet(seedPhrase = SVS_SEED_PHRASE_12, tokenName = tokenName)
|
||||
}
|
||||
step("Enter amount '$tokenAmount' and open the 'Send confirm' screen") {
|
||||
enterAmountAndOpenSendConfirm(amount = tokenAmount, recipientAddress = ETHEREUM_RECIPIENT_ADDRESS)
|
||||
}
|
||||
waitUntilNetworkFeeIsStable { readNetworkFeeAmount() }
|
||||
assertNetworkFeeContains("\$")
|
||||
step("Sign, send and open the 'Transaction sent' screen") {
|
||||
openSendSuccessScreenViaLongClickOnSendButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4908")
|
||||
@DisplayName("Send (Fee in token): send TerraClassicUSD and complete the transaction")
|
||||
@Test
|
||||
fun sendTerraClassicUsdWithFeeInTokenTest() {
|
||||
val tokenName = "TerraClassicUSD"
|
||||
val scenarioState = "Terra"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario '$USER_TOKENS_API_SCENARIO' to '$scenarioState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = scenarioState)
|
||||
}
|
||||
step("Set WireMock scenario '$QUOTES_API_SCENARIO' to '$scenarioState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = scenarioState)
|
||||
}
|
||||
step("Open the send flow for '$tokenName' on an existing hot wallet") {
|
||||
openSendScreenWithHotWallet(seedPhrase = SVS_SEED_PHRASE_12, tokenName = tokenName)
|
||||
}
|
||||
step("Enter amount '$tokenAmount' and open the 'Send confirm' screen") {
|
||||
enterAmountAndOpenSendConfirm(amount = tokenAmount, recipientAddress = TERRA_RECIPIENT_ADDRESS)
|
||||
}
|
||||
waitUntilNetworkFeeIsStable { readNetworkFeeAmount() }
|
||||
assertNetworkFeeContains("\$")
|
||||
step("Sign, send and open the 'Transaction sent' screen") {
|
||||
openSendSuccessScreenViaLongClickOnSendButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue