Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-20 15:25:19 +03:00
parent 11eb1bbbdc
commit be5786260a
34 changed files with 873 additions and 48 deletions

View file

@ -349,7 +349,7 @@ dependencies {
/** Chucker */
debugImplementation(deps.chucker)
mockedImplementation(deps.chuckerStub)
mockedImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)

View file

@ -127,6 +127,7 @@ class ApiEnvironmentRule : TestRule {
ApiConfig.ID.TangemTech,
ApiConfig.ID.Express,
ApiConfig.ID.TangemPay,
ApiConfig.ID.StakeKit,
)
}
}

View file

@ -0,0 +1,117 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.*
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 com.tangem.features.staking.impl.R as StakingImplR
import androidx.compose.ui.test.hasTestTag as withTestTag
class StakingDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<StakingDetailsPageObject>(semanticsProvider = semanticsProvider) {
val screenContainer: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.SCREEN_CONTAINER)
}
val stakingTitle: KNode = child {
hasTestTag(TopAppBarTestTags.TITLE)
useUnmergedTree = true
}
val bannerImage: KNode = child {
hasTestTag(StakingDetailsScreenTestTags.BANNER_IMAGE)
useUnmergedTree = true
}
val bannerText: KNode = child {
hasTestTag(StakingDetailsScreenTestTags.BANNER_TEXT)
useUnmergedTree = true
}
val annualPercentageRate: KNode = child {
hasParent(withTestTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK))
hasTestTag(StakingDetailsScreenTestTags.PARAMETER_NAME)
hasText(getResourceString(StakingImplR.string.staking_details_annual_percentage_rate))
useUnmergedTree = true
}
val availableBlock: KNode = child {
hasParent(withTestTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK))
hasTestTag(StakingDetailsScreenTestTags.PARAMETER_NAME)
hasText(getResourceString(StakingImplR.string.staking_details_available))
useUnmergedTree = true
}
val unbondingPeriodBlock: KNode = child {
hasParent(withTestTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK))
hasTestTag(StakingDetailsScreenTestTags.PARAMETER_NAME)
hasText(getResourceString(StakingImplR.string.staking_details_unbonding_period))
useUnmergedTree = true
}
val rewardClaimingBlock: KNode = child {
hasParent(withTestTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK))
hasTestTag(StakingDetailsScreenTestTags.PARAMETER_NAME)
hasText(getResourceString(StakingImplR.string.staking_details_reward_claiming))
useUnmergedTree = true
}
val rewardScheduleBlock: KNode = child {
hasParent(withTestTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK))
hasTestTag(StakingDetailsScreenTestTags.PARAMETER_NAME)
hasText(getResourceString(StakingImplR.string.staking_details_reward_schedule))
useUnmergedTree = true
}
val rewardsBlock: KNode = child {
hasTestTag(BaseBlockTestTags.BLOCK)
useUnmergedTree = true
}
val rewardsBlockTitle: KNode = child {
hasTestTag(BaseBlockTestTags.BLOCK_TITLE)
useUnmergedTree = true
}
val rewardsBlockText: KNode = child {
hasTestTag(BaseBlockTestTags.BLOCK_TEXT)
useUnmergedTree = true
}
val yourStakesTitle: KNode = child {
hasText(getResourceString(StakingImplR.string.staking_your_stakes))
useUnmergedTree = true
}
val activeStakingBlock: KNode = child {
hasTestTag(StakingDetailsScreenTestTags.ACTIVE_STAKING_BLOCK)
useUnmergedTree = true
}
val toSText: KNode = child {
hasTestTag(StakingDetailsScreenTestTags.TOS_TEXT)
useUnmergedTree = true
}
val stakeMoreButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.staking_stake_more))
useUnmergedTree = true
}
val stakeButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_stake))
useUnmergedTree = true
}
}
internal fun BaseTestCase.onStakingDetailsScreen(function: StakingDetailsPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -0,0 +1,51 @@
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.BaseButtonTestTags
import com.tangem.core.ui.test.StakingSendDetailsScreenTestTags
import com.tangem.core.ui.test.TopAppBarTestTags
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 StakingSendDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<StakingSendDetailsPageObject>(semanticsProvider = semanticsProvider) {
val title: KNode = child {
hasTestTag(TopAppBarTestTags.TITLE)
useUnmergedTree = true
}
val primaryAmount: KNode = child {
hasTestTag(StakingSendDetailsScreenTestTags.PRIMARY_AMOUNT)
useUnmergedTree = true
}
val secondaryAmount: KNode = child {
hasTestTag(StakingSendDetailsScreenTestTags.SECONDARY_AMOUNT)
useUnmergedTree = true
}
val validatorBlock: KNode = child {
hasTestTag(StakingSendDetailsScreenTestTags.VALIDATOR_BLOCK)
useUnmergedTree = true
}
val networkFeeBlock: KNode = child {
hasTestTag(StakingSendDetailsScreenTestTags.NETWORK_FEE_BLOCK)
useUnmergedTree = true
}
val stakeButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_stake))
useUnmergedTree = true
}
}
internal fun BaseTestCase.onStakingSendDetailsScreen(function: StakingSendDetailsPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -0,0 +1,78 @@
package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase
import com.tangem.core.ui.test.BaseButtonTestTags
import com.tangem.core.ui.test.StakingSendScreenTestTags
import com.tangem.core.ui.test.TopAppBarTestTags
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 com.tangem.features.send.v2.impl.R as SendR
import androidx.compose.ui.test.hasTestTag as withTestTag
class StakingSendPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<StakingSendPageObject>(semanticsProvider = semanticsProvider) {
val screenContainer: KNode = child {
hasTestTag(StakingSendScreenTestTags.SCREEN_CONTAINER)
}
val title: KNode = child {
hasTestTag(TopAppBarTestTags.TITLE)
useUnmergedTree = true
}
val amountContainerTitle: KNode = child {
hasTestTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TITLE)
useUnmergedTree = true
}
val amountContainerText: KNode = child {
hasTestTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TEXT)
useUnmergedTree = true
}
val amountInputTextField: KNode = child {
hasTestTag(StakingSendScreenTestTags.INPUT_TEXT_FIELD)
useUnmergedTree = true
}
val secondaryAmount: KNode = child {
hasTestTag(StakingSendScreenTestTags.SECONDARY_AMOUNT)
useUnmergedTree = true
}
val currencyButton: KNode = child {
hasTestTag(StakingSendScreenTestTags.CURRENCY_BUTTON)
hasAnyChild(withTestTag(StakingSendScreenTestTags.CURRENCY_ICON))
useUnmergedTree = true
}
val fiatButton: KNode = child {
hasTestTag(StakingSendScreenTestTags.CURRENCY_BUTTON)
hasAnyChild(withTestTag(StakingSendScreenTestTags.FIAT_ICON))
useUnmergedTree = true
}
val maxButton: KNode = child {
hasTestTag(StakingSendScreenTestTags.MAX_BUTTON)
useUnmergedTree = true
}
val previousButton: KNode = child {
hasTestTag(StakingSendScreenTestTags.PREVIOUS_BUTTON)
useUnmergedTree = true
}
val nextButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(SendR.string.common_next))
useUnmergedTree = true
}
}
internal fun BaseTestCase.onStakingSendScreen(function: StakingSendPageObject.() -> Unit) =
onComposeScreen(composeTestRule, function)

View file

@ -3,10 +3,7 @@ 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.BaseButtonTestTags
import com.tangem.core.ui.test.NotificationTestTags
import com.tangem.core.ui.test.SwapTokenScreenTestTags
import com.tangem.core.ui.test.TopAppBarTestTags
import com.tangem.core.ui.test.*
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
@ -32,7 +29,7 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
}
val networkFeeBlock: KNode = child {
hasTestTag(SwapTokenScreenTestTags.NETWORK_FEE_BLOCK)
hasTestTag(BaseBlockTestTags.BLOCK)
useUnmergedTree = true
}

View file

@ -4,15 +4,16 @@ 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.TokenDetailsScreenTestTags
import com.tangem.core.ui.utils.LazyListItemPositionSemantics
import com.tangem.features.tokendetails.impl.R
import com.tangem.core.ui.utils.LazyListItemPositionSemantics
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 io.github.kakaocup.compose.node.element.lazylist.KLazyListNode
class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
ComposeScreen<TokenDetailsPageObject>(semanticsProvider = semanticsProvider) {
@ -21,6 +22,61 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
hasTestTag(TokenDetailsScreenTestTags.SCREEN_CONTAINER)
}
val availableStakingBlock: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_AVAILABLE_BLOCK)
useUnmergedTree = true
}
val stakingBlock: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_BLOCK)
useUnmergedTree = true
}
val availableStakingBlockTitle: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TITLE)
useUnmergedTree = true
}
val availableStakingBlockText: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TEXT)
useUnmergedTree = true
}
val availableStakingBlockCurrencyIcon: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_CURRENCY_ICON)
useUnmergedTree = true
}
val stakeButton: KNode = child {
hasTestTag(BaseButtonTestTags.TEXT)
hasText(getResourceString(R.string.common_stake))
useUnmergedTree = true
}
val stakingFiatAmount: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_FIAT_AMOUNT)
useUnmergedTree = true
}
val stakingDot: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_DOT)
useUnmergedTree = true
}
val stakingTokenAmount: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_TOKEN_AMOUNT)
useUnmergedTree = true
}
val stakingChevronIcon: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.STAKING_CHEVRON_ICON)
useUnmergedTree = true
}
val stakingTitle: KNode = child {
hasText(getResourceString(R.string.staking_native))
}
val title: KNode = child {
hasTestTag(TokenDetailsScreenTestTags.TOKEN_TITLE)
}

View file

@ -371,13 +371,6 @@ class BuyTokenTest : BaseTestCase() {
step("Open 'Select Provider' bottom sheet") {
onBuyTokenDetailsScreen { providerTitle.performClick() }
}
step("Assert unavailable provider name is displayed") {
onSelectProviderBottomSheet {
flakySafely(WAIT_UNTIL_TIMEOUT) {
unavailableProviderItem.assertIsDisplayed()
}
}
}
step("Assert available provider name is displayed") {
onSelectProviderBottomSheet {
flakySafely(WAIT_UNTIL_TIMEOUT) {

View file

@ -31,6 +31,7 @@ class OrganizeTokensTest : BaseTestCase() {
}
step("Swipe to 'Organize tokens' button") {
swipeUp()
swipeUp()
}
step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -55,6 +56,7 @@ class OrganizeTokensTest : BaseTestCase() {
}
step("Swipe to 'Organize tokens' button") {
swipeUp()
swipeUp()
}
step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -105,6 +107,7 @@ class OrganizeTokensTest : BaseTestCase() {
}
step("Swipe to 'Organize tokens' button") {
swipeUp()
swipeUp()
}
step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -140,6 +143,7 @@ class OrganizeTokensTest : BaseTestCase() {
}
step("Swipe to 'Organize tokens' button") {
swipeUp()
swipeUp()
}
step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() }
@ -192,6 +196,7 @@ class OrganizeTokensTest : BaseTestCase() {
}
step("Swipe to 'Organize tokens' button") {
swipeUp()
swipeUp()
}
step("Click 'Organize tokens' button") {
onMainScreen { organizeTokensButton().clickWithAssertion() }

View file

@ -0,0 +1,379 @@
package com.tangem.tests
import com.tangem.common.BaseTestCase
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.scenarios.OpenMainScreenScenario
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 StakingTest : BaseTestCase() {
@AllureId("3558")
@DisplayName("Staking: validate staking block on 'Token details' screen")
@Test
fun validateStakingBlockTest() {
val tokenTitle = "POL (ex-MATIC)"
val balance = TOTAL_BALANCE
val scenarioName = "staking_eth_pol_balances_android"
val scenarioState = "Staked"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
}
step("Open 'Main Screen'") {
scenario(OpenMainScreenScenario(composeTestRule))
}
step("Click on 'Synchronize addresses' button") {
onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
}
step("Assert wallet balance = $balance") {
onMainScreen { walletBalance().assertTextContains(balance) }
}
step("Click on token with name: '$tokenTitle'") {
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
}
step("Assert 'Token details screen' open") {
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
}
step("Assert 'Staking block' is displayed") {
onTokenDetailsScreen { stakingBlock.assertIsDisplayed() }
}
step("Assert 'Staking title' is displayed") {
onTokenDetailsScreen { stakingTitle.assertIsDisplayed() }
}
step("Assert 'Staking fiat amount' is displayed") {
onTokenDetailsScreen { stakingFiatAmount.assertIsDisplayed() }
}
step("Assert 'Staking dot' is displayed") {
onTokenDetailsScreen { stakingDot.assertIsDisplayed() }
}
step("Assert 'Staking token amount' is displayed") {
onTokenDetailsScreen { stakingTokenAmount.assertIsDisplayed() }
}
step("Assert 'Staking block chevron icon' is displayed") {
onTokenDetailsScreen { stakingChevronIcon.assertIsDisplayed() }
}
}
}
@AllureId("3550")
@DisplayName("Staking: validate staking more screens")
@Test
fun validateStakingMoreScreensTest() {
val tokenTitle = "POL (ex-MATIC)"
val balance = TOTAL_BALANCE
val scenarioName = "staking_eth_pol_balances_android"
val scenarioState = "Staked"
val stakingAmount = "1"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
}
step("Open 'Main Screen'") {
scenario(OpenMainScreenScenario(composeTestRule))
}
step("Click on 'Synchronize addresses' button") {
onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
}
step("Assert wallet balance = $balance") {
onMainScreen { walletBalance().assertTextContains(balance) }
}
step("Click on token with name: '$tokenTitle'") {
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
}
step("Assert 'Token details screen' open") {
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
}
step("Click on 'Staking block'") {
onTokenDetailsScreen { stakingBlock.clickWithAssertion() }
}
step("Assert 'Title' is displayed") {
onStakingDetailsScreen { stakingTitle.assertIsDisplayed() }
}
step("Assert 'Annual percentage rate' is displayed") {
onStakingDetailsScreen { annualPercentageRate.assertIsDisplayed() }
}
step("Assert 'Available' block is displayed") {
onStakingDetailsScreen { availableBlock.assertIsDisplayed() }
}
step("Assert 'Unbonding Period' block is displayed") {
onStakingDetailsScreen { unbondingPeriodBlock.assertIsDisplayed() }
}
step("Assert 'Reward claiming' block is displayed") {
onStakingDetailsScreen { rewardClaimingBlock.assertIsDisplayed() }
}
step("Assert 'Reward schedule' block is displayed") {
onStakingDetailsScreen { rewardScheduleBlock.assertIsDisplayed() }
}
step("Assert 'Rewards block' is displayed") {
onStakingDetailsScreen { rewardsBlock.assertIsDisplayed() }
}
step("Assert 'Rewards block' title is displayed") {
onStakingDetailsScreen { rewardsBlockTitle.assertIsDisplayed() }
}
step("Assert 'Rewards block' text is displayed") {
onStakingDetailsScreen { rewardsBlockText.assertIsDisplayed() }
}
step("Assert 'Active staking block' is displayed") {
onStakingDetailsScreen { activeStakingBlock.assertIsDisplayed() }
}
step("Assert 'Your stakes' title is displayed") {
onStakingDetailsScreen { yourStakesTitle.assertIsDisplayed() }
}
step("Assert 'ToS' text is displayed") {
onStakingDetailsScreen { toSText.assertIsDisplayed() }
}
step("Assert 'Stake more' button is displayed") {
onStakingDetailsScreen { stakeMoreButton.assertIsDisplayed() }
}
step("Click 'Stake more' button") {
onStakingDetailsScreen { stakeMoreButton.performClick() }
}
step("Assert 'Send' screen is displayed") {
onStakingSendScreen { screenContainer.assertIsDisplayed() }
}
step("Assert 'Send' screen title is displayed") {
onStakingSendScreen { title.assertIsDisplayed() }
}
step("Assert amount container title is displayed") {
onStakingSendScreen { amountContainerTitle.assertIsDisplayed() }
}
step("Assert amount container text is displayed") {
onStakingSendScreen { amountContainerText.assertIsDisplayed() }
}
step("Assert input text field is displayed") {
onStakingSendScreen { amountInputTextField.assertIsDisplayed() }
}
step("Assert secondary amount is displayed") {
onStakingSendScreen { secondaryAmount.assertIsDisplayed() }
}
step("Type '$stakingAmount' in input text field") {
onStakingSendScreen {
amountInputTextField.performClick()
amountInputTextField.performTextReplacement(stakingAmount)
}
}
step("Assert input text field has value: '$stakingAmount'") {
onStakingSendScreen { amountInputTextField.assertTextContains(value = stakingAmount, substring = true) }
}
step("Assert currency button is displayed") {
onStakingSendScreen { currencyButton.assertIsDisplayed() }
}
step("Assert fiat button is displayed") {
onStakingSendScreen { fiatButton.assertIsDisplayed() }
}
step("Assert currency button is displayed") {
onStakingSendScreen { currencyButton.assertIsDisplayed() }
}
step("Assert fiat button is displayed") {
onStakingSendScreen { fiatButton.assertIsDisplayed() }
}
step("Assert 'Max' button is displayed") {
onStakingSendScreen { maxButton.assertIsDisplayed() }
}
step("Assert previous button is displayed") {
onStakingSendScreen { previousButton.assertIsDisplayed() }
}
step("Assert 'Next' button is displayed") {
onStakingSendScreen { nextButton.assertIsDisplayed() }
}
step("Click on 'Next' button") {
onStakingSendScreen { nextButton.performClick() }
}
step("Assert 'Send details' screen title is displayed") {
onStakingSendDetailsScreen { title.assertIsDisplayed() }
}
step("Assert primary amount is displayed") {
onStakingSendDetailsScreen { primaryAmount.assertIsDisplayed() }
}
step("Assert secondary amount is displayed") {
onStakingSendDetailsScreen { secondaryAmount.assertIsDisplayed() }
}
step("Assert 'Validator' block is displayed") {
onStakingSendDetailsScreen { validatorBlock.assertIsDisplayed() }
}
step("Assert 'Network Fee' block is displayed") {
onStakingSendDetailsScreen { networkFeeBlock.assertIsDisplayed() }
}
step("Assert 'Stake' button is displayed") {
onStakingSendDetailsScreen { stakeButton.assertIsDisplayed() }
}
}
}
@AllureId("3548")
@DisplayName("Staking: validate staking screens")
@Test
fun validateStakingScreensTest() {
val tokenTitle = "POL (ex-MATIC)"
val balance = TOTAL_BALANCE
val scenarioName = "staking_eth_pol_balances_android"
val scenarioState = "Started"
val stakingAmount = "1"
setupHooks(
additionalAfterSection = {
resetWireMockScenarioState(scenarioName)
}
).run {
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
}
step("Open 'Main Screen'") {
scenario(OpenMainScreenScenario(composeTestRule))
}
step("Click on 'Synchronize addresses' button") {
onMainScreen { synchronizeAddressesButton.clickWithAssertion() }
}
step("Assert wallet balance = $balance") {
onMainScreen { walletBalance().assertTextContains(balance) }
}
step("Click on token with name: '$tokenTitle'") {
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
}
step("Assert 'Token details screen' open") {
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
}
step("Assert 'Available staking block' is displayed") {
onTokenDetailsScreen { availableStakingBlock.assertIsDisplayed() }
}
step("Assert 'Available staking block' title is displayed") {
onTokenDetailsScreen { availableStakingBlockTitle.assertIsDisplayed() }
}
step("Assert 'Available staking block' text is displayed") {
onTokenDetailsScreen { availableStakingBlockText.assertIsDisplayed() }
}
step("Assert 'Available staking block' currency icon is displayed") {
onTokenDetailsScreen { availableStakingBlockCurrencyIcon.assertIsDisplayed() }
}
step("Click on 'Stake' button") {
onTokenDetailsScreen { stakeButton.clickWithAssertion() }
}
step("Assert 'Title' is displayed") {
onStakingDetailsScreen { stakingTitle.assertIsDisplayed() }
}
step("Assert banner image is displayed") {
onStakingDetailsScreen { bannerImage.assertIsDisplayed() }
}
step("Assert banner text is displayed") {
onStakingDetailsScreen { bannerText.assertIsDisplayed() }
}
step("Assert 'Annual percentage rate' is displayed") {
onStakingDetailsScreen { annualPercentageRate.assertIsDisplayed() }
}
step("Assert 'Available' block is displayed") {
onStakingDetailsScreen { availableBlock.assertIsDisplayed() }
}
step("Assert 'Unbonding Period' block is displayed") {
onStakingDetailsScreen { unbondingPeriodBlock.assertIsDisplayed() }
}
step("Assert 'Reward claiming' block is displayed") {
onStakingDetailsScreen { rewardClaimingBlock.assertIsDisplayed() }
}
step("Assert 'Reward schedule' block is displayed") {
onStakingDetailsScreen { rewardScheduleBlock.assertIsDisplayed() }
}
step("Assert 'ToS' text is displayed") {
onStakingDetailsScreen { toSText.assertIsDisplayed() }
}
step("Assert 'Stake' button is displayed") {
onStakingDetailsScreen { stakeButton.assertIsDisplayed() }
}
step("Click 'Stake' button") {
onStakingDetailsScreen { stakeButton.performClick() }
}
step("Assert 'Send' screen is displayed") {
onStakingSendScreen { screenContainer.assertIsDisplayed() }
}
step("Assert 'Send' screen title is displayed") {
onStakingSendScreen { title.assertIsDisplayed() }
}
step("Assert amount container title is displayed") {
onStakingSendScreen { amountContainerTitle.assertIsDisplayed() }
}
step("Assert amount container text is displayed") {
onStakingSendScreen { amountContainerText.assertIsDisplayed() }
}
step("Assert input text field is displayed") {
onStakingSendScreen { amountInputTextField.assertIsDisplayed() }
}
step("Assert secondary amount is displayed") {
onStakingSendScreen { secondaryAmount.assertIsDisplayed() }
}
step("Type '$stakingAmount' in input text field") {
onStakingSendScreen {
amountInputTextField.performClick()
amountInputTextField.performTextReplacement(stakingAmount)
}
}
step("Assert input text field has value: '$stakingAmount'") {
onStakingSendScreen { amountInputTextField.assertTextContains(value = stakingAmount, substring = true) }
}
step("Assert currency button is displayed") {
onStakingSendScreen { currencyButton.assertIsDisplayed() }
}
step("Assert fiat button is displayed") {
onStakingSendScreen { fiatButton.assertIsDisplayed() }
}
step("Assert currency button is displayed") {
onStakingSendScreen { currencyButton.assertIsDisplayed() }
}
step("Assert fiat button is displayed") {
onStakingSendScreen { fiatButton.assertIsDisplayed() }
}
step("Assert 'Max' button is displayed") {
onStakingSendScreen { maxButton.assertIsDisplayed() }
}
step("Assert previous button is displayed") {
onStakingSendScreen { previousButton.assertIsDisplayed() }
}
step("Assert 'Next' button is displayed") {
onStakingSendScreen { nextButton.assertIsDisplayed() }
}
step("Click on 'Next' button") {
onStakingSendScreen { nextButton.performClick() }
}
step("Assert 'Send details' screen title is displayed") {
onStakingSendDetailsScreen { title.assertIsDisplayed() }
}
step("Assert primary amount is displayed") {
onStakingSendDetailsScreen { primaryAmount.assertIsDisplayed() }
}
step("Assert secondary amount is displayed") {
onStakingSendDetailsScreen { secondaryAmount.assertIsDisplayed() }
}
step("Assert 'Validator' block is displayed") {
onStakingSendDetailsScreen { validatorBlock.assertIsDisplayed() }
}
step("Assert 'Network Fee' block is displayed") {
onStakingSendDetailsScreen { networkFeeBlock.assertIsDisplayed() }
}
step("Assert 'Stake' button is displayed") {
onStakingSendDetailsScreen { stakeButton.assertIsDisplayed() }
}
}
}
}

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -25,6 +26,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingSendDetailsScreenTestTags
import java.math.BigDecimal
@Composable
@ -63,7 +65,8 @@ fun AmountBlock(amountState: AmountState, isClickDisabled: Boolean, isEditingDis
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing24),
.padding(top = TangemTheme.dimens.spacing24)
.testTag(StakingSendDetailsScreenTestTags.PRIMARY_AMOUNT),
)
Text(
text = secondAmount,
@ -72,7 +75,8 @@ fun AmountBlock(amountState: AmountState, isClickDisabled: Boolean, isEditingDis
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing8),
.padding(top = TangemTheme.dimens.spacing8)
.testTag(StakingSendDetailsScreenTestTags.SECONDARY_AMOUNT),
)
}
}

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.testTag
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig
@ -22,6 +23,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIcon
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingSendScreenTestTags
import kotlinx.collections.immutable.PersistentList
private const val AMOUNT_BUTTONS_KEY = "amountButtonsKey"
@ -77,7 +79,8 @@ internal fun LazyListScope.buttons(
.padding(
vertical = TangemTheme.dimens.spacing10,
horizontal = TangemTheme.dimens.spacing34,
),
)
.testTag(StakingSendScreenTestTags.MAX_BUTTON),
)
}
}
@ -90,7 +93,8 @@ private fun AmountCurrencyButton(button: AmountSegmentedButtonsConfig, isSegment
.fillMaxSize()
.padding(
horizontal = TangemTheme.dimens.spacing10,
),
)
.testTag(StakingSendScreenTestTags.CURRENCY_BUTTON),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
@ -102,13 +106,13 @@ private fun AmountCurrencyButton(button: AmountSegmentedButtonsConfig, isSegment
url = button.iconUrl,
size = TangemTheme.dimens.size18,
isGrayscale = !isSegmentedButtonsEnabled,
modifier = iconModifier,
modifier = iconModifier.testTag(StakingSendScreenTestTags.FIAT_ICON),
)
} else if (button.iconState != null) {
CurrencyIcon(
state = button.iconState,
shouldDisplayNetwork = false,
modifier = iconModifier,
modifier = iconModifier.testTag(StakingSendScreenTestTags.CURRENCY_ICON),
)
}
Text(

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
@ -28,6 +29,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingSendScreenTestTags
import com.tangem.core.ui.utils.rememberDecimalFormat
import kotlinx.coroutines.delay
@ -116,7 +118,8 @@ private fun AmountSecondary(amountField: AmountFieldModel, appCurrencyCode: Stri
textAlign = TextAlign.Center,
modifier = Modifier
.align(TopCenter)
.padding(bottom = TangemTheme.dimens.spacing32),
.padding(bottom = TangemTheme.dimens.spacing32)
.testTag(StakingSendScreenTestTags.SECONDARY_AMOUNT),
)
AmountFieldError(
isError = amountField.isError,

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.R
@ -29,6 +30,7 @@ import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingSendScreenTestTags
private const val AMOUNT_FIELD_KEY = "amountFieldKey"
@ -52,7 +54,8 @@ internal fun LazyListScope.amountField(
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing14),
.padding(top = TangemTheme.dimens.spacing14)
.testTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TITLE),
)
val balance = amountState.availableBalance.orMaskWithStars(isBalanceHidden).resolveReference()
@ -66,7 +69,8 @@ internal fun LazyListScope.amountField(
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing2),
.padding(top = TangemTheme.dimens.spacing2)
.testTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TEXT),
)
}
CurrencyIcon(

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.singleEvent
import com.tangem.core.ui.test.StakingSendScreenTestTags
@Composable
fun NavigationButtonsBlock(
@ -146,7 +148,8 @@ private fun PreviousButton(prevButton: NavigationButton?) {
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
.background(TangemTheme.colors.button.secondary)
.clickable(onClick = button.onClick)
.padding(TangemTheme.dimens.spacing12),
.padding(TangemTheme.dimens.spacing12)
.testTag(StakingSendScreenTestTags.PREVIOUS_BUTTON),
)
}
}

View file

@ -77,7 +77,7 @@ dependencies {
/** Chucker */
debugImplementation(deps.chucker)
mockedImplementation(deps.chuckerStub)
mockedImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)

View file

@ -1,5 +1,6 @@
package com.tangem.datasource.api.common.config
import com.tangem.datasource.BuildConfig
import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.utils.ProviderSuspend
@ -14,20 +15,44 @@ internal class StakeKit(
private val stakeKitAuthProvider: StakeKitAuthProvider,
) : ApiConfig() {
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
override val defaultEnvironment: ApiEnvironment = getInitialEnvironment()
override val environmentConfigs: List<ApiEnvironmentConfig> = listOf(
createProdEnvironment(),
createMockEnvironment(),
)
private fun getInitialEnvironment(): ApiEnvironment {
return when (BuildConfig.BUILD_TYPE) {
MOCKED_BUILD_TYPE,
-> ApiEnvironment.MOCK
DEBUG_BUILD_TYPE,
INTERNAL_BUILD_TYPE,
EXTERNAL_BUILD_TYPE,
RELEASE_BUILD_TYPE,
-> ApiEnvironment.PROD
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
}
}
private fun createProdEnvironment(): ApiEnvironmentConfig {
return ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://api.stakek.it/v1/",
headers = mapOf(
"X-API-KEY" to ProviderSuspend(stakeKitAuthProvider::getApiKey),
"accept" to ProviderSuspend { "application/json" },
),
headers = createHeaders(),
)
}
private fun createMockEnvironment(): ApiEnvironmentConfig {
return ApiEnvironmentConfig(
environment = ApiEnvironment.MOCK,
baseUrl = "[REDACTED_ENV_URL]",
headers = createHeaders(),
)
}
private fun createHeaders() = buildMap {
put(key = "X-API-KEY", value = ProviderSuspend(stakeKitAuthProvider::getApiKey))
put(key = "accept", value = ProviderSuspend { "application/json" })
}
}

