tangem-app-android-audited/app/src/androidTest/kotlin/com/tangem/scenarios/GaslessScenarios.kt
2026-07-08 12:19:32 +03:00

157 lines
No EOL
6.2 KiB
Kotlin

package com.tangem.scenarios
import com.tangem.common.BaseTestCase
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.utils.setWireMockScenarioState
import com.tangem.screens.*
import io.qameta.allure.kotlin.Allure.step
/**
* From the recipient step: fill the address and advance to the 'Send confirm' screen.
* Uses `composeTestRule.waitUntil` because `flakySafely` is unavailable in extensions on [BaseTestCase].
*/
fun BaseTestCase.enterRecipientAndOpenSendConfirm(recipientAddress: String) {
step("Type recipient address") {
onSendAddressScreen { addressTextField.performTextReplacement(recipientAddress) }
}
step("Click on 'Next' button until 'Send confirm' screen opens") {
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { openSendConfirmScreenViaNextButton() }.isSuccess
}
}
}
/** Enter the send amount, then fill the recipient and open the 'Send confirm' screen. */
fun BaseTestCase.enterAmountAndOpenSendConfirm(amount: String, recipientAddress: String) {
step("Type '$amount' in input text field") {
onSendScreen {
amountInputTextField.performClick()
amountInputTextField.performTextReplacement(amount)
}
}
step("Click on 'Next' button") {
onSendScreen { nextButton.clickWithAssertion() }
}
enterRecipientAndOpenSendConfirm(recipientAddress)
}
/**
* On the 'Send confirm' screen, open the network-fee selector and switch the fee token from the
* native coin to the given (stablecoin) token — the core gasless action repeated across the suite.
*/
fun BaseTestCase.selectStablecoinAsFeeToken(
coinName: String,
tokenName: String,
// Positive flows wait until 'Apply' is enabled (fee is computed & payable). Negative flows
// (insufficient balance) must NOT wait for that — 'Apply' stays disabled — so pass false and let
// the caller assert the disabled/error state itself.
expectApplyEnabled: Boolean = true,
) {
step("Click on 'Network fee' block") {
onSendConfirmScreen {
feeSelectorBlock.assertIsDisplayed()
feeSelectorBlock.performClick()
}
}
step("Click on '$coinName' fee token to open 'Choose token'") {
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { onSendFeeSelectorBottomSheet { feeTokenItem(coinName).performClick() } }.isSuccess
}
}
step("Select '$tokenName' as the fee-paying token") {
onSendFeeSelectorBottomSheet { feeTokenItem(tokenName).performClick() }
}
step("Wait until the '$tokenName' fee is loaded") {
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching {
onSendFeeSelectorBottomSheet {
networkFeeTitle.assertIsDisplayed()
feeTokenItem(tokenName).assertIsDisplayed()
if (expectApplyEnabled) applyButton.assertIsEnabled()
}
}.isSuccess
}
}
}
/**
* Open an existing hot wallet (gasless signing needs a hot wallet, not the mock card), set the
* portfolio and quotes mocks, and reach the send amount input for the given token.
*/
fun BaseTestCase.openGaslessSendScreenWithHotWallet(
seedPhrase: String,
tokenName: String,
userTokensState: String,
quotesState: String,
) {
step("Set WireMock scenario '$USER_TOKENS_API_SCENARIO' to '$userTokensState'") {
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
}
step("Set WireMock scenario '$QUOTES_API_SCENARIO' to '$quotesState'") {
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = quotesState)
}
step("Open 'Main' screen with existing hot wallet") {
openMainScreenWithExistingHotWallet(seedPhrase)
}
step("Click on token with name: '$tokenName'") {
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
}
step("Click on 'Transfer' button") {
onTokenDetailsScreen { transferButton.clickWithAssertion() }
}
step("Click on 'Send' button in bottom sheet") {
onTransferBottomSheet { sendButton.clickWithAssertion() }
}
}
/**
* Open an existing hot wallet, select the token to send and choose the swap target token/network —
* the shared entry into the gasless send-via-swap flow. Scenario states stay in the test body.
*/
fun BaseTestCase.openSendViaSwapScreenWithHotWallet(
seedPhrase: String,
tokenName: String,
swapTokenName: String,
networkName: String,
networkType: String? = null,
) {
step("Open 'Main' screen with existing hot wallet") {
openMainScreenWithExistingHotWallet(seedPhrase)
}
step("Click on token with name: '$tokenName'") {
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
}
step("Select '$swapTokenName' as the token to receive via swap") {
selectTokenToSendViaSwap(
swapTokenName = swapTokenName,
networkName = networkName,
networkType = networkType,
)
}
}
/**
* Send-via-swap amount entry: type the amount, advance past the quote-gated 'Next' button (waiting
* until it becomes enabled once the swap quote loads), then fill the recipient and open the
* 'Send confirm' screen. Uses `composeTestRule.waitUntil` because `flakySafely` is unavailable in
* extensions on [BaseTestCase].
*/
fun BaseTestCase.enterSwapAmountAndOpenSendConfirm(amount: String, recipientAddress: String) {
step("Type amount '$amount' in input field") {
onSendScreen { amountInputTextField.performTextReplacement(amount) }
}
step("Click on 'Next' button") {
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching {
onSendScreen {
nextButton.assertIsEnabled()
nextButton.performClick()
}
}.isSuccess
}
}
enterRecipientAndOpenSendConfirm(recipientAddress)
}