Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-13 23:39:01 +03:00
parent ece1122e14
commit d792ac6304
13 changed files with 73 additions and 30 deletions

View file

@ -223,6 +223,8 @@ abstract class BaseTestCase : TestCase(
"AND_15489_EXPRESS_SHARE_BUTTON_ENABLED" to true, "AND_15489_EXPRESS_SHARE_BUTTON_ENABLED" to true,
"AND_15235_VISA_MULTIPLE_CARDS" to true, "AND_15235_VISA_MULTIPLE_CARDS" to true,
"AND_15715_SWAP_BEST_DEX_RATE_ENABLED" to true, "AND_15715_SWAP_BEST_DEX_RATE_ENABLED" to true,
// 6.1
"TWI_1638_VA_MVP0_ENABLED" to true,
) )
) )
} }

View file

@ -86,13 +86,23 @@ fun BaseTestCase.addMissingReceiveTokenToWallet(token: String, recipientWalletNa
runCatching { onSwapSelectTokenScreen { marketsTokenWithName(token).performClick() } }.isSuccess 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("Select recipient wallet '$recipientWalletName'") {
step("Assert recipient wallet '$recipientWalletName' is selected") {
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) { composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { onAddToPortfolioScreen { walletName(recipientWalletName).assertIsDisplayed() } }.isSuccess runCatching { onAddToPortfolioScreen { selectedWalletRow.performClick() } }.isSuccess
}
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { onAddToPortfolioScreen { walletOption(recipientWalletName).performClick() } }.isSuccess
} }
} }
step("Click on 'Add' button") { step("Select network '$token'") {
onAddToPortfolioScreen { addButton.performClick() } composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { onAddToPortfolioScreen { networkRow.performClick() } }.isSuccess
}
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
runCatching { onAddToPortfolioScreen { networkOption(token).performClick() } }.isSuccess
}
}
step("Click on 'Confirm' button") {
onAddToPortfolioScreen { confirmButton.performClick() }
} }
} }

View file

@ -317,6 +317,9 @@ fun BaseTestCase.openSwapInTransferModeWithHotWallet(
} }
private fun BaseTestCase.navigateToSwapForToken(tokenName: String, fromAccountName: String) { private fun BaseTestCase.navigateToSwapForToken(tokenName: String, fromAccountName: String) {
step("Collapse header") {
onMainScreen { collapseHeader() }
}
step("Scroll '$fromAccountName' into view (semantics, not touch — avoids the Markets sheet)") { step("Scroll '$fromAccountName' into view (semantics, not touch — avoids the Markets sheet)") {
onMainScreen { scrollToAccount(fromAccountName) } onMainScreen { scrollToAccount(fromAccountName) }
} }

View file

@ -1,24 +1,53 @@
package com.tangem.screens package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.test.hasClickAction
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.BaseBottomSheetTestTags
import com.tangem.wallet.R import com.tangem.wallet.R
import io.github.kakaocup.compose.node.element.ComposeScreen 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.ComposeScreen.Companion.onComposeScreen
import io.github.kakaocup.compose.node.element.KNode import io.github.kakaocup.compose.node.element.KNode
import io.github.kakaocup.kakao.common.utilities.getResourceString import io.github.kakaocup.kakao.common.utilities.getResourceString
// Scoped to the bottom-sheet container: the swap token-search screen stays behind the sheet and
// would otherwise make text matches (wallet tabs, market 'Ethereum' item) ambiguous.
class AddToPortfolioPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : class AddToPortfolioPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<AddToPortfolioPageObject>(semanticsProvider = semanticsProvider) { ComposeScreen<AddToPortfolioPageObject>(
semanticsProvider = semanticsProvider,
viewBuilderAction = { hasTestTag(BaseBottomSheetTestTags.CONTAINER) },
) {
fun walletName(walletName: String): KNode = child { fun walletName(walletName: String): KNode = child {
hasText(walletName) hasText(walletName)
useUnmergedTree = true useUnmergedTree = true
} }
val addButton: KNode = child { /** The wallet row on the 'Add token' screen (label + selected value); tap to open the wallet selector. */
hasText(getResourceString(R.string.common_add)) val selectedWalletRow: KNode = child {
hasText(getResourceString(R.string.wc_common_wallet))
hasClickAction()
}
/** A wallet item inside the portfolio selector screen. */
fun walletOption(walletName: String): KNode = child {
hasText(walletName)
hasClickAction()
}
/** The network row on the 'Add token' screen; tap to open the 'Choose network' selector. */
val networkRow: KNode = child {
hasText(getResourceString(R.string.wc_common_network))
hasClickAction()
}
/** A network item inside the 'Choose network' selector. */
fun networkOption(networkName: String): KNode = child {
hasText(networkName)
hasClickAction()
}
val confirmButton: KNode = child {
hasText(getResourceString(R.string.common_confirm))
hasClickAction() hasClickAction()
} }
} }

View file

@ -26,9 +26,9 @@ class AddTokenBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractions
useUnmergedTree = true useUnmergedTree = true
} }
val addButton: KNode = child { val confirmButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT) hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_add)) hasText(getResourceString(R.string.common_confirm))
useUnmergedTree = true useUnmergedTree = true
} }

View file

@ -96,7 +96,7 @@ class MainScreenPageObject(private val semanticsProvider: SemanticsNodeInteracti
* Required because TangemCollapsingTopBar places the body at y=collapsingHeight, which * Required because TangemCollapsingTopBar places the body at y=collapsingHeight, which
* pushes lower list items off-screen when the header is expanded. * pushes lower list items off-screen when the header is expanded.
*/ */
private fun collapseHeader() { fun collapseHeader() {
screenContainer { screenContainer {
performTouchInput { swipeUp(startY = visibleSize.height * 0.6f, endY = visibleSize.height * 0.1f) } performTouchInput { swipeUp(startY = visibleSize.height * 0.6f, endY = visibleSize.height * 0.1f) }
} }

View file

@ -6,7 +6,6 @@ import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.captureToImage
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.BaseBottomSheetTestTags import com.tangem.core.ui.test.BaseBottomSheetTestTags
import com.tangem.core.ui.test.BaseButtonTestTags
import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags
import com.tangem.wallet.R import com.tangem.wallet.R
import io.github.kakaocup.compose.intercept.operation.ComposeOperationType import io.github.kakaocup.compose.intercept.operation.ComposeOperationType
@ -44,15 +43,13 @@ class TokenReceiveQrCodeBottomSheetPageObject(semanticsProvider: SemanticsNodeIn
} }
val copyButton: KNode = child { val copyButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_copy)) hasText(getResourceString(R.string.common_copy))
useUnmergedTree = true hasClickAction()
} }
val shareButton: KNode = child { val shareButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_share)) hasText(getResourceString(R.string.common_share))
useUnmergedTree = true hasClickAction()
} }
/** Captures the QR code node as a bitmap via the Kakao node delegate (no raw composeTestRule access). */ /** Captures the QR code node as a bitmap via the Kakao node delegate (no raw composeTestRule access). */

View file

@ -21,11 +21,13 @@ import dagger.hilt.android.testing.HiltAndroidTest
import io.github.kakaocup.kakao.common.utilities.getResourceString import io.github.kakaocup.kakao.common.utilities.getResourceString
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
import org.junit.Ignore
import org.junit.Test import org.junit.Test
@HiltAndroidTest @HiltAndroidTest
class BlockchainTest : BaseTestCase() { class BlockchainTest : BaseTestCase() {
@Ignore("[REDACTED_JIRA]")
@AllureId("3644") @AllureId("3644")
@DisplayName("ADA: Checking the min amount/change = 1ADA") @DisplayName("ADA: Checking the min amount/change = 1ADA")
@Test @Test

View file

@ -68,8 +68,8 @@ class BuyTest : BaseTestCase() {
step("Click on $token in Trending list") { step("Click on $token in Trending list") {
onAddFundsBottomSheet { trendingTokenWithTitle(token).clickWithAssertion() } onAddFundsBottomSheet { trendingTokenWithTitle(token).clickWithAssertion() }
} }
step("Click on 'Add' button") { step("Click on 'Confirm' button") {
onAddTokenBottomSheet { addButton.clickWithAssertion() } onAddTokenBottomSheet { confirmButton.clickWithAssertion() }
} }
step("Close 'Get token' screen") { step("Close 'Get token' screen") {
onAddFundsBottomSheet { closeButton.clickWithAssertion() } onAddFundsBottomSheet { closeButton.clickWithAssertion() }

View file

@ -86,9 +86,9 @@ class TotalBalanceUpdateTest : BaseTestCase() {
step("Click on 'Add' button in 'Markets' bottom sheet") { step("Click on 'Add' button in 'Markets' bottom sheet") {
onMarketsScreen { addButton.clickWithAssertion() } onMarketsScreen { addButton.clickWithAssertion() }
} }
step("Click on 'Add' button in 'Add token' bottom sheet") { step("Click on 'Confirm' button in 'Add token' bottom sheet") {
flakySafely(WAIT_UNTIL_TIMEOUT) { flakySafely(WAIT_UNTIL_TIMEOUT) {
onAddTokenBottomSheet { addButton.performClick() } onAddTokenBottomSheet { confirmButton.performClick() }
} }
} }
step("Press 'Back' button") { step("Press 'Back' button") {

View file

@ -24,6 +24,7 @@ import org.junit.Test
@HiltAndroidTest @HiltAndroidTest
class SendConfirmScreenTest : BaseTestCase() { class SendConfirmScreenTest : BaseTestCase() {
@Ignore("[REDACTED_JIRA]")
@AllureId("4003") @AllureId("4003")
@DisplayName("Send (Confirm screen): change sending amount") @DisplayName("Send (Confirm screen): change sending amount")
@Test @Test

View file

@ -58,8 +58,8 @@ class SearchAndSwapTest : BaseTestCase() {
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() } onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
} }
} }
step("Click on 'Add' button") { step("Click on 'Confirm' button") {
onAddTokenBottomSheet { addButton.performClick() } onAddTokenBottomSheet { confirmButton.performClick() }
} }
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") { step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() } onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }
@ -106,8 +106,8 @@ class SearchAndSwapTest : BaseTestCase() {
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() } onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
} }
} }
step("Click on 'Add' button") { step("Click on 'Confirm' button") {
onAddTokenBottomSheet { addButton.performClick() } onAddTokenBottomSheet { confirmButton.performClick() }
} }
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") { step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() } onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }
@ -195,8 +195,8 @@ class SearchAndSwapTest : BaseTestCase() {
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() } onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
} }
} }
step("Click on 'Add' button") { step("Click on 'Confirm' button") {
onAddTokenBottomSheet { addButton.performClick() } onAddTokenBottomSheet { confirmButton.performClick() }
} }
step("Assert warning title '$warningTitle' is displayed") { step("Assert warning title '$warningTitle' is displayed") {
onSwapTokenScreen { warningTitle(warningTitle).assertIsDisplayed() } onSwapTokenScreen { warningTitle(warningTitle).assertIsDisplayed() }
@ -251,8 +251,8 @@ class SearchAndSwapTest : BaseTestCase() {
onSwapSelectTokenScreen { marketsTokenWithName(swapTokenName).performClick() } onSwapSelectTokenScreen { marketsTokenWithName(swapTokenName).performClick() }
} }
} }
step("Click on 'Add' button") { step("Click on 'Confirm' button") {
onAddTokenBottomSheet { addButton.performClick() } onAddTokenBottomSheet { confirmButton.performClick() }
} }
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") { step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() } onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }

View file

@ -19,7 +19,6 @@ import com.tangem.tap.domain.sdk.mocks.content.Wallet2WithDerivationsMockContent
import com.tangem.tap.domain.sdk.mocks.content.WalletMockContent import com.tangem.tap.domain.sdk.mocks.content.WalletMockContent
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
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.AllureId import io.qameta.allure.kotlin.AllureId
import io.qameta.allure.kotlin.junit4.DisplayName import io.qameta.allure.kotlin.junit4.DisplayName
import org.junit.Ignore import org.junit.Ignore