View file

@ -7,7 +7,6 @@ import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.SwitchEnvironmentInterceptor
import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE
import com.tangem.datasource.api.common.config.ApiConfigs
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
@ -111,7 +110,7 @@ internal class RetrofitApiBuilder @Inject constructor(
apiConfigId: ApiConfig.ID,
environmentConfig: ApiEnvironmentConfig,
): OkHttpClient.Builder {
return if (BuildConfig.TESTER_MENU_ENABLED || BuildConfig.BUILD_TYPE == MOCKED_BUILD_TYPE) {
return if (BuildConfig.TESTER_MENU_ENABLED) {
addInterceptor(
interceptor = SwitchEnvironmentInterceptor(
id = apiConfigId,

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
import androidx.compose.ui.Alignment.Companion.TopStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
@ -23,6 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingSendScreenTestTags
import com.tangem.core.ui.utils.*
import java.math.BigDecimal
import java.text.DecimalFormat
@ -102,7 +104,9 @@ fun AmountTextField(
singleLine = true,
readOnly = !isEnabled,
visualTransformation = visualTransformation,
modifier = Modifier.background(backgroundColor),
modifier = Modifier
.background(backgroundColor)
.testTag(StakingSendScreenTestTags.INPUT_TEXT_FIELD),
)
}
}

View file

@ -27,7 +27,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.SwapTokenScreenTestTags
import com.tangem.core.ui.test.BaseBlockTestTags
/**
* [InputRowDefault](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=86eKp9izWxUvmoCq-4)
@ -67,20 +67,23 @@ fun InputRowDefault(
Column(
modifier = Modifier
.weight(1f)
.testTag(SwapTokenScreenTestTags.NETWORK_FEE_BLOCK),
.testTag(BaseBlockTestTags.BLOCK),
) {
title?.let {
Text(
text = title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = titleColor,
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing8),
modifier = Modifier
.padding(bottom = TangemTheme.dimens.spacing8)
.testTag(BaseBlockTestTags.BLOCK_TITLE),
)
}
Text(
text = text.resolveReference(),
style = TangemTheme.typography.body2,
color = textColor,
modifier = Modifier.testTag(BaseBlockTestTags.BLOCK_TEXT),
)
}
iconRes?.let {

View file

@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
@ -23,6 +24,7 @@ import com.tangem.core.ui.R
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
@Suppress("LongParameterList")
@Composable
@ -54,7 +56,8 @@ fun RoundableCornersRow(
.padding(
horizontal = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing12,
),
)
.testTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
@ -63,6 +66,7 @@ fun RoundableCornersRow(
color = startTextColor,
maxLines = 1,
style = startTextStyle,
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_NAME),
)
if (iconResId != null && iconClick != null) {
Icon(
@ -85,6 +89,7 @@ fun RoundableCornersRow(
color = endTextColor,
maxLines = 1,
style = endTextStyle,
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_VALUE),
)
}
}

View file

@ -0,0 +1,7 @@
package com.tangem.core.ui.test
object BaseBlockTestTags {
const val BLOCK = "BASE_BLOCK"
const val BLOCK_TITLE = "BASE_BLOCK_TITLE"
const val BLOCK_TEXT = "BASE_BLOCK_REWARDS_TEXT"
}

View file

@ -0,0 +1,15 @@
package com.tangem.core.ui.test
object StakingDetailsScreenTestTags {
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
const val BANNER_IMAGE = "TOKEN_DETAILS_SCREEN_BANNER_IMAGE"
const val BANNER_TEXT = "TOKEN_DETAILS_SCREEN_BANNER_TEXT"
const val PARAMETER_BLOCK = "STAKING_DETAILS_PARAMETER_BLOCK"
const val PARAMETER_NAME = "STAKING_DETAILS_PARAMETER_NAME"
const val PARAMETER_VALUE = "STAKING_DETAILS_PARAMETER_VALUE"
const val TOS_TEXT = "STAKING_DETAILS_TOS_TEXT"
const val ACTIVE_STAKING_BLOCK = "STAKING_DETAILS_ACTIVE_STAKING_BLOCK"
}

View file

@ -0,0 +1,10 @@
package com.tangem.core.ui.test
object StakingSendDetailsScreenTestTags {
const val PRIMARY_AMOUNT = "STAKING_SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
const val SECONDARY_AMOUNT = "TAKING_SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
const val VALIDATOR_BLOCK = "TAKING_SEND_DETAILS_SCREEN_VALIDATOR_BLOCK"
const val NETWORK_FEE_BLOCK = "TAKING_SEND_DETAILS_SCREEN_NETWORK_FEE_BLOCK"
}

View file

@ -0,0 +1,16 @@
package com.tangem.core.ui.test
object StakingSendScreenTestTags {
const val SCREEN_CONTAINER = "STAKING_SEND_SCREEN_CONTAINER"
const val AMOUNT_CONTAINER_TITLE = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TITLE"
const val AMOUNT_CONTAINER_TEXT = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TEXT"
const val INPUT_TEXT_FIELD = "STAKING_SEND_SCREEN_INPUT_TEXT_FIELD"
const val SECONDARY_AMOUNT = "STAKING_SEND_SCREEN_SECONDARY_AMOUNT"
const val CURRENCY_BUTTON = "STAKING_SEND_SCREEN_CURRENCY_BUTTON"
const val FIAT_ICON = "STAKING_SEND_SCREEN_FIAT_ICON"
const val CURRENCY_ICON = "STAKING_SEND_SCREEN_CURRENCY_ICON"
const val MAX_BUTTON = "STAKING_SEND_SCREEN_MAX_BUTTON"
const val PREVIOUS_BUTTON = "STAKING_SEND_SCREEN_PREVIOUS_BUTTON"
}

View file

@ -6,7 +6,6 @@ object SwapTokenScreenTestTags {
const val SWAP_TEXT_FIELD = "SWAP_TOKEN_SCREEN_SWAP_TEXT_FIELD"
const val RECEIVE_TEXT_FIELD = "SWAP_TOKEN_SCREEN_RECEIVE_TEXT_FIELD"
const val RECEIVE_AMOUNT_SHIMMER = "SWAP_TOKEN_SCREEN_RECEIVE_AMOUNT_SHIMMER"
const val NETWORK_FEE_BLOCK = "SWAP_TOKEN_SCREEN_NETWORK_FEE_BLOCK"
const val PROVIDERS_BLOCK = "SWAP_TOKEN_SCREEN_PROVIDERS_BLOCK"
const val SWAP_BUTTON = "SWAP_TOKEN_SCREEN_SWAP_BUTTON"
const val TOKEN = "SWAP_TOKEN_SCREEN_TOKEN"

View file

@ -2,7 +2,19 @@ package com.tangem.core.ui.test
object TokenDetailsScreenTestTags {
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
const val TOKEN_TITLE = "TOKEN_DETAILS_SCREEN_TOKEN_TITLE"
const val ACTION_BUTTON = "TOKEN_DETAILS_SCREEN_ACTION_BUTTON"
const val HORIZONTAL_ACTION_CHIPS = "TOKEN_DETAILS_SCREEN_HORIZONTAL_ACTION_CHIPS"
const val STAKING_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_BLOCK"
const val STAKING_AVAILABLE_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_AVAILABLE_BLOCK"
const val STAKING_CURRENCY_ICON = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_CURRENCY_ICON"
const val STAKING_SERVICE_TITLE = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TITLE"
const val STAKING_SERVICE_TEXT = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TEXT"
const val STAKING_FIAT_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_FIAT_AMOUNT"
const val STAKING_DOT = "TOKEN_DETAILS_SCREEN_STAKING_DOT"
const val STAKING_TOKEN_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_TOKEN_AMOUNT"
const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE"
const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON"
}

View file

@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
@ -42,6 +43,7 @@ import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.features.staking.impl.R
@ -171,7 +173,8 @@ private fun LazyListScope.activeStakingBlock(
currentIndex = index + 1,
lastIndex = state.yieldBalance.balances.lastIndex + 1,
addDefaultPadding = false,
),
)
.testTag(StakingDetailsScreenTestTags.ACTIVE_STAKING_BLOCK),
)
}
}
@ -189,7 +192,9 @@ private fun BannerBlock(onClick: () -> Unit) {
),
) {
Image(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.fillMaxSize()
.testTag(StakingDetailsScreenTestTags.BANNER_IMAGE),
contentScale = ContentScale.FillWidth,
painter = painterResource(R.drawable.img_staking_banner),
contentDescription = null,
@ -197,7 +202,8 @@ private fun BannerBlock(onClick: () -> Unit) {
Text(
modifier = Modifier
.align(Alignment.CenterStart)
.padding(TangemTheme.dimens.spacing16),
.padding(TangemTheme.dimens.spacing16)
.testTag(StakingDetailsScreenTestTags.BANNER_TEXT),
text = buildAnnotatedString {
withStyle(SpanStyle(Brush.linearGradient(textGradientColors))) {
append(stringResourceSafe(R.string.staking_details_banner_text))

View file

@ -11,6 +11,7 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import com.tangem.common.ui.amountScreen.AmountScreenContent
import com.tangem.common.ui.bottomsheet.permission.GiveTxPermissionBottomSheet
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
@ -20,6 +21,7 @@ import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingSendScreenTestTags
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.StakingStep
@ -44,7 +46,8 @@ internal fun StakingScreen(uiState: StakingUiState) {
.background(color = TangemTheme.colors.background.secondary)
.fillMaxSize()
.imePadding()
.systemBarsPadding(),
.systemBarsPadding()
.testTag(StakingSendScreenTestTags.SCREEN_CONTAINER),
horizontalAlignment = Alignment.CenterHorizontally,
) {
StakingAppBar(

View file

@ -2,11 +2,14 @@ package com.tangem.features.staking.impl.presentation.ui
import androidx.compose.foundation.text.ClickableText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.extensions.appendColored
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
import com.tangem.features.staking.impl.R
private const val TERMS_OF_USE_KEY = "termsOfUse"
@ -58,5 +61,6 @@ internal fun StakingTosText(onTextClick: (String) -> Unit) {
onTextClick(PRIVACY_POLICY_URL)
}
},
modifier = Modifier.testTag(StakingDetailsScreenTestTags.TOS_TEXT),
)
}

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -27,6 +28,7 @@ import com.tangem.core.ui.format.bigdecimal.fee
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingSendDetailsScreenTestTags
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.utils.StringsSigns.DASH_SIGN
@ -39,7 +41,8 @@ internal fun StakingFeeBlock(feeState: FeeState) {
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action)
.padding(TangemTheme.dimens.spacing12),
.padding(TangemTheme.dimens.spacing12)
.testTag(StakingSendDetailsScreenTestTags.NETWORK_FEE_BLOCK),
) {
Text(
text = stringResourceSafe(R.string.common_network_fee_title),

View file

@ -10,11 +10,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.StakingSendDetailsScreenTestTags
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.ui.ValidatorImagePlaceholder
@ -35,7 +37,8 @@ internal fun ValidatorBlock(validatorState: StakingStates.ValidatorState, isClic
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = onClick,
),
)
.testTag(StakingSendDetailsScreenTestTags.VALIDATOR_BLOCK),
) {
InputRowImageInfo(
title = resourceReference(R.string.staking_validator),

View file

@ -7,6 +7,7 @@ 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 androidx.compose.ui.tooling.preview.PreviewParameter
@ -17,6 +18,7 @@ import com.tangem.core.ui.extensions.resolveReference
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.TokenDetailsScreenTestTags
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingBalanceBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM
import com.tangem.features.tokendetails.impl.R
@ -30,7 +32,9 @@ internal fun StakingBalanceBlock(
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxWidth()
.testTag(TokenDetailsScreenTestTags.STAKING_BLOCK),
) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
@ -47,16 +51,19 @@ internal fun StakingBalanceBlock(
text = state.fiatValue.orMaskWithStars(isBalanceHidden).resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_FIAT_AMOUNT),
)
Text(
text = StringsSigns.DOT,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_DOT),
)
Text(
text = state.cryptoValue.orMaskWithStars(isBalanceHidden).resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_TOKEN_AMOUNT),
)
}
if (state.rewardValue != TextReference.EMPTY) {
@ -64,6 +71,7 @@ internal fun StakingBalanceBlock(
text = state.rewardValue.orMaskWithStars(isBalanceHidden).resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_REWARD_VALUE),
)
}
}
@ -71,6 +79,7 @@ internal fun StakingBalanceBlock(
painter = painterResource(id = R.drawable.ic_chevron_right_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_CHEVRON_ICON),
)
}
}

View file

@ -13,6 +13,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
@ -23,6 +24,7 @@ import com.tangem.core.ui.extensions.resolveReference
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.TokenDetailsScreenTestTags
import com.tangem.core.ui.utils.getGreyScaleColorFilter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingAvailableBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingBalanceBlock
@ -77,7 +79,9 @@ internal fun TokenStakingBlock(state: StakingBlockUM, isBalanceHidden: Boolean,
@Composable
private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxWidth()
.testTag(TokenDetailsScreenTestTags.STAKING_AVAILABLE_BLOCK),
) {
Row {
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
@ -87,7 +91,8 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi
modifier = Modifier
.size(TangemTheme.dimens.size20)
.clip(TangemTheme.shapes.roundedCorners8)
.align(Alignment.CenterVertically),
.align(Alignment.CenterVertically)
.testTag(TokenDetailsScreenTestTags.STAKING_CURRENCY_ICON),
icon = state.iconState,
alpha = alpha,
colorFilter = colorFilter,
@ -98,6 +103,7 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi
text = state.titleText.resolveReference(),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle2,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TITLE),
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
@ -106,6 +112,7 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi
text = state.subtitleText.resolveReference(),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TEXT),
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))