Updated on 2026-08-14
This commit is contained in:
parent
de41c92d35
commit
392234221b
4 changed files with 46 additions and 55 deletions
|
|
@ -1,47 +1,15 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import androidx.compose.ui.semantics.SemanticsActions
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.hasAnyDescendant
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performSemanticsAction
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.compose.ui.test.swipeLeft
|
||||
import androidx.compose.ui.test.swipeRight
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.isDisplayedSafely
|
||||
import com.tangem.core.ui.test.BuyTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.DetailsScreenTestTags
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
// Custom/nested clickables (Add-Wallet row, wallet tab) reject touch injection — drive OnClick directly.
|
||||
private fun BaseTestCase.clickViaSemantics(matcher: SemanticsMatcher, useUnmergedTree: Boolean = false) {
|
||||
composeTestRule.onNode(matcher, useUnmergedTree).performSemanticsAction(SemanticsActions.OnClick)
|
||||
}
|
||||
|
||||
// Both pager pages stay mounted; the off-screen one ignores swipes, so always swipe the on-screen wallet card.
|
||||
private fun BaseTestCase.swipeDisplayedWallet(toPrevious: Boolean) {
|
||||
val nodes = composeTestRule.onAllNodes(hasTestTag(MainScreenTestTags.WALLET_LIST_ITEM))
|
||||
for (i in 0 until nodes.fetchSemanticsNodes().size) {
|
||||
val swiped = runCatching {
|
||||
nodes[i].assertIsDisplayed()
|
||||
nodes[i].performTouchInput { if (toPrevious) swipeRight() else swipeLeft() }
|
||||
}.isSuccess
|
||||
if (swiped) break
|
||||
}
|
||||
waitForIdle()
|
||||
}
|
||||
|
||||
/** 'Add Wallet' scans a card immediately (no type chooser), so [mockContent] must be set before the click. */
|
||||
fun BaseTestCase.addNewCardWallet(mockContent: MockContent) {
|
||||
step("Click 'More' button on TopBar") {
|
||||
|
|
@ -49,7 +17,7 @@ fun BaseTestCase.addNewCardWallet(mockContent: MockContent) {
|
|||
}
|
||||
MockProvider.setMocks(mockContent)
|
||||
step("Click on 'Add Wallet' button (scans a new hardware wallet)") {
|
||||
clickViaSemantics(hasTestTag(DetailsScreenTestTags.ADD_WALLET_BUTTON))
|
||||
onDetailsScreen { addWalletButton.clickWithAssertion() }
|
||||
}
|
||||
// Gate on the top-bar More button, not the container — the bottom Markets sheet can leave the container un-"displayed".
|
||||
step("Assert 'Main' screen is displayed with the new wallet") {
|
||||
|
|
@ -62,7 +30,7 @@ fun BaseTestCase.addNewCardWallet(mockContent: MockContent) {
|
|||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
var shown = false
|
||||
onMainScreen { shown = synchronizeAddressesButton.isDisplayedSafely() }
|
||||
if (!shown) swipeDisplayedWallet(toPrevious = false)
|
||||
if (!shown) onMainScreen { swipeToAdjacentWallet(toPrevious = false) }
|
||||
shown
|
||||
}
|
||||
// Let the pager fling settle — a click mid-animation is eaten by the button's clickableSingle debounce.
|
||||
|
|
@ -77,25 +45,15 @@ fun BaseTestCase.addNewCardWallet(mockContent: MockContent) {
|
|||
}
|
||||
}
|
||||
|
||||
/** Both pager pages mount the same token; clicks the [tokenName] copy currently on-screen. */
|
||||
fun BaseTestCase.clickDisplayedTokenOnMain(tokenName: String) {
|
||||
val matcher = hasTestTag(MainScreenTestTags.TOKEN_LIST_ITEM) and hasAnyDescendant(hasText(tokenName))
|
||||
step("Click on token '$tokenName' on the visible wallet") {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
val nodes = composeTestRule.onAllNodes(matcher, useUnmergedTree = true)
|
||||
(0 until nodes.fetchSemanticsNodes().size).any { i ->
|
||||
runCatching {
|
||||
nodes[i].assertIsDisplayed()
|
||||
nodes[i].performClick()
|
||||
}.isSuccess
|
||||
}
|
||||
}
|
||||
onMainScreen { clickDisplayedToken(tokenName) }
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.switchToPreviousWallet() {
|
||||
step("Swipe wallet card to the previous wallet") {
|
||||
swipeDisplayedWallet(toPrevious = true)
|
||||
onMainScreen { swipeToAdjacentWallet(toPrevious = true) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,18 +62,11 @@ fun BaseTestCase.selectReceiveTokenOnWallet(token: String, walletName: String) {
|
|||
step("Click on 'Choose token' button") {
|
||||
onSwapTokenScreen { chooseTokenButton.performClick() }
|
||||
}
|
||||
// The wallet tab is a custom clickable Row that Kakao's performClick can't hit (autoscroll fails); invoke OnClick directly.
|
||||
step("Select wallet tab '$walletName'") {
|
||||
clickViaSemantics(
|
||||
hasTestTag(BuyTokenScreenTestTags.WALLET_TAB) and hasAnyDescendant(hasText(walletName)),
|
||||
useUnmergedTree = true,
|
||||
)
|
||||
onBuyTokenScreen { walletTab(walletName).performClick() }
|
||||
}
|
||||
step("Click on token with name '$token'") {
|
||||
clickViaSemantics(
|
||||
hasTestTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM) and hasAnyDescendant(hasText(token)),
|
||||
useUnmergedTree = true,
|
||||
)
|
||||
onBuyTokenScreen { tokenWithTitle(token).performClick() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ 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.lazylist.KLazyListNode
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import androidx.compose.ui.test.hasText as withText
|
||||
|
||||
class BuyTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<BuyTokenPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
|
@ -48,6 +49,19 @@ class BuyTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
|||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
fun walletTab(walletName: String): KNode = child {
|
||||
hasTestTag(BuyTokenScreenTestTags.WALLET_TAB)
|
||||
hasAnyDescendant(withText(walletName))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
fun tokenWithTitle(tokenTitle: String): LazyListItemNode = lazyList.childWith<LazyListItemNode> {
|
||||
hasTestTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM)
|
||||
hasText(tokenTitle)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onBuyTokenScreen(function: BuyTokenPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
|||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val addWalletButton: KNode = child {
|
||||
hasTestTag(DetailsScreenTestTags.ADD_WALLET_BUTTON)
|
||||
}
|
||||
|
||||
val buyTangemButton: KNode = child {
|
||||
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
|
||||
hasText(getResourceString(R.string.details_buy_wallet))
|
||||
|
|
|
|||
|
|
@ -111,6 +111,28 @@ class MainScreenPageObject(private val semanticsProvider: SemanticsNodeInteracti
|
|||
)
|
||||
}
|
||||
|
||||
// Wallet pager keeps the adjacent page composed (beyondViewportPageCount=1), so the token is mounted on two pages — click the displayed copy.
|
||||
fun clickDisplayedToken(tokenName: String) {
|
||||
val matcher = withTestTag(MainScreenTestTags.TOKEN_LIST_ITEM) and hasAnyDescendant(withText(tokenName))
|
||||
val nodes = semanticsProvider.onAllNodes(matcher, useUnmergedTree = true)
|
||||
for (i in 0 until nodes.fetchSemanticsNodes().size) {
|
||||
if (runCatching { nodes[i].assertIsDisplayed(); nodes[i].performClick() }.isSuccess) return
|
||||
}
|
||||
error("Token '$tokenName' is not displayed on the current wallet page")
|
||||
}
|
||||
|
||||
// Adjacent pager pages stay mounted; swipe the wallet card that's actually on-screen.
|
||||
fun swipeToAdjacentWallet(toPrevious: Boolean) {
|
||||
val nodes = semanticsProvider.onAllNodes(withTestTag(MainScreenTestTags.WALLET_LIST_ITEM))
|
||||
for (i in 0 until nodes.fetchSemanticsNodes().size) {
|
||||
val swiped = runCatching {
|
||||
nodes[i].assertIsDisplayed()
|
||||
nodes[i].performTouchInput { if (toPrevious) swipeRight() else swipeLeft() }
|
||||
}.isSuccess
|
||||
if (swiped) return
|
||||
}
|
||||
}
|
||||
|
||||
val restoringProgressText: KNode = child {
|
||||
hasTestTag(MainScreenTestTags.SYNC_PROGRESS_TEXT)
|
||||
useUnmergedTree = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue