Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-29 14:00:48 +02:00
parent 1b706b20a7
commit a121268ce0
30 changed files with 685 additions and 9 deletions

View file

@ -0,0 +1,30 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.AppCurrencySelectorScreenTestTags
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 androidx.compose.ui.test.hasText as withText
class AppCurrencySelectorPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<AppCurrencySelectorPageObject>(semanticsProvider = semanticsProvider) {
val searchActionButton: KNode = child {
hasTestTag(AppCurrencySelectorScreenTestTags.TOP_BAR_ACTION_BUTTON)
}
val searchField: KNode = child {
hasTestTag(AppCurrencySelectorScreenTestTags.SEARCH_FIELD)
}
fun currencyItem(code: String): KNode = child {
hasTestTag(AppCurrencySelectorScreenTestTags.CURRENCY_ITEM)
hasAnyDescendant(withText(code, substring = true))
useUnmergedTree = true
}
}
internal fun BaseTestCase.onAppCurrencySelectorScreen(function: AppCurrencySelectorPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -0,0 +1,20 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.AppSettingsScreenTestTags
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
class AppSettingsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<AppSettingsPageObject>(semanticsProvider = semanticsProvider) {
val currencyButton: KNode = child {
hasTestTag(AppSettingsScreenTestTags.CURRENCY_BUTTON)
useUnmergedTree = true
}
}
internal fun BaseTestCase.onAppSettingsScreen(function: AppSettingsPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -9,6 +9,7 @@ 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 androidx.compose.ui.test.hasText as withText
class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<DetailsPageObject>(semanticsProvider = semanticsProvider) {
@ -58,6 +59,12 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
hasTestTag(DetailsScreenTestTags.VERSION_NAME)
useUnmergedTree = true
}
fun walletNameValue(name: String): KNode = child {
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
hasAnyDescendant(withText(name))
useUnmergedTree = true
}
}
internal fun BaseTestCase.onDetailsScreen(function: DetailsPageObject.() -> Unit) =

View file

@ -46,6 +46,10 @@ class DeviceSettingsPageObject(semanticsProvider: SemanticsNodeInteractionsProvi
useUnmergedTree = true
}
val securityModeRow: KNode = child {
hasTestTag(DeviceSettingsScreenTestTags.SECURITY_MODE_ROW)
}
fun resetToFactorySettingsButtonSubtitle(withBackup: Boolean = false): KNode = child {
hasTestTag(DeviceSettingsScreenTestTags.ITEM_SUBTITLE)
useUnmergedTree = true

View file

@ -25,6 +25,11 @@ class DialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
hasTestTag(BaseDialogTestTags.TEXT)
}
val inputField: KNode = child {
hasTestTag(BaseDialogTestTags.TEXT_INPUT_FIELD)
useUnmergedTree = true
}
val cancelButton: KNode = child {
hasTestTag(BaseButtonTestTags.BUTTON)
hasText(getResourceString(R.string.common_cancel))

View file

@ -0,0 +1,19 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.SecurityModeScreenTestTags
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
class SecurityModePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<SecurityModePageObject>(semanticsProvider = semanticsProvider) {
val screenContainer: KNode = child {
hasTestTag(SecurityModeScreenTestTags.SCREEN_CONTAINER)
}
}
internal fun BaseTestCase.onSecurityModeScreen(function: SecurityModePageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -85,6 +85,11 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
hasTestTag(TokenDetailsScreenTestTags.TOKEN_TITLE)
}
val fiatBalance: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.BALANCE_FIAT)
useUnmergedTree = true
}
private val horizontalActionChips = KLazyListNode(
semanticsProvider = semanticsProvider,
viewBuilderAction = { hasTestTag(BaseActionButtonsBlockTestTags.HORIZONTAL_ACTION_CHIPS) },

View file

@ -38,6 +38,16 @@ class WalletSettingsPageObject(semanticsProvider: SemanticsNodeInteractionsProvi
hasText(getResourceString(R.string.settings_forget_wallet))
}
val renameWalletButton: KNode = child {
hasTestTag(WalletSettingsScreenTestTags.RENAME_BUTTON)
useUnmergedTree = true
}
fun walletNameValue(name: String): KNode = walletSettingsItem.child {
withText(name)
useUnmergedTree = true
}
val accountsListContainer: KNode = walletSettingsItem.child {
hasTestTag(WalletSettingsScreenTestTags.ACCOUNTS_CONTAINER)
}

View file

@ -0,0 +1,71 @@
package com.tangem.tests
import com.tangem.common.BaseTestCase
import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.domain.models.scan.ProductType
import com.tangem.scenarios.openMainScreen
import com.tangem.screens.*
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 AppCurrencyTest : BaseTestCase() {
@AllureId("781")
@DisplayName("App Currency: change of equivalent")
@Test
fun changeAppCurrencyTest() {
val currenciesScenario = "currencies_api"
val appSettingsState = "AppSettings"
val targetCurrency = "EUR"
val targetSymbol = ""
val token = "Polygon"
setupHooks(
additionalAfterSection = { resetWireMockScenarioState(currenciesScenario) },
).run {
step("Set WireMock scenario '$currenciesScenario' to '$appSettingsState'") {
setWireMockScenarioState(scenarioName = currenciesScenario, state = appSettingsState)
}
step("Open 'Main Screen'") {
openMainScreen(productType = ProductType.Wallet2)
}
step("Open wallet details") {
onMainScreenTopBar { moreButton.clickWithAssertion() }
}
step("Click on 'App settings' button") {
onDetailsScreen { appSettingsButton.clickWithAssertion() }
}
step("Click on 'App currency' button") {
onAppSettingsScreen { currencyButton.clickWithAssertion() }
}
step("Click on search button") {
onAppCurrencySelectorScreen { searchActionButton.clickWithAssertion() }
}
step("Search currency '$targetCurrency'") {
onAppCurrencySelectorScreen { searchField.performTextInput(targetCurrency) }
}
step("Click on currency '$targetCurrency'") {
onAppCurrencySelectorScreen { currencyItem(targetCurrency).performClick() }
}
step("Return to 'Main' screen") {
device.uiDevice.pressBack()
device.uiDevice.pressBack()
waitForIdle()
}
step("Assert total balance contains '$targetSymbol' on 'Main' screen") {
onMainScreen { totalBalanceText.assertTextContains(targetSymbol) }
}
step("Click on token '$token'") {
onMainScreen { tokenWithTitleAndAddress(token).clickWithAssertion() }
}
step("Assert token fiat balance contains '$targetSymbol'") {
onTokenDetailsScreen { fiatBalance.assertTextContains(targetSymbol) }
}
}
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.common.extensions.clickWithAssertion
import com.tangem.domain.models.scan.ProductType
import com.tangem.scenarios.openMainScreen
import com.tangem.screens.*
import com.tangem.tap.domain.sdk.mocks.content.Firmware412MockContent
import dagger.hilt.android.testing.HiltAndroidTest
import io.qameta.allure.kotlin.AllureId
import io.qameta.allure.kotlin.junit4.DisplayName
@ -13,6 +14,8 @@ import org.junit.Test
@HiltAndroidTest
class DetailsTest : BaseTestCase() {
@AllureId("836")
@DisplayName("Details: (Wallet) fields")
@Test
fun walletWithoutBackupDetailsTest() =
setupHooks().run {
@ -60,7 +63,8 @@ class DetailsTest : BaseTestCase() {
}
}
// @Test
@DisplayName("Details: (Wallet 2.0) fields")
@Test
fun wallet2DetailsTest() =
setupHooks().run {
step("Open 'Main Screen'") {
@ -110,6 +114,8 @@ class DetailsTest : BaseTestCase() {
}
}
@AllureId("837")
@DisplayName("Details: (Note) fields")
@Test
fun noteDetailsTest() =
setupHooks().run {
@ -154,6 +160,76 @@ class DetailsTest : BaseTestCase() {
}
}
@AllureId("840")
@DisplayName("Details: (Twins) fields")
@Test
fun twinsDetailsTest() =
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen(productType = ProductType.Twins, isTwinsCard = true)
}
onMainScreenTopBar {
step("Open wallet details") {
moreButton.clickWithAssertion()
}
}
onDetailsScreen {
step("Assert 'Wallet connect' button does not exist") {
walletConnectButton.assertIsNotDisplayed()
}
step("Assert 'Buy Tangem card' button is visible") {
buyTangemButton.assertIsDisplayed()
}
step("Assert 'App settings' button is visible") {
appSettingsButton.assertIsDisplayed()
}
step("Assert 'Contact support' button is visible") {
contactSupportButton.assertIsDisplayed()
}
step("Assert 'Terms of service' button is visible") {
toSButton.assertIsDisplayed()
}
step("Assert app version is visible") {
versionName.assertIsDisplayed()
}
}
}
@AllureId("839")
@DisplayName("Details: (v4.12) fields")
@Test
fun firmware412DetailsTest() =
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen(mockContent = Firmware412MockContent)
}
onMainScreenTopBar {
step("Open wallet details") {
moreButton.clickWithAssertion()
}
}
onDetailsScreen {
step("Assert 'Wallet connect' button is visible") {
walletConnectButton.assertIsDisplayed()
}
step("Assert 'Buy Tangem card' button is visible") {
buyTangemButton.assertIsDisplayed()
}
step("Assert 'App settings' button is visible") {
appSettingsButton.assertIsDisplayed()
}
step("Assert 'Contact support' button is visible") {
contactSupportButton.assertIsDisplayed()
}
step("Assert 'Terms of service' button is visible") {
toSButton.assertIsDisplayed()
}
step("Assert app version is visible") {
versionName.assertIsDisplayed()
}
}
}
@AllureId("3647")
@DisplayName("Referral program: validate screen")
@Test

View file

@ -0,0 +1,55 @@
package com.tangem.tests
import com.tangem.common.BaseTestCase
import com.tangem.common.extensions.clickWithAssertion
import com.tangem.domain.models.scan.ProductType
import com.tangem.scenarios.openDeviceSettingsScreen
import com.tangem.scenarios.openMainScreen
import com.tangem.screens.*
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 SecurityModeTest : BaseTestCase() {
@AllureId("2267")
@DisplayName("Security Mode: Twin card opens the section")
@Test
fun twinSecurityModeOpensTest() =
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen(productType = ProductType.Twins, isTwinsCard = true)
}
openDeviceSettingsScreen()
step("Click on 'Scan card or ring' button") {
onDeviceSettingsScreen { scanCardOrRingButton.clickWithAssertion() }
}
step("Assert 'Security Mode' row is enabled") {
onDeviceSettingsScreen { securityModeRow.assertIsEnabled() }
}
step("Click on 'Security Mode' row") {
onDeviceSettingsScreen { securityModeRow.clickWithAssertion() }
}
step("Assert 'Security Mode' screen is displayed") {
onSecurityModeScreen { screenContainer.assertIsDisplayed() }
}
}
@DisplayName("Security Mode: other cards cannot open the section")
@Test
fun walletSecurityModeDisabledTest() =
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen()
}
openDeviceSettingsScreen()
step("Click on 'Scan card or ring' button") {
onDeviceSettingsScreen { scanCardOrRingButton.clickWithAssertion() }
}
step("Assert 'Security Mode' row is not clickable") {
onDeviceSettingsScreen { securityModeRow.assertIsNotEnabled() }
}
}
}

View file

@ -0,0 +1,56 @@
package com.tangem.tests
import com.tangem.common.BaseTestCase
import com.tangem.common.extensions.clickWithAssertion
import com.tangem.scenarios.openMainScreen
import com.tangem.screens.*
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 WalletRenameTest : BaseTestCase() {
@AllureId("2264")
@DisplayName("Wallet details: rename wallet")
@Test
fun renameWalletTest() =
setupHooks().run {
val newWalletName = "Tangem QA"
step("Open 'Main Screen'") {
openMainScreen()
}
step("Open wallet details") {
onMainScreenTopBar { moreButton.clickWithAssertion() }
}
step("Open 'Wallet settings' screen") {
onDetailsScreen { walletNameButton.clickWithAssertion() }
}
step("Click on 'Rename' button") {
onWalletSettingsScreen { renameWalletButton.clickWithAssertion() }
}
step("Enter new wallet name '$newWalletName'") {
onDialog { inputField.performTextReplacement(newWalletName) }
}
step("Click on 'OK' button") {
onDialog { okButton.clickWithAssertion() }
}
step("Assert new wallet name '$newWalletName' is displayed on 'Wallet settings' screen") {
onWalletSettingsScreen { walletNameValue(newWalletName).assertIsDisplayed() }
}
step("Click on 'Back' button") {
onWalletSettingsScreen { topAppBarBackButton.clickWithAssertion() }
}
step("Assert new wallet name '$newWalletName' is displayed on 'Details' screen") {
onDetailsScreen { walletNameValue(newWalletName).assertIsDisplayed() }
}
step("Click on 'Back' button") {
onDetailsScreen { topAppBarBackButton.clickWithAssertion() }
}
step("Assert new wallet name '$newWalletName' is displayed on 'Main' screen") {
onMainScreen { walletNameText.assertTextContains(newWalletName) }
}
}
}