Updated on 2026-08-14
This commit is contained in:
parent
3472f48de7
commit
9b6ed88d78
8 changed files with 581 additions and 1 deletions
|
|
@ -0,0 +1,156 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import androidx.compose.ui.semantics.SemanticsActions
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.hasAnyDescendant
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performSemanticsAction
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.compose.ui.test.swipeLeft
|
||||
import androidx.compose.ui.test.swipeRight
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.isDisplayedSafely
|
||||
import com.tangem.core.ui.test.BuyTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.DetailsScreenTestTags
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
// The 'Add Wallet' button sits in a nested LazyColumn and rejects touch injection, so invoke OnClick directly.
|
||||
private fun BaseTestCase.clickViaSemantics(matcher: SemanticsMatcher, useUnmergedTree: Boolean = false) {
|
||||
composeTestRule.onNode(matcher, useUnmergedTree).performSemanticsAction(SemanticsActions.OnClick)
|
||||
}
|
||||
|
||||
// Both pager pages stay mounted; the off-screen one ignores swipes, so always swipe the on-screen wallet card.
|
||||
private fun BaseTestCase.swipeDisplayedWallet(toPrevious: Boolean) {
|
||||
val nodes = composeTestRule.onAllNodes(hasTestTag(MainScreenTestTags.WALLET_LIST_ITEM))
|
||||
for (i in 0 until nodes.fetchSemanticsNodes().size) {
|
||||
val swiped = runCatching {
|
||||
nodes[i].assertIsDisplayed()
|
||||
nodes[i].performTouchInput { if (toPrevious) swipeRight() else swipeLeft() }
|
||||
}.isSuccess
|
||||
if (swiped) break
|
||||
}
|
||||
waitForIdle()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a second hardware (card) wallet from a wallet-restricted state (a hot wallet already exists):
|
||||
* 'Add Wallet' scans a card immediately — there is no wallet-type chooser — so the scan mock must be
|
||||
* set before the click. After saving, the app returns to Main; the added card lands as an off-screen
|
||||
* pager page, so this swipes to it and syncs its missing addresses before returning.
|
||||
*/
|
||||
fun BaseTestCase.addNewCardWallet(mockContent: MockContent) {
|
||||
step("Click 'More' button on TopBar") {
|
||||
onMainScreenTopBar { moreButton.clickWithAssertion() }
|
||||
}
|
||||
MockProvider.setMocks(mockContent)
|
||||
step("Click on 'Add Wallet' button (scans a new hardware wallet)") {
|
||||
clickViaSemantics(hasTestTag(DetailsScreenTestTags.ADD_WALLET_BUTTON))
|
||||
}
|
||||
// Details pops back to Main only after the freshly scanned wallet finishes its initial load over many (some failing) RPCs.
|
||||
// Gate on the top-bar More button, not the screen container — the bottom Markets sheet can leave the container un-"displayed".
|
||||
step("Assert 'Main' screen is displayed with the new wallet") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_VERY_LONG) {
|
||||
runCatching { onMainScreenTopBar { moreButton.assertIsDisplayed() } }.isSuccess
|
||||
}
|
||||
}
|
||||
// The added card is the newest pager page; its "Synchronize addresses" prompt is off-screen until swiped to.
|
||||
step("Synchronize the new card wallet's addresses") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
var shown = false
|
||||
onMainScreen { shown = synchronizeAddressesButton.isDisplayedSafely() }
|
||||
if (!shown) swipeDisplayedWallet(toPrevious = false)
|
||||
shown
|
||||
}
|
||||
// Let the pager fling settle before clicking — the wait above releases as soon as the prompt has bounds, and a
|
||||
// click landing mid-animation is eaten by the button's clickableSingle debounce, so the prompt never clears.
|
||||
waitForIdle()
|
||||
onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
|
||||
// The prompt clears once the card's addresses are derived (re-scan + reload over many, some failing, RPCs).
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_VERY_LONG) {
|
||||
var generated = false
|
||||
onMainScreen { generated = !synchronizeAddressesButton.isDisplayedSafely() }
|
||||
generated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Taps the on-screen token row by [tokenName]; the wallet pager keeps both pages mounted, so click the displayed copy. */
|
||||
fun BaseTestCase.clickDisplayedTokenOnMain(tokenName: String) {
|
||||
val matcher = hasTestTag(MainScreenTestTags.TOKEN_LIST_ITEM) and hasAnyDescendant(hasText(tokenName))
|
||||
step("Click on token '$tokenName' on the visible wallet") {
|
||||
// Both pager pages mount the same token; click the one currently on-screen once the swipe has settled.
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
val nodes = composeTestRule.onAllNodes(matcher, useUnmergedTree = true)
|
||||
(0 until nodes.fetchSemanticsNodes().size).any { i ->
|
||||
runCatching {
|
||||
nodes[i].assertIsDisplayed()
|
||||
nodes[i].performClick()
|
||||
}.isSuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Swipes the wallet card to the previous wallet (the one added before the current page). */
|
||||
fun BaseTestCase.switchToPreviousWallet() {
|
||||
step("Swipe wallet card to the previous wallet") {
|
||||
swipeDisplayedWallet(toPrevious = true)
|
||||
}
|
||||
}
|
||||
|
||||
/** Opens the receive selector and picks identical [token] located on a different wallet [walletName] via its wallet tab. */
|
||||
fun BaseTestCase.selectReceiveTokenOnWallet(token: String, walletName: String) {
|
||||
step("Click on 'Choose token' button") {
|
||||
onSwapTokenScreen { chooseTokenButton.performClick() }
|
||||
}
|
||||
// The wallet tab is a custom clickable Row that Kakao's performClick can't hit (autoscroll fails); invoke OnClick directly.
|
||||
step("Select wallet tab '$walletName'") {
|
||||
clickViaSemantics(
|
||||
hasTestTag(BuyTokenScreenTestTags.WALLET_TAB) and hasAnyDescendant(hasText(walletName)),
|
||||
useUnmergedTree = true,
|
||||
)
|
||||
}
|
||||
step("Click on token with name '$token'") {
|
||||
clickViaSemantics(
|
||||
hasTestTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM) and hasAnyDescendant(hasText(token)),
|
||||
useUnmergedTree = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Searches the receive selector for [token], adds it to recipient wallet [recipientWalletName] via the market result. */
|
||||
fun BaseTestCase.addMissingReceiveTokenToWallet(token: String, recipientWalletName: String) {
|
||||
step("Click on 'Choose token' button") {
|
||||
onSwapTokenScreen { chooseTokenButton.performClick() }
|
||||
}
|
||||
step("Type '$token' in search field") {
|
||||
onSwapSelectTokenScreen {
|
||||
searchBarBlock.performClick()
|
||||
searchBarBlock.performTextInput(token)
|
||||
}
|
||||
}
|
||||
step("Click on market token '$token'") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
runCatching { onSwapSelectTokenScreen { marketsTokenWithName(token).performClick() } }.isSuccess
|
||||
}
|
||||
}
|
||||
// The 'Add token' sheet pre-selects the recipient (the only wallet missing the token, since the source already holds it).
|
||||
step("Assert recipient wallet '$recipientWalletName' is selected") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
runCatching { onAddToPortfolioScreen { walletName(recipientWalletName).assertIsDisplayed() } }.isSuccess
|
||||
}
|
||||
}
|
||||
step("Click on 'Add' button") {
|
||||
onAddToPortfolioScreen { addButton.performClick() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import androidx.compose.ui.test.hasClickAction
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.wallet.R
|
||||
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 AddToPortfolioPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<AddToPortfolioPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
fun walletName(walletName: String): KNode = child {
|
||||
hasText(walletName)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val addButton: KNode = child {
|
||||
hasText(getResourceString(R.string.common_add))
|
||||
hasClickAction()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onAddToPortfolioScreen(function: AddToPortfolioPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -5,14 +5,18 @@ import com.tangem.common.extensions.clickWithAssertion
|
|||
import com.tangem.common.extensions.extractText
|
||||
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.TANGEM_PAY_ELIGIBILITY_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.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.screens.tangempay.*
|
||||
import com.tangem.tap.domain.sdk.mocks.content.Wallet2WithDerivationsMockContent
|
||||
import com.tangem.tap.domain.sdk.mocks.content.WalletMockContent
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
|
@ -808,6 +812,334 @@ class AppTransfersTest : BaseTestCase() {
|
|||
}
|
||||
}
|
||||
|
||||
@AllureId("10005")
|
||||
@DisplayName("App transfers: Tron network fee")
|
||||
@Test
|
||||
fun tronNetworkFeeTest() {
|
||||
val token = "Tron"
|
||||
val amount = "0.001"
|
||||
val userTokensState = "TwoAccountsSameTron"
|
||||
val networksProvidersScenario = "networks_providers"
|
||||
val appTransfersNetworksState = "AppTransfersNetworks"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = {
|
||||
setWireMockScenarioState(storiesScenario, storiesErrorState)
|
||||
// networks_providers configures SDK RPC hosts at launch — must be set before the activity starts.
|
||||
setWireMockScenarioState(networksProvidersScenario, appTransfersNetworksState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(networksProvidersScenario)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
// Non-zero prices keep total fiat > 0 so the empty-wallet banner doesn't push the account list under the Markets sheet.
|
||||
step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$appTransfersNetworksState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = appTransfersNetworksState)
|
||||
}
|
||||
|
||||
step("Open Swap in Transfer mode for '$token'") { openSwapInTransferMode(token) }
|
||||
step("Enter amount '$amount'") { inputAmount(amount) }
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
step("Assert network fee is displayed") { waitForFeeDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
// [REDACTED_TASK_KEY]: blockchain SDK TonProvidersBuilder drops public providers, so TON has no provider in the mocked build.
|
||||
@Ignore("[REDACTED_JIRA]")
|
||||
@AllureId("10012")
|
||||
@DisplayName("App transfers: TON network fee")
|
||||
@Test
|
||||
fun tonNetworkFeeTest() {
|
||||
// The SDK names TON's coin "Gram" (Blockchain.TON.getCoinName), so the portfolio row shows "Gram", not "Toncoin".
|
||||
val token = "Gram"
|
||||
val amount = "0.001"
|
||||
val userTokensState = "TwoAccountsSameTON"
|
||||
val networksProvidersScenario = "networks_providers"
|
||||
val appTransfersNetworksState = "AppTransfersNetworks"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = {
|
||||
setWireMockScenarioState(storiesScenario, storiesErrorState)
|
||||
// networks_providers configures SDK RPC hosts at launch — must be set before the activity starts.
|
||||
setWireMockScenarioState(networksProvidersScenario, appTransfersNetworksState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(networksProvidersScenario)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
// Non-zero prices keep total fiat > 0 so the empty-wallet banner doesn't push the account list under the Markets sheet.
|
||||
step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$appTransfersNetworksState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = appTransfersNetworksState)
|
||||
}
|
||||
|
||||
step("Open Swap in Transfer mode for '$token'") { openSwapInTransferMode(token) }
|
||||
step("Enter amount '$amount'") { inputAmount(amount) }
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
step("Assert network fee is displayed") { waitForFeeDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("10013")
|
||||
@DisplayName("App transfers: Cosmos network fee")
|
||||
@Test
|
||||
fun cosmosNetworkFeeTest() {
|
||||
val token = "Cosmos"
|
||||
val amount = "0.001"
|
||||
val userTokensState = "TwoAccountsSameCosmos"
|
||||
val networksProvidersScenario = "networks_providers"
|
||||
val appTransfersNetworksState = "AppTransfersNetworks"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = {
|
||||
setWireMockScenarioState(storiesScenario, storiesErrorState)
|
||||
// networks_providers configures SDK RPC hosts at launch — must be set before the activity starts.
|
||||
setWireMockScenarioState(networksProvidersScenario, appTransfersNetworksState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(networksProvidersScenario)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
// Non-zero prices keep total fiat > 0 so the empty-wallet banner doesn't push the account list under the Markets sheet.
|
||||
step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$appTransfersNetworksState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = appTransfersNetworksState)
|
||||
}
|
||||
|
||||
step("Open Swap in Transfer mode for '$token'") { openSwapInTransferMode(token) }
|
||||
step("Enter amount '$amount'") { inputAmount(amount) }
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
step("Assert network fee is displayed") { waitForFeeDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("10015")
|
||||
@DisplayName("App transfers: Aptos network fee")
|
||||
@Test
|
||||
fun aptosNetworkFeeTest() {
|
||||
val token = "Aptos"
|
||||
val amount = "0.001"
|
||||
val userTokensState = "TwoAccountsSameAptos"
|
||||
val networksProvidersScenario = "networks_providers"
|
||||
val appTransfersNetworksState = "AppTransfersNetworks"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = {
|
||||
setWireMockScenarioState(storiesScenario, storiesErrorState)
|
||||
// networks_providers configures SDK RPC hosts at launch — must be set before the activity starts.
|
||||
setWireMockScenarioState(networksProvidersScenario, appTransfersNetworksState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(networksProvidersScenario)
|
||||
resetWireMockScenarioState(QUOTES_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
// Non-zero prices keep total fiat > 0 so the empty-wallet banner doesn't push the account list under the Markets sheet.
|
||||
step("Set WireMock scenario: '$QUOTES_API_SCENARIO' to state: '$appTransfersNetworksState'") {
|
||||
setWireMockScenarioState(scenarioName = QUOTES_API_SCENARIO, state = appTransfersNetworksState)
|
||||
}
|
||||
|
||||
step("Open Swap in Transfer mode for '$token'") { openSwapInTransferMode(token) }
|
||||
step("Enter amount '$amount'") { inputAmount(amount) }
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
step("Assert network fee is displayed") { waitForFeeDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("9856")
|
||||
@DisplayName("App transfers: Transfer mode is available from a Tangem Pay account")
|
||||
@Test
|
||||
fun transferModeAvailableFromTangemPayAccountTest() {
|
||||
val token = "USDC"
|
||||
val receiveAccountName = "Main account"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
val balanceScenario = "tangem_pay_balance_update"
|
||||
val balanceInitialState = "InitialBalance"
|
||||
val historyScenario = "tangem_pay_transaction_history"
|
||||
val historyInitialState = "InitialEmpty"
|
||||
val userTokensState = "TangemPayTransferUsdc"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = { setWireMockScenarioState(storiesScenario, storiesErrorState) },
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(balanceScenario)
|
||||
resetWireMockScenarioState(historyScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to state: '$eligibilityState'") {
|
||||
setWireMockScenarioState(scenarioName = TANGEM_PAY_ELIGIBILITY_SCENARIO, state = eligibilityState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balanceScenario' to state: '$balanceInitialState'") {
|
||||
setWireMockScenarioState(scenarioName = balanceScenario, state = balanceInitialState)
|
||||
}
|
||||
step("Set WireMock scenario: '$historyScenario' to state: '$historyInitialState'") {
|
||||
setWireMockScenarioState(scenarioName = historyScenario, state = historyInitialState)
|
||||
}
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
|
||||
step("Open Tangem Pay") { openTangemPay() }
|
||||
step("Click on 'Withdraw' button") {
|
||||
onTangemPayMainScreen { withdrawButton.clickWithAssertion() }
|
||||
}
|
||||
step("Acknowledge withdrawal note sheet") {
|
||||
onTangemPayWithdrawNoteSheet {
|
||||
title.assertIsDisplayed()
|
||||
gotItButton.clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Choose identical receive token '$token' from '$receiveAccountName'") {
|
||||
chooseIdenticalReceiveToken(tokenName = token, receiveAccountName = receiveAccountName)
|
||||
}
|
||||
// Assert the top-bar 'Transfer' title (unambiguous transfer-mode signal) + no provider block; the
|
||||
// withdraw-entry swap keeps recalculating, so use flakySafely rather than assertTransferReady's waitUntil.
|
||||
step("Assert Transfer mode is ready") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_VERY_LONG) {
|
||||
onSwapTokenScreen { transferTitle.assertIsDisplayed() }
|
||||
}
|
||||
onSwapTokenScreen { providersBlock.assertIsNotDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("9995")
|
||||
@DisplayName("App transfers: transfer between different wallets reaches 'Transfer in progress' screen")
|
||||
@Test
|
||||
fun transferBetweenDifferentWalletsReachesFinishTest() {
|
||||
val token = "Ethereum"
|
||||
val amount = "0.001"
|
||||
val secondWalletName = "Wallet 2"
|
||||
val userTokensState = "EthereumWithSecondToken"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = { setWireMockScenarioState(storiesScenario, storiesErrorState) },
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(ethCallScenario)
|
||||
resetWireMockScenarioState(ethBalanceScenario)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$userTokensState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = userTokensState)
|
||||
}
|
||||
step("Set WireMock scenario: '$ethCallScenario' to state: '$started'") {
|
||||
setWireMockScenarioState(scenarioName = ethCallScenario, state = started)
|
||||
}
|
||||
step("Set WireMock scenario: '$ethBalanceScenario' to state: '$started'") {
|
||||
setWireMockScenarioState(scenarioName = ethBalanceScenario, state = started)
|
||||
}
|
||||
|
||||
step("Open 'Main' screen with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12)
|
||||
}
|
||||
step("Generate missing addresses") { generateMissingHotWalletAddresses() }
|
||||
step("Wait for addresses to be generated") { waitForAddressesGenerated() }
|
||||
step("Add a second card wallet '$secondWalletName'") {
|
||||
addNewCardWallet(WalletMockContent)
|
||||
}
|
||||
step("Switch back to the hot wallet") { switchToPreviousWallet() }
|
||||
step("Click on token with name: '$token'") { clickDisplayedTokenOnMain(token) }
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.TokenDetails, storiesExist = false)
|
||||
}
|
||||
step("Select identical receive token '$token' on '$secondWalletName'") {
|
||||
selectReceiveTokenOnWallet(token = token, walletName = secondWalletName)
|
||||
}
|
||||
step("Enter amount '$amount'") { inputAmount(amount) }
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
step("Assert network fee is displayed") { waitForFeeDisplayed() }
|
||||
step("Hold to confirm the transfer") { holdToConfirmTransfer() }
|
||||
step("Assert 'Transfer in progress' screen is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onSwapSuccessScreen { transferInProgressTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("9996")
|
||||
@DisplayName("App transfers: adding a missing token to the recipient wallet enables Transfer")
|
||||
@Test
|
||||
fun addMissingTokenToRecipientWalletEnablesTransferTest() {
|
||||
val token = "Ethereum"
|
||||
val bitcoinToken = "Bitcoin"
|
||||
val recipientWalletName = "Wallet"
|
||||
val recipientWithoutEthereumState = "RecipientWithoutEthereum"
|
||||
val ethereumWithSecondTokenState = "EthereumWithSecondToken"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeAppLaunchSection = { setWireMockScenarioState(storiesScenario, storiesErrorState) },
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(storiesScenario)
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
resetWireMockScenarioState(ethCallScenario)
|
||||
resetWireMockScenarioState(ethBalanceScenario)
|
||||
}
|
||||
).run {
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$recipientWithoutEthereumState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = recipientWithoutEthereumState)
|
||||
}
|
||||
step("Set WireMock scenario: '$ethCallScenario' to state: '$started'") {
|
||||
setWireMockScenarioState(scenarioName = ethCallScenario, state = started)
|
||||
}
|
||||
step("Set WireMock scenario: '$ethBalanceScenario' to state: '$started'") {
|
||||
setWireMockScenarioState(scenarioName = ethBalanceScenario, state = started)
|
||||
}
|
||||
|
||||
step("Open 'Main' screen with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12)
|
||||
}
|
||||
step("Generate missing addresses") { generateMissingHotWalletAddresses() }
|
||||
step("Wait for addresses to be generated") { waitForAddressesGenerated() }
|
||||
step("Assert token '$bitcoinToken' is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onMainScreen { tokenWithTitleAndAddress(bitcoinToken).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
// Switch the user-tokens mock so the second wallet loads with Ethereum while the recipient stays Ethereum-less.
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$ethereumWithSecondTokenState'") {
|
||||
setWireMockScenarioState(scenarioName = USER_TOKENS_API_SCENARIO, state = ethereumWithSecondTokenState)
|
||||
}
|
||||
step("Add a second card wallet") {
|
||||
addNewCardWallet(WalletMockContent)
|
||||
}
|
||||
step("Click on token with name: '$token'") { clickDisplayedTokenOnMain(token) }
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.TokenDetails, storiesExist = false)
|
||||
}
|
||||
step("Add missing token '$token' to recipient wallet '$recipientWalletName'") {
|
||||
addMissingReceiveTokenToWallet(token = token, recipientWalletName = recipientWalletName)
|
||||
}
|
||||
step("Assert Transfer mode is ready") { assertTransferReady() }
|
||||
}
|
||||
}
|
||||
|
||||
// [REDACTED_TASK_KEY]: transfer mode never runs tx validation, so the destination rent-exemption notification never shows.
|
||||
@Ignore("[REDACTED_JIRA]")
|
||||
@AllureId("9852")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue