Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-23 07:35:46 +03:00
parent 404daaa085
commit 3d4136ac8c
26 changed files with 1775 additions and 125 deletions

View file

@ -12,7 +12,8 @@ import io.qameta.allure.kotlin.Allure.step
fun BaseTestCase.scanCard(
productType: ProductType? = null,
mockContent: MockContent? = null,
alreadyActivatedDialogIsShown : Boolean = false
alreadyActivatedDialogIsShown: Boolean = false,
isTwinsCard: Boolean = false,
) {
if (productType != null) {
MockProvider.setMocks(productType)
@ -32,18 +33,28 @@ fun BaseTestCase.scanCard(
AlreadyUsedWalletDialogPageObject { thisIsMyWalletButton.click() }
}
}
if (isTwinsCard) {
step("Click on 'Continue' button") {
onOnboardingScreen { continueButton.clickWithAssertion() }
}
step("Click on 'Continue to my wallet' button") {
onOnboardingScreen { continueToMyWalletButton.clickWithAssertion() }
}
}
}
fun BaseTestCase.openMainScreen(
productType: ProductType? = null,
mockContent: MockContent? = null,
alreadyActivatedDialogIsShown: Boolean = false
alreadyActivatedDialogIsShown: Boolean = false,
isTwinsCard: Boolean = false,
) {
step("Scan card") {
scanCard(
productType = productType,
mockContent = mockContent,
alreadyActivatedDialogIsShown = alreadyActivatedDialogIsShown
alreadyActivatedDialogIsShown = alreadyActivatedDialogIsShown,
isTwinsCard = isTwinsCard,
)
}
step("Assert 'Main' screen is displayed") {

View file

@ -0,0 +1,116 @@
package com.tangem.scenarios
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,
withTransactions: Boolean = false,
withWalletImage: Boolean = true
) {
step("Assert card title equal '$cardTitle'") {
onMainScreen { walletNameText.assertTextEquals(cardTitle) }
}
if (withTransactions) {
step("Assert 'Transactions' block is displayed") {
onMainScreen { transactionsExplorerText().assertExists() }
}
step("Assert 'Transactions' title is displayed") {
onMainScreen { transactionsTitle.assertIsDisplayed() }
}
step("Assert 'Explorer' icon is displayed") {
onMainScreen { transactionsExplorerIcon.assertIsDisplayed() }
}
} else {
step("Assert empty 'Transactions' block is displayed") {
onMainScreen { emptyTransactionBlock.assertIsDisplayed() }
}
step("Assert empty 'Transactions' block icon is displayed") {
onMainScreen { emptyTransactionBlockIcon.assertIsDisplayed() }
}
step("Assert empty 'Transactions' block text is displayed") {
onMainScreen { emptyTransactionBlockText.assertIsDisplayed() }
}
step("Assert empty 'Transactions' block 'Explore' button is displayed") {
onMainScreen { emptyTransactionBlockExploreButton.assertIsDisplayed() }
}
}
if (withWalletImage) {
step("Assert card image is displayed") { //TODO: create assertion method for checking images
onMainScreen { walletImage.assertIsDisplayed() }
}
} else {
step("Assert card image is not displayed") {
onMainScreen { walletImage.assertIsNotDisplayed() }
}
}
step("Assert 'Receive' button is displayed") {
onMainScreen { receiveButton.assertIsDisplayed() }
}
step("Assert 'Buy' button is displayed") {
onMainScreen { buyButton.assertIsDisplayed() }
}
step("Assert 'Send' button is displayed") {
onMainScreen { sendButton.assertIsDisplayed() }
}
step("Assert 'Sell' button is displayed") {
onMainScreen { sellButton.assertIsDisplayed() }
}
step("Assert 'Swap' button is not displayed") {
onMainScreen { swapButton.assertIsNotDisplayed() }
}
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")
}
}
}
fun BaseTestCase.checkMultiCurrencyMainScreen(
devicesCount: String,
cardTitle: String,
withWalletImage: Boolean = true
) {
step("Assert card title equal '$cardTitle'") {
onMainScreen { walletNameText.assertTextEquals(cardTitle) }
}
if (withWalletImage) {
step("Assert card image is displayed") {
onMainScreen { walletImage.assertIsDisplayed() }
}
} else {
step("Assert card image is not displayed") {
onMainScreen { walletImage.assertIsNotDisplayed() }
}
}
step("Assert devices count equal to '$devicesCount'") {
onMainScreen { walletDevicesCount.assertTextContains(devicesCount) }
}
step("Assert 'Buy' button is displayed") {
onMainScreen { buyButton.assertIsDisplayed() }
}
step("Assert 'Swap' button is displayed") {
onMainScreen { swapButton.assertIsDisplayed() }
}
step("Assert 'Sell' button is displayed") {
onMainScreen { sellButton.assertIsDisplayed() }
}
step("Assert 'Send' button is not displayed") {
onMainScreen { sendButton.assertIsNotDisplayed() }
}
step("Assert 'Receive' button is not displayed") {
onMainScreen { receiveButton.assertIsNotDisplayed() }
}
step("Assert 'Organize tokens' button is displayed") {
onMainScreen { organizeTokensButton().assertIsDisplayed() }
}
}

View file

@ -44,10 +44,30 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
}
val buyButton: KNode = child {
hasTestTag(MainScreenTestTags.MULTI_CURRENCY_ACTION_BUTTON)
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_buy))
}
val sendButton: KNode = child {
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_send))
}
val receiveButton: KNode = child {
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_receive))
}
val sellButton: KNode = child {
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_sell))
}
val swapButton: KNode = child {
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_swap))
}
val walletNameText: KNode = child {
hasTestTag(MainScreenTestTags.CARD_TITLE)
useUnmergedTree = true
@ -58,6 +78,11 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
useUnmergedTree = true
}
val walletDevicesCount: KNode = child {
hasTestTag(MainScreenTestTags.DEVICES_COUNT)
useUnmergedTree = true
}
val marketPriceBlock: KNode = child {
hasTestTag(MarketPriceBlockTestTags.BLOCK)
useUnmergedTree = true
@ -79,7 +104,7 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
useUnmergedTree = true
}
fun transactionsExplorer(): KNode {
fun transactionsExplorerText(): KNode {
return child {
hasTestTag(TransactionHistoryBlockTestTags.EXPLORER_TEXT)
hasText(getResourceString(R.string.common_explorer))
@ -87,6 +112,22 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
}
}
val emptyTransactionBlock: KNode = child {
hasTestTag(EmptyTransactionBlockTestTags.BLOCK)
}
val emptyTransactionBlockIcon: KNode = child {
hasTestTag(EmptyTransactionBlockTestTags.ICON)
}
val emptyTransactionBlockText: KNode = child {
hasTestTag(EmptyTransactionBlockTestTags.TEXT)
}
val emptyTransactionBlockExploreButton: KNode = child {
hasTestTag(EmptyTransactionBlockTestTags.EXPLORE_BUTTON)
}
val notificationContainer: KNode = child {
hasTestTag(NotificationTestTags.CONTAINER)
useUnmergedTree = true

View file

@ -22,6 +22,18 @@ class OnboardingPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
useUnmergedTree = true
}
val continueButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_continue))
useUnmergedTree = true
}
val continueToMyWalletButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.onboarding_button_continue_wallet))
useUnmergedTree = true
}
val createWalletTopBarTitle: KNode = child {
hasTestTag(TopAppBarTestTags.TITLE)
hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_header))

View file

@ -5,6 +5,7 @@ import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.common.utils.LazyListItemNode
import com.tangem.core.ui.test.BaseActionButtonsBlockTestTags
import com.tangem.core.ui.test.BaseButtonTestTags
import com.tangem.core.ui.test.NotificationTestTags
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
@ -85,7 +86,7 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
private val horizontalActionChips = KLazyListNode(
semanticsProvider = semanticsProvider,
viewBuilderAction = { hasTestTag(TokenDetailsScreenTestTags.HORIZONTAL_ACTION_CHIPS) },
viewBuilderAction = { hasTestTag(BaseActionButtonsBlockTestTags.HORIZONTAL_ACTION_CHIPS) },
itemTypeBuilder = { itemType(::LazyListItemNode) },
positionMatcher = { position ->
SemanticsMatcher.expectValue(
@ -97,25 +98,25 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
@OptIn(ExperimentalTestApi::class)
val swapButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_swap))
}
@OptIn(ExperimentalTestApi::class)
val sellButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_sell))
}
@OptIn(ExperimentalTestApi::class)
val buyButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_buy))
}
@OptIn(ExperimentalTestApi::class)
val sendButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
hasText(getResourceString(R.string.common_send))
}

View file

@ -1,35 +0,0 @@
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")
}
}
}

View file

