Updated on 2026-08-14
This commit is contained in:
parent
17df23f198
commit
bcee90926c
9 changed files with 339 additions and 13 deletions
|
|
@ -58,3 +58,12 @@ fun KNode.clickAndWaitFor(
|
|||
|
||||
throw AssertionError("Condition not met after $maxRetries click attempts")
|
||||
}
|
||||
|
||||
fun KNode.performTextInputInChunks(
|
||||
text: String,
|
||||
chunkSize: Int = 2
|
||||
) {
|
||||
text.chunked(chunkSize).forEach { chunk ->
|
||||
performTextInput(chunk)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
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 com.tangem.core.ui.test.BaseButtonTestTags
|
||||
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 AddTokenBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<AddTokenBottomSheetPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(BaseBottomSheetTestTags.TITLE)
|
||||
hasText(getResourceString(R.string.common_add_token))
|
||||
}
|
||||
|
||||
val addButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(R.string.common_add))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onAddTokenBottomSheet(function: AddTokenBottomSheetPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.BaseTestCase
|
|||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.test.AppBarWithSearchTestTags
|
||||
import com.tangem.core.ui.test.BuyTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.MarketsTestTags
|
||||
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
|
||||
|
|
@ -46,6 +47,13 @@ class SwapChooseTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProv
|
|||
}
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun marketsTokenWithTitle(title: String): KNode {
|
||||
return child {
|
||||
hasTestTag(MarketsTestTags.TOKENS_LIST_ITEM)
|
||||
hasText(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSwapChooseTokenScreen(function: SwapChooseTokenPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -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.SearchBarTestTags
|
||||
import com.tangem.core.ui.test.SwapSelectTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
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
|
||||
|
|
@ -48,6 +45,11 @@ class SwapSelectTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProv
|
|||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val searchBarBlock: KNode = child {
|
||||
hasTestTag(BaseSearchBarTestTags.SEARCH_BAR)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val searchBarIcon: KNode = child {
|
||||
hasTestTag(SearchBarTestTags.ICON)
|
||||
useUnmergedTree = true
|
||||
|
|
@ -58,12 +60,28 @@ class SwapSelectTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProv
|
|||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val tryAgainButton: KNode = child {
|
||||
hasText(getResourceString(R.string.alert_button_try_again))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val unableToLoadData: KNode = child {
|
||||
hasText(getResourceString(R.string.markets_loading_error_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun tokenWithName(tokenName: String): KNode = child {
|
||||
hasTestTag(TokenElementsTestTags.TOKEN_TITLE)
|
||||
hasAnyChild(withText(tokenName))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun marketsTokenWithName(title: String): KNode {
|
||||
return child {
|
||||
hasTestTag(MarketsTestTags.TOKENS_LIST_ITEM)
|
||||
hasText(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSwapSelectTokenScreen(function: SwapSelectTokenPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
|
|||
hasTestTag(SwapTokenScreenTestTags.RECEIVE_AMOUNT_SHIMMER)
|
||||
}
|
||||
|
||||
val swapTokensOnscreenButton: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.SWAP_BUTTON)
|
||||
val replaceTokensButton: KNode = child {
|
||||
hasTestTag(SwapTokenScreenTestTags.REPLACE_TOKENS_BUTTON)
|
||||
}
|
||||
|
||||
val receiveAmount: KNode = child {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,262 @@
|
|||
package com.tangem.tests.swap
|
||||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.R
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.performTextInputInChunks
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.SwapEntryPoint
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.openSwapScreen
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.*
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class SearchAndSwapTest : BaseTestCase() {
|
||||
|
||||
@AllureId("8520")
|
||||
@DisplayName("Search and Swap: add token without derivation")
|
||||
@Test
|
||||
fun addTokenWithoutDerivationTest() {
|
||||
val swapTokenName = "Ethereum"
|
||||
val receiveTokenName = "Tether"
|
||||
val swapTokenSymbol = "ETH"
|
||||
val receiveTokenSymbol = "USDT"
|
||||
|
||||
setupHooks().run {
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.MainScreen)
|
||||
}
|
||||
step("Click on token with name '$swapTokenName'") {
|
||||
onSwapSelectTokenScreen { tokenWithName(swapTokenName).performClick() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Click on 'Search' text field") {
|
||||
onSwapSelectTokenScreen { searchBarPlaceholderText.performClick() }
|
||||
}
|
||||
step("Type '$receiveTokenName' in input text field") {
|
||||
onSwapSelectTokenScreen { searchBarBlock.performTextInputInChunks(receiveTokenName) }
|
||||
}
|
||||
step("Click on token with name '$receiveTokenName'") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Add' button") {
|
||||
onAddTokenBottomSheet { addButton.performClick() }
|
||||
}
|
||||
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert receive token symbol: '$receiveTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { receiveTokenSymbol(receiveTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("8519")
|
||||
@DisplayName("Search and Swap: add token with derivation")
|
||||
@Test
|
||||
fun addTokenWithDerivationTest() {
|
||||
val swapTokenName = "Ethereum"
|
||||
val receiveTokenName = "TRON"
|
||||
val swapTokenSymbol = "ETH"
|
||||
val receiveTokenSymbol = "TRX"
|
||||
|
||||
setupHooks().run {
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.MainScreen)
|
||||
}
|
||||
step("Click on token with name '$swapTokenName'") {
|
||||
onSwapSelectTokenScreen { tokenWithName(swapTokenName).performClick() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Click on 'Search' text field") {
|
||||
onSwapSelectTokenScreen { searchBarPlaceholderText.performClick() }
|
||||
}
|
||||
step("Type '$receiveTokenSymbol' in input text field") {
|
||||
onSwapSelectTokenScreen { searchBarBlock.performTextInputInChunks(receiveTokenSymbol) }
|
||||
}
|
||||
step("Click on token with name '$receiveTokenName'") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Add' button") {
|
||||
onAddTokenBottomSheet { addButton.performClick() }
|
||||
}
|
||||
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert receive token symbol: '$receiveTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { receiveTokenSymbol(receiveTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("8523")
|
||||
@DisplayName("Search and Swap: Markets error")
|
||||
@Test
|
||||
fun marketsErrorTest() {
|
||||
val swapTokenName = "Ethereum"
|
||||
val scenarioName = "coins_list_api"
|
||||
val scenarioState = "Error"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(scenarioName)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(scenarioName, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.MainScreen)
|
||||
}
|
||||
step("Click on token with name '$swapTokenName'") {
|
||||
onSwapSelectTokenScreen { tokenWithName(swapTokenName).performClick() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert 'Unable to load data...' error is displayed") {
|
||||
onSwapSelectTokenScreen { unableToLoadData.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Try again' button is displayed") {
|
||||
onSwapSelectTokenScreen { tryAgainButton.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("8522")
|
||||
@DisplayName("Search and Swap: check 'Unsupported token pair' warning")
|
||||
@Test
|
||||
fun unsupportedTokenPairTest() {
|
||||
val swapTokenName = "Ethereum"
|
||||
val receiveTokenName = "Pepe"
|
||||
val warningTitle = getResourceString(R.string.warning_express_unsupported_pair_title)
|
||||
val warningMessage = getResourceString(R.string.warning_express_unsupported_pair_description)
|
||||
|
||||
setupHooks().run {
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.MainScreen)
|
||||
}
|
||||
step("Click on token with name '$swapTokenName'") {
|
||||
onSwapSelectTokenScreen { tokenWithName(swapTokenName).performClick() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Click on 'Search' text field") {
|
||||
onSwapSelectTokenScreen { searchBarPlaceholderText.performClick() }
|
||||
}
|
||||
step("Type '$receiveTokenName' in input text field") {
|
||||
onSwapSelectTokenScreen { searchBarBlock.performTextInputInChunks(receiveTokenName) }
|
||||
}
|
||||
step("Click on token with name '$receiveTokenName'") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
onSwapSelectTokenScreen { marketsTokenWithName(receiveTokenName).clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Add' button") {
|
||||
onAddTokenBottomSheet { addButton.performClick() }
|
||||
}
|
||||
step("Assert warning title '$warningTitle' is displayed") {
|
||||
onSwapTokenScreen { warningTitle(warningTitle).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert warning message '$warningMessage' is displayed") {
|
||||
onSwapTokenScreen { warningMessage(warningMessage).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert warning icon is displayed'") {
|
||||
onSwapTokenScreen { warningIcon(warningMessage).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("8521")
|
||||
@DisplayName("Swap: search token on Swap token screen")
|
||||
@Test
|
||||
fun networkFeeTest() {
|
||||
val tokenTitle = "Ethereum"
|
||||
val swapTokenSymbol = "TRX"
|
||||
val receiveTokenSymbol = "ETH"
|
||||
val swapTokenName = "TRON"
|
||||
|
||||
setupHooks().run {
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Open 'Swap' screen") {
|
||||
openSwapScreen(from = SwapEntryPoint.TokenDetails)
|
||||
}
|
||||
step("Click on 'Replace tokens' button") {
|
||||
onSwapTokenScreen { replaceTokensButton.performClick() }
|
||||
}
|
||||
step("Click on 'Select token' icon") {
|
||||
onSwapTokenScreen { selectTokenIcon.performClick() }
|
||||
}
|
||||
step("Click on 'Search' icon") {
|
||||
onSwapChooseTokenScreen { searchIcon.performClick() }
|
||||
}
|
||||
step("Click on 'Search' text field") {
|
||||
onSwapChooseTokenScreen { searchTextField.performClick() }
|
||||
}
|
||||
step("Type '$swapTokenSymbol' in 'Search' text field") {
|
||||
onSwapChooseTokenScreen { searchTextField.performTextInputInChunks(swapTokenSymbol) }
|
||||
}
|
||||
step("Click on token with name: '$swapTokenName'") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
onSwapChooseTokenScreen { marketsTokenWithTitle(swapTokenName).performClick() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Add' button") {
|
||||
onAddTokenBottomSheet { addButton.performClick() }
|
||||
}
|
||||
step("Assert swap token symbol: '$swapTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { swapTokenSymbol(swapTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert receive token symbol: '$receiveTokenSymbol' is displayed") {
|
||||
onSwapTokenScreen { receiveTokenSymbol(receiveTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ class SwapTokenScreenTest : BaseTestCase() {
|
|||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
replaceTokensButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ class SwapTokenScreenTest : BaseTestCase() {
|
|||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
replaceTokensButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -307,7 +307,7 @@ class SwapTokenScreenTest : BaseTestCase() {
|
|||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
replaceTokensButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@ class SwapTokenScreenTest : BaseTestCase() {
|
|||
step("Assert 'Swap tokens on screen' button is displayed") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
swapTokensOnscreenButton.assertIsDisplayed()
|
||||
replaceTokensButton.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,7 +442,7 @@ class SwapTokenScreenTest : BaseTestCase() {
|
|||
onSwapTokenScreen { receiveTokenSymbol(receiveTokenSymbol).assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Swap tokens on screen' button") {
|
||||
onSwapTokenScreen { swapTokensOnscreenButton.performClick() }
|
||||
onSwapTokenScreen { replaceTokensButton.performClick() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert new swap token symbol: '$receiveTokenSymbol' is displayed") {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ object SwapTokenScreenTestTags {
|
|||
const val RECEIVE_CARD = "SWAP_TOKEN_SCREEN_RECEIVE_CARD"
|
||||
const val RECEIVE_AMOUNT_SHIMMER = "SWAP_TOKEN_SCREEN_RECEIVE_AMOUNT_SHIMMER"
|
||||
const val PROVIDERS_BLOCK = "SWAP_TOKEN_SCREEN_PROVIDERS_BLOCK"
|
||||
const val SWAP_BUTTON = "SWAP_TOKEN_SCREEN_SWAP_BUTTON"
|
||||
const val REPLACE_TOKENS_BUTTON = "SWAP_TOKEN_SCREEN_REPLACE_TOKENS_BUTTON"
|
||||
const val TOKEN = "SWAP_TOKEN_SCREEN_TOKEN"
|
||||
const val TOKEN_SYMBOL = "SWAP_TOKEN_SCREEN_TOKEN_SYMBOL"
|
||||
const val TOKEN_ICON = "SWAP_TOKEN_SCREEN_TOKEN_ICON"
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) {
|
|||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
)
|
||||
.testTag(SwapTokenScreenTestTags.SWAP_BUTTON),
|
||||
.testTag(SwapTokenScreenTestTags.REPLACE_TOKENS_BUTTON),
|
||||
) {
|
||||
when (state.changeCardsButtonState) {
|
||||
ChangeCardsButtonState.UPDATE_IN_PROGRESS -> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue