From ef39279cb9987549d7198552ddd3245264af8c8e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 16 Sep 2025 14:32:40 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../common/extensions/CustomAssertsExt.kt | 25 +++++++++ .../tangem/screens/MainScreenPageObject.kt | 52 +++++++++++++++++-- .../screens/OrganizeTokensPageObject.kt | 22 +++++--- .../tangem/screens/TokenDetailsPageObject.kt | 7 ++- .../steps/SingleCurrencyCardScenario.kt | 35 +++++++++++++ .../tangem/tests/SingleCurrencyCardsScan.kt | 31 +++++++++++ .../marketprice/MarketPriceBlock.kt | 6 ++- .../components/transactions/TxHistoryTitle.kt | 8 ++- .../tangem/core/ui/test/MainScreenTestTags.kt | 2 + .../core/ui/test/MarketPriceBlockTestTags.kt | 6 +++ .../test/TransactionHistoryBlockTestTags.kt | 7 +++ .../wallet/ui/components/common/WalletCard.kt | 6 ++- 12 files changed, 187 insertions(+), 20 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/steps/SingleCurrencyCardScenario.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/tests/SingleCurrencyCardsScan.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/MarketPriceBlockTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/TransactionHistoryBlockTestTags.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt b/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt new file mode 100644 index 0000000000..e1040b8ffc --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/common/extensions/CustomAssertsExt.kt @@ -0,0 +1,25 @@ +package com.tangem.common.extensions + +import io.github.kakaocup.compose.node.element.KNode + +fun assertElementDoesNotExist( + elementProvider: () -> KNode, + elementDescription: String, +) { + try { + elementProvider().assertExists() + throw AssertionError("$elementDescription should not exist but was found") + } catch (e: AssertionError) { + val isNotFoundError = e.message?.let { message -> + message.contains("No node found") || + message.contains("scrollable container") || + message.contains("There are no existing nodes") || + message.contains("There are no existing nodes for that selector") + } ?: false + if (isNotFoundError) { + return + } else { + throw e + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt index 4f14e268c2..f8abf2f869 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt @@ -7,10 +7,7 @@ import androidx.compose.ui.test.hasAnyAncestor import com.tangem.common.BaseTestCase import com.tangem.common.extensions.hasLazyListItemPosition import com.tangem.common.utils.LazyListItemNode -import com.tangem.core.ui.test.BaseButtonTestTags -import com.tangem.core.ui.test.MainScreenTestTags -import com.tangem.core.ui.test.NotificationTestTags -import com.tangem.core.ui.test.TokenElementsTestTags +import com.tangem.core.ui.test.* import com.tangem.core.ui.utils.LazyListItemPositionSemantics import com.tangem.feature.wallet.impl.R import io.github.kakaocup.compose.node.element.ComposeScreen @@ -51,6 +48,45 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) hasText(getResourceString(R.string.common_buy)) } + val walletNameText: KNode = child { + hasTestTag(MainScreenTestTags.CARD_TITLE) + useUnmergedTree = true + } + + val walletImage: KNode = child { + hasTestTag(MainScreenTestTags.CARD_IMAGE) + useUnmergedTree = true + } + + val marketPriceBlock: KNode = child { + hasTestTag(MarketPriceBlockTestTags.BLOCK) + useUnmergedTree = true + } + + val marketPriceText: KNode = child { + hasTestTag(MarketPriceBlockTestTags.TEXT) + useUnmergedTree = true + } + + val transactionsExplorerIcon: KNode = child { + hasTestTag(TransactionHistoryBlockTestTags.EXPLORER_ICON) + useUnmergedTree = true + } + + val transactionsTitle: KNode = child { + hasTestTag(TransactionHistoryBlockTestTags.TITLE_TEXT) + hasText(getResourceString(R.string.common_transactions)) + useUnmergedTree = true + } + + fun transactionsExplorer(): KNode { + return child { + hasTestTag(TransactionHistoryBlockTestTags.EXPLORER_TEXT) + hasText(getResourceString(R.string.common_explorer)) + useUnmergedTree = true + } + } + val notificationContainer: KNode = child { hasTestTag(NotificationTestTags.CONTAINER) useUnmergedTree = true @@ -146,6 +182,14 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) } } + fun organizeTokensButtonWithoutLazySearch(): KNode { + return child { + hasTestTag(MainScreenTestTags.ORGANIZE_TOKENS_BUTTON) + hasText(getResourceString(R.string.organize_tokens_title)) + useUnmergedTree = true + } + } + fun tokenNetworkGroupTitle(tokenNetwork: String): KNode { return lazyList.child { hasTestTag(MainScreenTestTags.TOKEN_LIST_ITEM) diff --git a/app/src/androidTest/kotlin/com/tangem/screens/OrganizeTokensPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/OrganizeTokensPageObject.kt index f7a76181e8..1d5bd3418b 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/OrganizeTokensPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/OrganizeTokensPageObject.kt @@ -5,8 +5,8 @@ import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import com.tangem.common.BaseTestCase import com.tangem.common.extensions.hasLazyListItemPosition import com.tangem.common.utils.LazyListItemNode -import com.tangem.core.ui.test.TokenElementsTestTags import com.tangem.core.ui.test.OrganizeTokensScreenTestTags +import com.tangem.core.ui.test.TokenElementsTestTags import com.tangem.core.ui.utils.LazyListItemPositionSemantics import com.tangem.feature.wallet.impl.R import io.github.kakaocup.compose.node.element.ComposeScreen @@ -14,10 +14,10 @@ import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onCompose import io.github.kakaocup.compose.node.element.KNode import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode import io.github.kakaocup.kakao.common.utilities.getResourceString -import androidx.compose.ui.test.hasText as withText -import androidx.compose.ui.test.hasTestTag as withTestTag -import androidx.compose.ui.test.hasAnySibling as withAnySibling import androidx.compose.ui.test.hasAnyChild as withAnyChild +import androidx.compose.ui.test.hasAnySibling as withAnySibling +import androidx.compose.ui.test.hasTestTag as withTestTag +import androidx.compose.ui.test.hasText as withText class OrganizeTokensPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen(semanticsProvider = semanticsProvider) { @@ -25,10 +25,12 @@ class OrganizeTokensPageObject(semanticsProvider: SemanticsNodeInteractionsProvi // region TopBar val title: KNode = child { hasText(getResourceString(R.string.organize_tokens_title)) + useUnmergedTree = true } private val topBarGroupButton: KNode = child { hasTestTag(OrganizeTokensScreenTestTags.GROUP_BUTTON) + useUnmergedTree = true } val groupButton: KNode = topBarGroupButton.child { @@ -100,10 +102,14 @@ class OrganizeTokensPageObject(semanticsProvider: SemanticsNodeInteractionsProvi return lazyList.child { hasTestTag(OrganizeTokensScreenTestTags.DRAGGABLE_IMAGE) useUnmergedTree = true - hasParent(withTestTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK) - .and(withAnySibling(withTestTag(TokenElementsTestTags.TOKEN_TITLE) - .and(withAnyChild(withText(tokenTitle)))) - ) + hasParent( + withTestTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK) + .and( + withAnySibling( + withTestTag(TokenElementsTestTags.TOKEN_TITLE) + .and(withAnyChild(withText(tokenTitle))) + ) + ) ) } } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt index 3b9fb1b195..3a0cd0962d 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/TokenDetailsPageObject.kt @@ -4,17 +4,17 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import com.tangem.common.BaseTestCase -import com.tangem.core.ui.test.BaseButtonTestTags import com.tangem.common.utils.LazyListItemNode +import com.tangem.core.ui.test.BaseButtonTestTags import com.tangem.core.ui.test.NotificationTestTags import com.tangem.core.ui.test.TokenDetailsScreenTestTags -import com.tangem.features.tokendetails.impl.R import com.tangem.core.ui.utils.LazyListItemPositionSemantics +import com.tangem.features.tokendetails.impl.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 import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode +import io.github.kakaocup.kakao.common.utilities.getResourceString import androidx.compose.ui.test.hasText as withText class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : @@ -156,7 +156,6 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide hasText(getResourceString(R.string.common_buy_currency, feeCurrencySymbol)) useUnmergedTree = true } - } internal fun BaseTestCase.onTokenDetailsScreen(function: TokenDetailsPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/steps/SingleCurrencyCardScenario.kt b/app/src/androidTest/kotlin/com/tangem/steps/SingleCurrencyCardScenario.kt new file mode 100644 index 0000000000..f9c68f5096 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/steps/SingleCurrencyCardScenario.kt @@ -0,0 +1,35 @@ +package com.tangem.steps + +import com.tangem.common.BaseTestCase +import com.tangem.common.extensions.assertElementDoesNotExist +import com.tangem.screens.onMainScreen +import io.qameta.allure.kotlin.Allure.step + +fun BaseTestCase.checkSingleCurrencyMainScreen(cardBlockchain: String, cardTitle: String) { + step("Assert card title equal '$cardTitle'") { + onMainScreen { walletNameText.assertTextEquals(cardTitle) } + } + step("Assert 'Transactions' block is displayed") { + onMainScreen { transactionsExplorer().assertExists() } + } + step("Assert 'Transactions' title is displayer") { + onMainScreen { transactionsTitle.assertIsDisplayed() } + } + step("Assert 'Explorer Icon' is displayed") { + onMainScreen { transactionsExplorerIcon.assertIsDisplayed() } + } + step("Assert card image is displayed") { //TODO: Придумать нормальную проверку / реализовать скриншот-тестинг + onMainScreen { walletImage.assertIsDisplayed() } + } + step("Assert 'Market Price' on single card main screen is displayed") { + onMainScreen { marketPriceBlock.assertIsDisplayed() } + } + step("Assert 'Market Price' title equals $cardBlockchain Market Price") { + onMainScreen { marketPriceText.assertTextContains("$cardBlockchain Market Price") } + } + step("Assert 'Organize tokens' button is not displayed") { + onMainScreen { + assertElementDoesNotExist({ organizeTokensButtonWithoutLazySearch() }, "Organize tokens button") + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/SingleCurrencyCardsScan.kt b/app/src/androidTest/kotlin/com/tangem/tests/SingleCurrencyCardsScan.kt new file mode 100644 index 0000000000..34d745794f --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/SingleCurrencyCardsScan.kt @@ -0,0 +1,31 @@ +package com.tangem.tests + +import com.tangem.common.BaseTestCase +import com.tangem.domain.models.scan.ProductType +import com.tangem.scenarios.openMainScreen +import com.tangem.steps.checkSingleCurrencyMainScreen +import dagger.hilt.android.testing.HiltAndroidTest +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test + +@HiltAndroidTest +class SingleCurrencyCardsScan : BaseTestCase() { + + @AllureId("868") + @DisplayName("Scan: Scanning single-currency cards") + @Test + fun singleTokenNoteScanTest() { + val cardBlockchain = "DOGE" + val cardType: ProductType = ProductType.Note + + setupHooks().run { + step("Open 'Main Screen' on ${cardType.name} card") { + openMainScreen(cardType) + } + step("Check 'Main' screen for ${cardType.name} $cardBlockchain card") { + checkSingleCurrencyMainScreen(cardBlockchain = cardBlockchain, cardTitle = cardType.name) + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt index 204f816c64..92785ec693 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -20,6 +21,7 @@ import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.MarketPriceBlockTestTags import com.tangem.utils.StringsSigns.DASH_SIGN /** @@ -45,7 +47,8 @@ fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier .fillMaxWidth() .heightIn(min = TangemTheme.dimens.size72) .padding(all = TangemTheme.dimens.spacing12) - .onSizeChanged { rootWidth = it.width }, + .onSizeChanged { rootWidth = it.width } + .testTag(MarketPriceBlockTestTags.BLOCK), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), horizontalAlignment = Alignment.Start, ) { @@ -64,6 +67,7 @@ private fun Title(currencyName: String, modifier: Modifier = Modifier) { text = stringResourceSafe(id = R.string.wallet_marketplace_block_title, currencyName), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.subtitle2, + modifier = Modifier.testTag(MarketPriceBlockTestTags.TEXT), ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index f7cb3263ce..eec1924146 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -9,12 +9,14 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.R import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TransactionHistoryBlockTestTags /** * Transactions block title @@ -38,10 +40,13 @@ fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Modifier) { text = stringResourceSafe(id = R.string.common_transactions), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.subtitle2, + modifier = Modifier.testTag(TransactionHistoryBlockTestTags.TITLE_TEXT), ) Row( - modifier = Modifier.clickable(onClick = onExploreClick), + modifier = Modifier + .clickable(onClick = onExploreClick) + .testTag(TransactionHistoryBlockTestTags.EXPLORER_ICON), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2), ) { @@ -55,6 +60,7 @@ fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Modifier) { text = stringResourceSafe(id = R.string.common_explorer), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.subtitle2, + modifier = Modifier.testTag(TransactionHistoryBlockTestTags.EXPLORER_TEXT), ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt index 117cfefc69..cb1b1e1edf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt @@ -8,6 +8,8 @@ object MainScreenTestTags { const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM" const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON" const val MULTI_CURRENCY_ACTION_BUTTON = "MAIN_SCREEN_MULTI_CURRENCY_ACTION_BUTTON" + const val CARD_TITLE = "MAIN_SCREEN_CARD_TITLE" + const val CARD_IMAGE = "MAIN_SCREEN_CARD_IMAGE" const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE" const val TOTAL_BALANCE_MENU_ITEM = "MAIN_SCREEN_TOTAL_BALANCE_MENU_ITEM" diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/MarketPriceBlockTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/MarketPriceBlockTestTags.kt new file mode 100644 index 0000000000..c271c61cc3 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/MarketPriceBlockTestTags.kt @@ -0,0 +1,6 @@ +package com.tangem.core.ui.test + +object MarketPriceBlockTestTags { + const val BLOCK = "MAIN_SCREEN_MARKET_PRICE_BLOCK" + const val TEXT = "MAIN_SCREEN_MARKET_PRICE_TEXT" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TransactionHistoryBlockTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TransactionHistoryBlockTestTags.kt new file mode 100644 index 0000000000..4a031ddd39 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TransactionHistoryBlockTestTags.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.test + +object TransactionHistoryBlockTestTags { + const val TITLE_TEXT = "TRANSACTION_HISTORY_TITLE_TEXT" + const val EXPLORER_TEXT = "TRANSACTION_HISTORY_EXPLORER_TEXT" + const val EXPLORER_ICON = "TRANSACTION_HISTORY_EXPLORER_ICON" +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index d953f8a8ba..08f52280ea 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -261,7 +261,7 @@ private fun MenuItem(text: TextReference, imageVector: ImageVector, onClick: () private fun TitleText(text: String, modifier: Modifier = Modifier) { Text( text = text, - modifier = modifier, + modifier = modifier.testTag(MainScreenTestTags.CARD_TITLE), color = TangemTheme.colors.text.tertiary, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -375,7 +375,9 @@ private fun Image(@DrawableRes id: Int?, modifier: Modifier = Modifier) { Image( painter = painterResource(id = imageRes), contentDescription = null, - modifier = Modifier.width(width = TangemTheme.dimens.size120), + modifier = Modifier + .width(width = TangemTheme.dimens.size120) + .testTag(MainScreenTestTags.CARD_IMAGE), contentScale = ContentScale.FillWidth, ) }