Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-16 16:23:13 +05:00
parent 82117cec21
commit 99400590a6
24 changed files with 443 additions and 47 deletions

View file

@ -6,4 +6,6 @@ object TestConstants {
const val RECIPIENT_ADDRESS = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0" const val RECIPIENT_ADDRESS = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"
const val WAIT_UNTIL_TIMEOUT = 20_000L const val WAIT_UNTIL_TIMEOUT = 20_000L
const val MARKETS_MAIN_NETWORK_SUFFIX = "MAIN"
} }

View file

@ -1,11 +1,15 @@
package com.tangem.common.extensions package com.tangem.common.extensions
import androidx.test.uiautomator.By
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.wallet.R
import io.github.kakaocup.kakao.common.utilities.getResourceString
fun BaseTestCase.swipeUp( fun BaseTestCase.swipeVertical(
startHeightRatio: Float = 0.8f, direction: SwipeDirection,
endHeightRatio: Float = 0.03f, startHeightRatio: Float = if (direction == SwipeDirection.UP) 0.8f else 0.03f,
steps: Int = 15 endHeightRatio: Float = if (direction == SwipeDirection.UP) 0.03f else 0.8f,
steps: Int = 15,
) { ) {
device.uiDevice.swipe( device.uiDevice.swipe(
device.uiDevice.displayWidth / 2, device.uiDevice.displayWidth / 2,
@ -14,4 +18,41 @@ fun BaseTestCase.swipeUp(
(device.uiDevice.displayHeight * endHeightRatio).toInt(), (device.uiDevice.displayHeight * endHeightRatio).toInt(),
steps steps
) )
}
fun BaseTestCase.pullToRefresh() {
swipeVertical(
direction = SwipeDirection.DOWN,
startHeightRatio = 0.2f,
endHeightRatio = 0.8f,
steps = 2000
)
}
fun BaseTestCase.swipeMarketsBlock(direction: SwipeDirection) {
val searchBarText = device.uiDevice
.findObject(By.textContains(getResourceString(R.string.markets_search_header_title)))
val bounds = searchBarText.visibleBounds
val centerX = bounds.centerX()
val startY = bounds.centerY()
val endY = when (direction) {
SwipeDirection.UP -> 50
SwipeDirection.DOWN -> device.uiDevice.displayHeight - 100
}
device.uiDevice.swipe(centerX, startY, centerX, endY, 100)
}
fun BaseTestCase.openTheAppFromRecents() {
device.uiDevice.pressRecentApps()
val centerX = device.uiDevice.displayWidth / 2
val centerY = device.uiDevice.displayHeight / 3
device.uiDevice.click(centerX, centerY)
device.uiDevice.click(centerX, centerY)
}
enum class SwipeDirection {
UP, DOWN
} }

View file

@ -5,4 +5,5 @@ import io.github.kakaocup.compose.node.element.KNode
fun KNode.clickWithAssertion() { fun KNode.clickWithAssertion() {
assertIsDisplayed() assertIsDisplayed()
performClick() performClick()
} }

View file

@ -0,0 +1,22 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.R
import com.tangem.core.ui.test.BaseBottomSheetTestTags
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 BaseBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<BaseBottomSheetPageObject>(semanticsProvider = semanticsProvider) {
val hideButton: KNode = child {
hasText(getResourceString(R.string.token_details_hide_token))
hasTestTag(BaseBottomSheetTestTags.ACTION_TITLE)
}
}
internal fun BaseTestCase.onBottomSheet(function: BaseBottomSheetPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -31,6 +31,16 @@ class DialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
hasTestTag(BaseButtonTestTags.BUTTON) hasTestTag(BaseButtonTestTags.BUTTON)
hasText(getResourceString(R.string.common_confirm)) hasText(getResourceString(R.string.common_confirm))
} }
val continueButton: KNode = child {
hasTestTag(BaseButtonTestTags.BUTTON)
hasText(getResourceString(R.string.common_continue))
}
val okButton: KNode = child {
hasTestTag(BaseButtonTestTags.BUTTON)
hasText(getResourceString(R.string.common_ok))
}
} }
internal fun BaseTestCase.onDialog(function: DialogPageObject.() -> Unit) = internal fun BaseTestCase.onDialog(function: DialogPageObject.() -> Unit) =

View file

@ -0,0 +1,60 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.MARKETS_MAIN_NETWORK_SUFFIX
import com.tangem.common.utils.LazyListItemNode
import com.tangem.core.ui.test.BaseButtonTestTags
import com.tangem.core.ui.test.MarketsTestTags
import com.tangem.core.ui.test.TopAppBarTestTags
import com.tangem.core.ui.utils.LazyListItemPositionSemantics
import com.tangem.features.onramp.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.compose.node.element.lazylist.KLazyListNode
import io.github.kakaocup.kakao.common.utilities.getResourceString
import androidx.compose.ui.test.hasText as withText
class MarketsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<MarketsPageObject>(semanticsProvider = semanticsProvider) {
private val lazyList = KLazyListNode(
semanticsProvider = semanticsProvider,
viewBuilderAction = { hasTestTag(MarketsTestTags.TOKENS_LIST) },
itemTypeBuilder = { itemType(::LazyListItemNode) },
positionMatcher = { position ->
SemanticsMatcher.expectValue(
LazyListItemPositionSemantics,
position
)
}
)
val addToPortfolioButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_add_to_portfolio))
useUnmergedTree = true
}
val mainNetworkSwitch: KNode = child<KNode> {
hasAnyDescendant(withText(MARKETS_MAIN_NETWORK_SUFFIX))
useUnmergedTree = true
}.child { hasTestTag(MarketsTestTags.ADD_TO_PORTFOLIO_SWITCH) }
val topBarBackButton: KNode = child {
hasTestTag(TopAppBarTestTags.CLOSE_BUTTON)
useUnmergedTree = true
}
fun tokenWithTitle(title: String): KNode {
return lazyList.child<KNode> {
hasText(title)
useUnmergedTree = true
}
}
}
internal fun BaseTestCase.onMarketsScreen(function: MarketsPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

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.BaseSearchBarTestTags
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 SearchBarPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<SearchBarPageObject>(semanticsProvider = semanticsProvider) {
val searchField: KNode = child {
hasTestTag(BaseSearchBarTestTags.SEARCH_BAR)
}
}
internal fun BaseTestCase.onSearchBar(function: SearchBarPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -4,6 +4,7 @@ import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.utils.LazyListItemNode import com.tangem.common.utils.LazyListItemNode
import com.tangem.core.ui.test.BaseSearchBarTestTags
import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags
import com.tangem.core.ui.utils.LazyListItemPositionSemantics import com.tangem.core.ui.utils.LazyListItemPositionSemantics
import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.impl.R
@ -12,9 +13,8 @@ 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.KNode
import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode
import io.github.kakaocup.kakao.common.utilities.getResourceString 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.hasTestTag as withTestTag
import androidx.compose.ui.test.hasText as withText
class SelectCountryPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : class SelectCountryPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<SelectCountryPageObject>(semanticsProvider = semanticsProvider) { ComposeScreen<SelectCountryPageObject>(semanticsProvider = semanticsProvider) {
@ -33,7 +33,7 @@ class SelectCountryPageObject(semanticsProvider: SemanticsNodeInteractionsProvid
) )
val searchBar: KNode = child { val searchBar: KNode = child {
hasTestTag(SelectCountryBottomSheetTestTags.SEARCH_BAR) hasTestTag(BaseSearchBarTestTags.SEARCH_BAR)
useUnmergedTree = true useUnmergedTree = true
} }

View file

@ -3,9 +3,9 @@ package com.tangem.tests
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.screens.*
import com.tangem.scenarios.openMainScreen import com.tangem.scenarios.openMainScreen
import com.tangem.scenarios.synchronizeAddresses import com.tangem.scenarios.synchronizeAddresses
import com.tangem.screens.*
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
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
@ -33,7 +33,7 @@ class HideTokenTest : BaseTestCase() {
step("Assert 'Token details screen' open") { step("Assert 'Token details screen' open") {
onTokenDetailsScreen { screenContainer.assertIsDisplayed() } onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
} }
step("Click 'More button'") { step("Click 'More' button") {
onTokenDetailsTopBar { moreButton.clickWithAssertion() } onTokenDetailsTopBar { moreButton.clickWithAssertion() }
} }
step("Click 'Hide token' button") { step("Click 'Hide token' button") {

View file

@ -3,12 +3,13 @@ package com.tangem.tests
import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onAllNodesWithText
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
import com.tangem.common.extensions.SwipeDirection
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.swipeUp import com.tangem.common.extensions.swipeVertical
import com.tangem.screens.onMainScreen
import com.tangem.screens.onOrganizeTokensScreen
import com.tangem.scenarios.openMainScreen import com.tangem.scenarios.openMainScreen
import com.tangem.scenarios.synchronizeAddresses import com.tangem.scenarios.synchronizeAddresses
import com.tangem.screens.onMainScreen
import com.tangem.screens.onOrganizeTokensScreen
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
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
@ -28,12 +29,12 @@ class OrganizeTokensTest : BaseTestCase() {
step("Open 'Main Screen'") { step("Open 'Main Screen'") {
openMainScreen() openMainScreen()
} }
step("Click on 'Synchronize addresses' button" ) { step("Click on 'Synchronize addresses' button") {
onMainScreen { synchronizeAddressesButton.clickWithAssertion() } onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
} }
step("Swipe to 'Organize tokens' button") { step("Swipe to 'Organize tokens' button") {
swipeUp() swipeVertical(SwipeDirection.UP)
swipeUp() swipeVertical(SwipeDirection.UP)
} }
step("Click 'Organize tokens' button") { step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() } onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -57,8 +58,8 @@ class OrganizeTokensTest : BaseTestCase() {
onMainScreen { tokenNetworkGroupTitle(tokenNetwork).assertIsDisplayed() } onMainScreen { tokenNetworkGroupTitle(tokenNetwork).assertIsDisplayed() }
} }
step("Swipe to 'Organize tokens' button") { step("Swipe to 'Organize tokens' button") {
swipeUp() swipeVertical(SwipeDirection.UP)
swipeUp() swipeVertical(SwipeDirection.UP)
} }
step("Click 'Organize tokens' button") { step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() } onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -106,8 +107,8 @@ class OrganizeTokensTest : BaseTestCase() {
} }
} }
step("Swipe to 'Organize tokens' button") { step("Swipe to 'Organize tokens' button") {
swipeUp() swipeVertical(SwipeDirection.UP)
swipeUp() swipeVertical(SwipeDirection.UP)
} }
step("Click 'Organize tokens' button") { step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() } onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -133,7 +134,7 @@ class OrganizeTokensTest : BaseTestCase() {
step("Open 'Main Screen'") { step("Open 'Main Screen'") {
openMainScreen() openMainScreen()
} }
step("Click on 'Synchronize addresses' button" ) { step("Click on 'Synchronize addresses' button") {
onMainScreen { synchronizeAddressesButton.clickWithAssertion() } onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
} }
step("Check positions of tokens on 'Main Screen'") { step("Check positions of tokens on 'Main Screen'") {
@ -143,8 +144,8 @@ class OrganizeTokensTest : BaseTestCase() {
} }
} }
step("Swipe to 'Organize tokens' button") { step("Swipe to 'Organize tokens' button") {
swipeUp() swipeVertical(SwipeDirection.UP)
swipeUp() swipeVertical(SwipeDirection.UP)
} }
step("Click 'Organize tokens' button") { step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() } onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -194,8 +195,8 @@ class OrganizeTokensTest : BaseTestCase() {
} }
} }
step("Swipe to 'Organize tokens' button") { step("Swipe to 'Organize tokens' button") {
swipeUp() swipeVertical(SwipeDirection.UP)
swipeUp() swipeVertical(SwipeDirection.UP)
} }
step("Click 'Organize tokens' button") { step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() } onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -234,6 +235,4 @@ class OrganizeTokensTest : BaseTestCase() {
} }
} }
} }
} }

View file

@ -2,8 +2,9 @@ package com.tangem.tests
import androidx.test.InstrumentationRegistry.getTargetContext import androidx.test.InstrumentationRegistry.getTargetContext
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.extensions.SwipeDirection
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.swipeUp import com.tangem.common.extensions.swipeVertical
import com.tangem.screens.onDisclaimerScreen import com.tangem.screens.onDisclaimerScreen
import com.tangem.screens.onStoriesScreen import com.tangem.screens.onStoriesScreen
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
@ -37,7 +38,8 @@ class TermsOfServiceTest : BaseTestCase() {
step("Assert 'Stories' screen is opened") { step("Assert 'Stories' screen is opened") {
onStoriesScreen { onStoriesScreen {
scanButton.assertIsDisplayed() scanButton.assertIsDisplayed()
orderButton.assertIsDisplayed()} orderButton.assertIsDisplayed()
}
} }
} }
} }
@ -67,7 +69,7 @@ class TermsOfServiceTest : BaseTestCase() {
device.uiDevice.pressRecentApps() device.uiDevice.pressRecentApps()
} }
step("Stop app by swipe") { step("Stop app by swipe") {
swipeUp(startHeightRatio = 0.5f) swipeVertical(SwipeDirection.UP, startHeightRatio = 0.5f)
} }
step("Launch app") { step("Launch app") {
device.apps.launch(packageName) device.apps.launch(packageName)
@ -90,7 +92,7 @@ class TermsOfServiceTest : BaseTestCase() {
device.uiDevice.pressRecentApps() device.uiDevice.pressRecentApps()
} }
step("Stop app by swipe") { step("Stop app by swipe") {
swipeUp(startHeightRatio = 0.5f) swipeVertical(SwipeDirection.UP, startHeightRatio = 0.5f)
} }
step("Launch app") { step("Launch app") {
device.apps.launch(packageName) device.apps.launch(packageName)
@ -98,9 +100,9 @@ class TermsOfServiceTest : BaseTestCase() {
step("Assert 'Stories' screen is opened") { step("Assert 'Stories' screen is opened") {
onStoriesScreen { onStoriesScreen {
scanButton.assertIsDisplayed() scanButton.assertIsDisplayed()
orderButton.assertIsDisplayed()} orderButton.assertIsDisplayed()
}
} }
} }
} }
} }

View file

@ -2,11 +2,12 @@ package com.tangem.tests
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
import com.tangem.common.extensions.SwipeDirection
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.swipeUp import com.tangem.common.extensions.swipeVertical
import com.tangem.common.utils.getWcUri import com.tangem.common.utils.getWcUri
import com.tangem.screens.*
import com.tangem.scenarios.* import com.tangem.scenarios.*
import com.tangem.screens.*
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
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
@ -140,7 +141,7 @@ class WalletConnectTest : BaseTestCase() {
device.uiDevice.pressRecentApps() device.uiDevice.pressRecentApps()
} }
step("Stop app by swipe") { step("Stop app by swipe") {
swipeUp(startHeightRatio = 0.8f) swipeVertical(SwipeDirection.UP, startHeightRatio = 0.8f)
} }
step("Create WC session buy deeplink") { step("Create WC session buy deeplink") {
openAppByDeepLink(deepLinkUri) openAppByDeepLink(deepLinkUri)

View file

@ -0,0 +1,209 @@
package com.tangem.tests.balance
import androidx.compose.ui.test.longClick
import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
import com.tangem.common.extensions.*
import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.scenarios.openMainScreen
import com.tangem.scenarios.synchronizeAddresses
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 TotalBalanceUpdateTest : BaseTestCase() {
@Test
@AllureId("150")
@DisplayName("Total balance: check balance update after pull to refresh")
fun afterPullToRefreshTest() {
val scenarioName = "eth_network_balance"
val scenarioState = "Empty"
val updatedBalance = "$763.55"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses(TOTAL_BALANCE)
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
}
step("Perform pull to refresh") {
pullToRefresh()
}
step("Assert Total balance is updated from $TOTAL_BALANCE to $updatedBalance") {
onMainScreen { totalBalanceText.assertTextContains(updatedBalance) }
}
}
}
@Test
@AllureId("3997")
@DisplayName("Total balance: check balance update after token added")
fun afterAddTokenTest() {
val tokenTitle = "XRP"
val scenarioName = "quotes_api"
val scenarioState = "Ripple"
val updatedBalance = "$3,307.18"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses(TOTAL_BALANCE)
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
step("Open 'Markets screen'") {
swipeMarketsBlock(SwipeDirection.UP)
composeTestRule.waitForIdle()
}
step("Click on $tokenTitle token") {
onMarketsScreen { tokenWithTitle(tokenTitle).clickWithAssertion() }
}
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
}
step("Click on 'Add to portfolio' button") {
onMarketsScreen { addToPortfolioButton.clickWithAssertion() }
}
step("Toggle the main network switch") {
onMarketsScreen { mainNetworkSwitch.performClick() }
}
step("Click on 'Continue' button") {
onDialog { continueButton.clickWithAssertion() }
}
step("Assert 'Continue' is not displayed") {
onDialog { continueButton.assertIsNotDisplayed() }
}
step("Go back to 'Markets: tokens list'") {
composeTestRule.waitForIdle()
onMarketsScreen { topBarBackButton.clickWithAssertion() }
}
step("Close 'Markets screen'") {
onSearchBar { searchField.assertIsDisplayed() }
swipeMarketsBlock(SwipeDirection.DOWN)
}
step("Assert $updatedBalance is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(updatedBalance) }
}
}
}
@Test
@AllureId("4000")
@DisplayName("Total balance: check balance update after collapse/expand")
fun afterCollapseAndExpandTest() {
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses(TOTAL_BALANCE)
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
step("Press 'Home' to collapse the app") {
device.uiDevice.pressHome()
}
step("Open the app from recent apps") {
openTheAppFromRecents()
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
}
}
@Test
@AllureId("3998")
@DisplayName("Total balance: check balance update after hide token")
fun afterHideTokenTest() {
val tokenTitle = "Polygon"
val updatedBalance = "$3,114.34"
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses(TOTAL_BALANCE)
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
step("Long click on token with name: '$tokenTitle'") {
composeTestRule.waitForIdle()
onMainScreen {
tokenWithTitleAndAddress(tokenTitle).performTouchInput {
longClick(
position = center,
durationMillis = 1000L
)
}
}
}
step("Click 'Hide token' button") {
onBottomSheet { hideButton.clickWithAssertion() }
}
step("Click 'Hide' button in dialog") {
onDialog {
dialogContainer.assertIsDisplayed()
okButton.clickWithAssertion()
}
}
step("Assert $updatedBalance is displayed in total balance") {
swipeVertical(SwipeDirection.DOWN)
onMainScreen { totalBalanceText.assertTextContains(updatedBalance) }
}
}
}
@Test
@AllureId("4001")
@DisplayName("Total balance: check balance update after navigation")
fun afterNavigationTest() {
val tokenTitle = "Polygon"
setupHooks().run {
step("Open 'Main Screen'") {
openMainScreen()
}
step("Synchronize addresses") {
synchronizeAddresses(TOTAL_BALANCE)
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
step("Click on token with name: '$tokenTitle'") {
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
}
step("Assert 'Token details screen' open") {
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
}
step("Click 'More' button") {
onTokenDetailsTopBar { backButton.clickWithAssertion() }
}
step("Assert $TOTAL_BALANCE is displayed in total balance") {
onMainScreen { totalBalanceText.assertTextContains(TOTAL_BALANCE) }
}
}
}
}

View file

@ -198,6 +198,13 @@ object WalletMockContent : MockContent {
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey( // xrp
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),

View file

@ -11,8 +11,10 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.BaseBottomSheetTestTags
/** /**
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=281-248&mode=design&t=bXqehWPHyATKcZEW-4) * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=281-248&mode=design&t=bXqehWPHyATKcZEW-4)
@ -38,7 +40,7 @@ fun SimpleSettingsRow(
onItemsClick() onItemsClick()
} }
}, },
), ).testTag(BaseBottomSheetTestTags.ACTION_TITLE),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {

View file

@ -15,10 +15,12 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.MarketsTestTags
@Suppress("MagicNumber") @Suppress("MagicNumber")
@Composable @Composable
@ -56,7 +58,7 @@ fun TangemSwitch(
onClick = { onClick = {
onCheckedChange(!checked) onCheckedChange(!checked)
}, },
), ).testTag(MarketsTestTags.ADD_TO_PORTFOLIO_SWITCH),
) { ) {
BoxWithConstraints( BoxWithConstraints(
modifier = Modifier modifier = Modifier

View file

@ -38,7 +38,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags import com.tangem.core.ui.test.BaseSearchBarTestTags
@Composable @Composable
fun SearchBar( fun SearchBar(
@ -64,7 +64,7 @@ fun SearchBar(
} }
} }
.focusRequester(focusRequester) .focusRequester(focusRequester)
.testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR), .testTag(BaseSearchBarTestTags.SEARCH_BAR),
enabled = enabled, enabled = enabled,
value = state.query, value = state.query,
onValueChange = state.onQueryChange, onValueChange = state.onQueryChange,

View file

@ -0,0 +1,5 @@
package com.tangem.core.ui.test
object BaseBottomSheetTestTags {
const val ACTION_TITLE = "BASE_BOTTOM_SHEET_ACTION_TITLE"
}

View file

@ -0,0 +1,5 @@
package com.tangem.core.ui.test
object BaseSearchBarTestTags {
const val SEARCH_BAR = "BASE_SEARCH_BAR"
}

View file

@ -0,0 +1,7 @@
package com.tangem.core.ui.test
object MarketsTestTags {
const val TOKENS_LIST = "MARKETS_TOKENS_LIST"
const val TOKENS_LIST_ITEM = "MARKETS_TOKENS_LIST_ITEM"
const val ADD_TO_PORTFOLIO_SWITCH = "MARKETS_ADD_TO_PORTFOLIO_SWITCH"
}

View file

@ -5,7 +5,6 @@ object SelectCountryBottomSheetTestTags {
const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST" const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST"
const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM" const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM"
const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM" const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM"
const val SEARCH_BAR = "SELECT_COUNTRY_BOTTOM_SHEET_SEARCH_BAR"
const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON" const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON"
const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON" const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON"
const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME" const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME"

View file

@ -26,8 +26,8 @@ import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.core.ui.components.* import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.block.TangemBlockCardColors import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.block.information.InformationBlock import com.tangem.core.ui.components.block.information.InformationBlock
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.buttons.common.TangemButton import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonSize import com.tangem.core.ui.components.buttons.common.TangemButtonSize
@ -228,9 +228,7 @@ private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifi
isLastItem = index == state.networks.lastIndex, isLastItem = index == state.networks.lastIndex,
content = { content = {
BlockchainRow( BlockchainRow(
modifier = Modifier.padding( modifier = Modifier.padding(end = TangemTheme.dimens.spacing4),
end = TangemTheme.dimens.spacing4,
),
model = network, model = network,
action = { action = {
TangemSwitch( TangemSwitch(

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
@ -28,6 +29,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.LocalWindowSize import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.MarketsTestTags
import com.tangem.core.ui.windowsize.WindowSizeType import com.tangem.core.ui.windowsize.WindowSizeType
import com.tangem.features.markets.impl.R import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider
@ -41,7 +43,8 @@ internal fun MarketsListItem(model: MarketsListItemUM, modifier: Modifier = Modi
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RectangleShape) .clip(RectangleShape)
.clickable(onClick = onClick), .clickable(onClick = onClick)
.testTag(MarketsTestTags.TOKENS_LIST_ITEM),
model = model, model = model,
) )
} }

View file

@ -10,6 +10,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import com.tangem.core.ui.components.UnableToLoadData import com.tangem.core.ui.components.UnableToLoadData
import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig import com.tangem.core.ui.components.buttons.SmallButtonConfig
@ -18,6 +19,7 @@ import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.MarketsTestTags
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.markets.impl.R import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
@ -65,7 +67,7 @@ internal fun MarketsListLazyColumn(
} }
} else { } else {
LazyColumn( LazyColumn(
modifier = modifier, modifier = modifier.testTag(MarketsTestTags.TOKENS_LIST),
state = lazyListState, state = lazyListState,
contentPadding = PaddingValues(bottom = bottomBarHeight), contentPadding = PaddingValues(bottom = bottomBarHeight),
userScrollEnabled = true, userScrollEnabled = true,