@ -0,0 +1,196 @@
package com.tangem.tests
import com.tangem.common.BaseTestCase
import com.tangem.domain.models.scan.ProductType
import com.tangem.scenarios.checkMultiCurrencyMainScreen
import com.tangem.scenarios.checkSingleCurrencyMainScreen
import com.tangem.scenarios.openMainScreen
import com.tangem.tap.domain.sdk.mocks.MockContent
import com.tangem.tap.domain.sdk.mocks.content.*
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 ScanCardTest : BaseTestCase() {
@AllureId("868")
@DisplayName("Scan: Scanning single-currency cards")
@Test
fun singleTokenNoteCardScanTest() {
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,
withTransactions = true
)
}
}
}
@AllureId("869")
@DisplayName("Scan: 'Twin' card")
@Test
fun twinsCardScanTest() {
val cardBlockchain = "BTC"
val cardType: MockContent = TwinsMockContent
val cardName = "Twin"
setupHooks().run {
step("Open 'Main Screen' on '$cardName' card") {
openMainScreen(mockContent = cardType, isTwinsCard = true)
}
step("Check 'Main' screen for '$cardName' $cardBlockchain card") {
checkSingleCurrencyMainScreen(cardBlockchain = cardBlockchain, cardTitle = cardName)
}
}
}
@AllureId("872")
@DisplayName("Scan: Card with Secp256k1 curve")
@Test
fun secpk1CurveCardScanTest() {
val devicesCount = "1 device"
val cardType: MockContent = Secpk1CurveMockContent
val cardName = "Wallet"
val card = "card with Secp256k1 curve"
setupHooks().run {
step("Open 'Main Screen' on $card") {
openMainScreen(mockContent = cardType, alreadyActivatedDialogIsShown = true)
}
step("Check 'Main' screen for $card curve with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(
devicesCount = devicesCount,
cardTitle = cardName,
withWalletImage = false
)
}
}
}
@AllureId("870")
@DisplayName("Scan: Card with Ed25519 curve")
@Test
fun edCurveCardScanTest() {
val cardBlockchain = "XLM"
val cardType: MockContent = EdCurveMockContent
val cardName = "Tangem card"
val card = "card with Ed25519 curve"
setupHooks().run {
step("Open 'Main Screen' on $card") {
openMainScreen(mockContent = cardType)
}
step("Check 'Main' screen for $card with blockchain: '$cardBlockchain'") {
checkSingleCurrencyMainScreen(
cardBlockchain = cardBlockchain,
cardTitle = cardName,
withWalletImage = false
)
}
}
}
@AllureId("866")
@DisplayName("Scan: 'Shiba' card")
@Test
fun shibaCardScanTest() {
val devicesCount = "2 devices"
val cardType: MockContent = ShibaMockContent
val cardName = "Wallet"
val card = "Shiba"
setupHooks().run {
step("Open 'Main Screen' on '$card' card") {
openMainScreen(mockContent = cardType, alreadyActivatedDialogIsShown = true)
}
step("Check 'Main' screen for '$card' card with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(devicesCount, cardName)
}
}
}
@AllureId("864")
@DisplayName("Scan: 'Ring'")
@Test
fun ringScanTest() {
val devicesCount = "3 devices"
val cardType: ProductType = ProductType.Ring
val cardName = "Wallet"
val ring = "Ring"
setupHooks().run {
step("Open 'Main Screen' on '$ring'") {
openMainScreen(productType = cardType, alreadyActivatedDialogIsShown = true)
}
step("Check 'Main' screen for '$ring' with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(devicesCount, cardName)
}
}
}
@AllureId("867")
@DisplayName("Scan: 'Wallet' card")
@Test
fun walletCardScanTest() {
val devicesCount = "1 device"
val cardType: ProductType = ProductType.Wallet
val cardName = "Wallet"
setupHooks().run {
step("Open 'Main Screen' on '$cardName' card") {
openMainScreen(productType = cardType)
}
step("Check 'Main' screen for '$cardName' card with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(devicesCount, cardName)
}
}
}
@AllureId("865")
@DisplayName("Scan: 'Wallet 2' card")
@Test
fun wallet2ScanTest() {
val devicesCount = "2 devices"
val cardType: MockContent = Wallet2MockContent
val cardName = "Wallet"
val card = "Wallet 2"
setupHooks().run {
step("Open 'Main Screen' on '$card' card") {
openMainScreen(mockContent = cardType, alreadyActivatedDialogIsShown = true)
}
step("Check 'Main' screen for '$card' card with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(devicesCount, cardName)
}
}
}
@AllureId("871")
@DisplayName("Scan: Card with 4.12 firmware")
@Test
fun firmware412CardScanTest() {
val devicesCount = "1 device"
val cardType: MockContent = Firmware412MockContent
val cardName = "Tangem card"
val card = "card with 4.12 firmware"
setupHooks().run {
step("Open 'Main Screen' on '$card'") {
openMainScreen(mockContent = cardType)
}
step("Check 'Main' screen for '$card' with devices count = '$devicesCount'") {
checkMultiCurrencyMainScreen(devicesCount, cardName)
}
}
}
}

View file

@ -1,31 +0,0 @@
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)
}
}
}
}