Updated on 2026-08-14
This commit is contained in:
parent
1c5fa97fdf
commit
e5408a2ac7
23 changed files with 517 additions and 20 deletions
|
|
@ -2,4 +2,6 @@ package com.tangem.common.constants
|
|||
|
||||
object TestConstants {
|
||||
const val TOTAL_BALANCE = "$3,299.18"
|
||||
|
||||
const val WAIT_UNTIL_TIMEOUT = 20_000L
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.SelectNetworkFeeBottomSheetTestTags
|
||||
import com.tangem.core.ui.test.TopAppBarTestTags
|
||||
import com.tangem.wallet.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 androidx.compose.ui.test.hasText as withText
|
||||
|
||||
class SelectNetworkFeePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<SelectNetworkFeePageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TopAppBarTestTags.TITLE)
|
||||
hasText(getResourceString(R.string.common_fee_selector_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val marketSelectorItem: KNode = child {
|
||||
hasTestTag(SelectNetworkFeeBottomSheetTestTags.SELECTOR_ITEM)
|
||||
hasAnyChild(withText(getResourceString(R.string.common_fee_selector_option_market)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val fastSelectorItem: KNode = child {
|
||||
hasTestTag(SelectNetworkFeeBottomSheetTestTags.SELECTOR_ITEM)
|
||||
hasAnyChild(withText(getResourceString(R.string.common_fee_selector_option_fast)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val readMoreTextBlock: KNode = child {
|
||||
hasTestTag(SelectNetworkFeeBottomSheetTestTags.READ_MORE_TEXT)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSelectNetworkFeeBottomSheet(function: SelectNetworkFeePageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -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.SwapStoriesScreenTestTags
|
||||
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 SwapStoriesPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<SwapStoriesPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val closeButton: KNode = child {
|
||||
hasTestTag(SwapStoriesScreenTestTags.CLOSE_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSwapStoriesScreen(function: SwapStoriesPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
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 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.hasTestTag as withTestTag
|
||||
|
||||
class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<SwapTokenPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TopAppBarTestTags.TITLE)
|
||||
hasText(getResourceString(R.string.common_swap))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val closeButton: KNode = child {
|
||||
hasTestTag(TopAppBarTestTags.CLOSE_BUTTON)
|
||||
}
|
||||
|
||||
val textInput: KNode = child {
|
||||
hasParent(withTestTag(SwapTokenScreenTestTags.SWAP_TEXT_FIELD))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val networkFeeBlock: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.NETWORK_FEE_BLOCK)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val receiveAmountShimmer: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.RECEIVE_AMOUNT_SHIMMER)
|
||||
}
|
||||
|
||||
val swapTokensOnscreenButton: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.SWAP_BUTTON)
|
||||
}
|
||||
|
||||
val receiveAmount: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.RECEIVE_TEXT_FIELD)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val providersBlock: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.PROVIDERS_BLOCK)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val errorNotificationTitle: KNode = child {
|
||||
hasTestTag(NotificationTestTags.TITLE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val errorNotificationText: KNode = child {
|
||||
hasTestTag(NotificationTestTags.TEXT)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val refreshButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||
hasText(getResourceString(R.string.warning_button_refresh))
|
||||
}
|
||||
|
||||
val swapButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||
hasText(getResourceString(R.string.common_swap))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSwapTokenScreen(function: SwapTokenPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
package com.tangem.screens
|
||||
|
||||
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.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 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
|
||||
|
||||
class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TokenDetailsPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
|
@ -13,6 +20,41 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
|
|||
val screenContainer: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.SCREEN_CONTAINER)
|
||||
}
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.TOKEN_TITLE)
|
||||
}
|
||||
|
||||
private val horizontalActionChips = KLazyListNode(
|
||||
semanticsProvider = semanticsProvider,
|
||||
viewBuilderAction = { hasTestTag(TokenDetailsScreenTestTags.HORIZONTAL_ACTION_CHIPS) },
|
||||
itemTypeBuilder = { itemType(::LazyListItemNode) },
|
||||
positionMatcher = { position ->
|
||||
SemanticsMatcher.expectValue(
|
||||
LazyListItemPositionSemantics,
|
||||
position
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
val swapButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
|
||||
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
|
||||
hasText(getResourceString(R.string.common_swap))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
val sellButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
|
||||
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
|
||||
hasText(getResourceString(R.string.common_sell))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
val buyButton: LazyListItemNode = horizontalActionChips.childWith<LazyListItemNode> {
|
||||
hasTestTag(TokenDetailsScreenTestTags.ACTION_BUTTON)
|
||||
hasText(getResourceString(R.string.common_buy))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTokenDetailsScreen(function: TokenDetailsPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tests
|
|||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
|
|
@ -372,14 +373,14 @@ class BuyTokenTest : BaseTestCase() {
|
|||
}
|
||||
step("Assert unavailable provider name is displayed") {
|
||||
onSelectProviderBottomSheet {
|
||||
flakySafely(timeoutMs = 20_000) {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
unavailableProviderItem.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert available provider name is displayed") {
|
||||
onSelectProviderBottomSheet {
|
||||
flakySafely(timeoutMs = 20_000) {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
availableProviderItem.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
240
app/src/androidTest/kotlin/com/tangem/tests/SwapTokenTest.kt
Normal file
240
app/src/androidTest/kotlin/com/tangem/tests/SwapTokenTest.kt
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
package com.tangem.tests
|
||||
|
||||
import androidx.compose.ui.test.hasText
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.annotations.ApiEnv
|
||||
import com.tangem.common.annotations.ApiEnvConfig
|
||||
import com.tangem.common.constants.TestConstants.TOTAL_BALANCE
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
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 SwapTokenTest : BaseTestCase() {
|
||||
|
||||
@ApiEnv(
|
||||
ApiEnvConfig(ApiConfig.ID.Express, ApiEnvironment.PROD)
|
||||
)
|
||||
@AllureId("3546")
|
||||
@DisplayName("Swap: network fee")
|
||||
@Test
|
||||
fun networkFeeTest() {
|
||||
val inputAmount = "100"
|
||||
setupHooks().run {
|
||||
val tokenTitle = "Polygon"
|
||||
val balance = TOTAL_BALANCE
|
||||
|
||||
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("Click on token with name: '$tokenTitle'") {
|
||||
onTokenDetailsScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Swap' button") {
|
||||
onTokenDetailsScreen { swapButton.performClick() }
|
||||
}
|
||||
step("Close 'Stories' screen") {
|
||||
onSwapStoriesScreen { closeButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Swap' screen title is displayed") {
|
||||
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Close' button is displayed") {
|
||||
onSwapTokenScreen { closeButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert receive amount is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
receiveAmount.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Input swap amount = '$inputAmount'") {
|
||||
composeTestRule.waitForIdle()
|
||||
onSwapTokenScreen {
|
||||
textInput.clickWithAssertion()
|
||||
textInput.performTextReplacement(inputAmount)
|
||||
}
|
||||
}
|
||||
step("Assert input amount = '$inputAmount'") {
|
||||
onSwapTokenScreen { textInput.assertTextEquals(inputAmount) }
|
||||
}
|
||||
step("Assert 'Providers' block is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
providersBlock.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert 'Network fee' block is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
networkFeeBlock.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert receive amount is not equal to '0'") {
|
||||
onSwapTokenScreen { receiveAmount.assert(!hasText("0")) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("3549")
|
||||
@DisplayName("Swap: network error test")
|
||||
@Test
|
||||
fun networkErrorSwapTest() {
|
||||
setupHooks().run {
|
||||
val tokenTitle = "Polygon"
|
||||
val balance = TOTAL_BALANCE
|
||||
|
||||
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("Click on token with name: '$tokenTitle'") {
|
||||
onTokenDetailsScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Swap' button") {
|
||||
onTokenDetailsScreen { swapButton.performClick() }
|
||||
}
|
||||
step("Close 'Stories' screen") {
|
||||
onSwapStoriesScreen { closeButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Swap' screen title is displayed") {
|
||||
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert error notification title is displayed") {
|
||||
onSwapTokenScreen { errorNotificationTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert error notification text is displayed") {
|
||||
onSwapTokenScreen { errorNotificationText.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Refresh' button is displayed") {
|
||||
onSwapTokenScreen { refreshButton.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiEnv(
|
||||
ApiEnvConfig(ApiConfig.ID.Express, ApiEnvironment.PROD)
|
||||
)
|
||||
@AllureId("3546")
|
||||
@DisplayName("Swap: change network fee")
|
||||
@Test
|
||||
fun changeNetworkFeeTest() {
|
||||
val inputAmount = "100"
|
||||
setupHooks().run {
|
||||
val tokenTitle = "Polygon"
|
||||
val balance = TOTAL_BALANCE
|
||||
|
||||
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("Click on token with name: '$tokenTitle'") {
|
||||
onTokenDetailsScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Swap' button") {
|
||||
onTokenDetailsScreen { swapButton.performClick() }
|
||||
}
|
||||
step("Close 'Stories' screen") {
|
||||
onSwapStoriesScreen { closeButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Swap' screen title is displayed") {
|
||||
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert receive amount is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
receiveAmount.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Input swap amount = '$inputAmount'") {
|
||||
composeTestRule.waitForIdle()
|
||||
onSwapTokenScreen {
|
||||
textInput.clickWithAssertion()
|
||||
textInput.performTextReplacement(inputAmount)
|
||||
}
|
||||
}
|
||||
step("Assert input amount = '$inputAmount'") {
|
||||
onSwapTokenScreen { textInput.assertTextEquals(inputAmount) }
|
||||
}
|
||||
step("Click on 'Network fee' block") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
networkFeeBlock.clickWithAssertion()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert 'Select fee' bottom sheet title is displayed") {
|
||||
onSelectNetworkFeeBottomSheet { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Market' item is displayed") {
|
||||
onSelectNetworkFeeBottomSheet { marketSelectorItem.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Fast' item is displayed") {
|
||||
onSelectNetworkFeeBottomSheet { fastSelectorItem.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Read more' text block is displayed") {
|
||||
onSelectNetworkFeeBottomSheet { readMoreTextBlock.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Fast' item") {
|
||||
onSelectNetworkFeeBottomSheet { fastSelectorItem.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Network fee' block is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
networkFeeBlock.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue