From 5d21b088c49bda0640b13f05cb50db3d646ce383 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Oct 2025 15:30:14 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/common/utils/ClipboardUtils.kt | 20 ++ .../com/tangem/scenarios/BaseScenarios.kt | 9 + .../scenarios/WalletConnectScenarios.kt | 71 ++++++-- .../WalletConnectBottonSheetPageObject.kt | 2 - .../tangem/screens/WalletConnectPageObject.kt | 15 ++ .../screens/WalletConnectScanQrPageObject.kt | 23 +++ .../com/tangem/tests/WalletConnectTest.kt | 171 +++++++++++++----- .../ui/test/WalletConnectScreenTestTags.kt | 1 + .../connections/ui/WcConnectionsContent.kt | 4 +- 9 files changed, 243 insertions(+), 73 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/WalletConnectScanQrPageObject.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt b/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt new file mode 100644 index 0000000000..f4648d1cc9 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt @@ -0,0 +1,20 @@ +package com.tangem.common.utils + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context + +fun getClipboardText(context: Context): String? { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + return if (clipboard.hasPrimaryClip()) { + clipboard.primaryClip?.getItemAt(0)?.text?.toString() + } else { + null + } +} + +fun setClipboardText(context: Context, text: String?) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("label", text) + clipboard.setPrimaryClip(clip) +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt index 3212a90432..d07178f3cb 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt @@ -85,4 +85,13 @@ fun BaseTestCase.openDeviceSettingsScreen() { step("Click on 'Device settings' button") { onWalletSettingsScreen { deviceSettingsButton.clickWithAssertion() } } +} + +fun BaseTestCase.openWalletConnectScreen() { + step("Click 'More' button on TopBar") { + onTopBar { moreButton.clickWithAssertion() } + } + step("Click on 'Wallet Connect' button") { + onDetailsScreen { walletConnectButton.clickWithAssertion() } + } } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/WalletConnectScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/WalletConnectScenarios.kt index 107806004a..facffbfd2d 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/WalletConnectScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/WalletConnectScenarios.kt @@ -7,6 +7,7 @@ import com.tangem.screens.onWalletConnectScreen import io.qameta.allure.kotlin.Allure.step fun BaseTestCase.checkWalletConnectBottomSheet() { + waitForIdle() step("Assert 'Wallet Connect' bottom sheet title is displayed") { onWalletConnectBottomSheet { title.assertIsDisplayed() } } @@ -60,34 +61,64 @@ fun BaseTestCase.checkWalletConnectBottomSheet() { } } -fun BaseTestCase.checkWalletConnectScreen() { +fun BaseTestCase.checkWalletConnectScreen(withConnections: Boolean) { + waitForIdle() step("Assert 'Wallet Connect' title is displayed") { onWalletConnectScreen { title.assertIsDisplayed() } } - step("Assert 'More' button is displayed") { - onWalletConnectScreen { moreButton.assertIsDisplayed() } - } - step("Assert wallet name is displayed") { - onWalletConnectScreen { walletName.assertIsDisplayed() } - } - step("Assert app icon is displayed") { - onWalletConnectScreen { appIcon.assertIsDisplayed() } - } - step("Assert app name is displayed") { - onWalletConnectScreen { appName.assertIsDisplayed() } - } - step("Assert approve icon is displayed") { - onWalletConnectScreen { approveIcon.assertIsDisplayed() } - } - step("Assert app URL is displayed") { - onWalletConnectScreen { appUrl.assertIsDisplayed() } - } step("Assert 'New Connection' button is displayed") { onWalletConnectScreen { newConnectionButton.assertIsDisplayed() } } + if (withConnections) { + step("Assert 'More' button is displayed") { + onWalletConnectScreen { moreButton.assertIsDisplayed() } + } + step("Assert wallet name is displayed") { + onWalletConnectScreen { walletName.assertIsDisplayed() } + } + step("Assert app icon is displayed") { + onWalletConnectScreen { appIcon.assertIsDisplayed() } + } + step("Assert app name is displayed") { + onWalletConnectScreen { appName.assertIsDisplayed() } + } + step("Assert approve icon is displayed") { + onWalletConnectScreen { approveIcon.assertIsDisplayed() } + } + step("Assert app URL is displayed") { + onWalletConnectScreen { appUrl.assertIsDisplayed() } + } + } else { + step("Assert wallet name is not displayed") { + onWalletConnectScreen { walletName.assertIsNotDisplayed() } + } + step("Assert app icon is not displayed") { + onWalletConnectScreen { appIcon.assertIsNotDisplayed() } + } + step("Assert app name is not displayed") { + onWalletConnectScreen { appName.assertIsNotDisplayed() } + } + step("Assert approve icon is not displayed") { + onWalletConnectScreen { approveIcon.assertIsNotDisplayed() } + } + step("Assert app URL is not displayed") { + onWalletConnectScreen { appUrl.assertIsNotDisplayed() } + } + step("Assert 'Wallet Connect' image is displayed") { + onWalletConnectScreen { walletConnectImage.assertIsDisplayed() } + } + step("Assert 'No session' title is displayed") { + onWalletConnectScreen { noSessionTitle.assertIsDisplayed() } + } + step("Assert 'No session' text is displayed") { + onWalletConnectScreen { noSessionText.assertIsDisplayed() } + } + } + } fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) { + waitForIdle() step("Assert connection details title is displayed") { onWalletConnectDetailsBottomSheet { title.assertIsDisplayed() } } @@ -128,7 +159,7 @@ fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) { onWalletConnectDetailsBottomSheet { connectedNetworkIcon.assertIsDisplayed() } } step("Assert connected dApp name: '$dAppName'") { - onWalletConnectDetailsBottomSheet { connectedNetworkName.assertTextContains(dAppName) } + onWalletConnectDetailsBottomSheet { appName.assertTextContains(dAppName) } } step("Assert connected network symbol is displayed") { onWalletConnectDetailsBottomSheet { connectedNetworkSymbol.assertIsDisplayed() } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectBottonSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectBottonSheetPageObject.kt index c111e92406..8490c75e76 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectBottonSheetPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectBottonSheetPageObject.kt @@ -9,13 +9,11 @@ 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.walletconnect.impl.R as WalletConnectImplR class WalletConnectBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen(semanticsProvider = semanticsProvider) { val title: KNode = child { - hasText(getResourceString(WalletConnectImplR.string.wc_wallet_connect)) hasTestTag(WalletConnectBottomSheetTestTags.TITLE) useUnmergedTree = true } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectPageObject.kt index b9da100325..cb84e644f1 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectPageObject.kt @@ -53,6 +53,21 @@ class WalletConnectPageObject(semanticsProvider: SemanticsNodeInteractionsProvid hasText(getResourceString(R.string.wc_new_connection)) useUnmergedTree = true } + + val walletConnectImage: KNode = child { + hasTestTag(WalletConnectScreenTestTags.WALLET_CONNECT_IMAGE) + useUnmergedTree = true + } + + val noSessionTitle: KNode = child { + hasText(getResourceString(R.string.wc_no_sessions_title)) + useUnmergedTree = true + } + + val noSessionText: KNode = child { + hasText(getResourceString(R.string.wc_no_sessions_desc)) + useUnmergedTree = true + } } internal fun BaseTestCase.onWalletConnectScreen(function: WalletConnectPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectScanQrPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectScanQrPageObject.kt new file mode 100644 index 0000000000..d48c6e7e67 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/WalletConnectScanQrPageObject.kt @@ -0,0 +1,23 @@ +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 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 WalletConnectScanQrPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val pasteFromClipboardButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(R.string.wallet_connect_paste_from_clipboard)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onWalletConnectScanQrScreen(function: WalletConnectScanQrPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/WalletConnectTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/WalletConnectTest.kt index db5e364f20..13fa9b6a47 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/WalletConnectTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/WalletConnectTest.kt @@ -2,12 +2,13 @@ package com.tangem.tests import com.tangem.common.BaseTestCase import com.tangem.common.constants.TestConstants.TOTAL_BALANCE -import com.tangem.common.extensions.SwipeDirection +import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT import com.tangem.common.extensions.clickWithAssertion -import com.tangem.common.extensions.swipeVertical import com.tangem.common.utils.getWcUri +import com.tangem.common.utils.setClipboardText import com.tangem.scenarios.* import com.tangem.screens.* +import com.tangem.wallet.BuildConfig import dagger.hilt.android.testing.HiltAndroidTest import io.qameta.allure.kotlin.AllureId import io.qameta.allure.kotlin.junit4.DisplayName @@ -21,7 +22,7 @@ class WalletConnectTest : BaseTestCase() { @DisplayName("WC (React App): open session from deeplink on main screen") @Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work") @Test - fun openWalletConnectSessionOnMainScreen() { + fun openWalletConnectSessionOnMainScreenTest() { val balance = TOTAL_BALANCE val dAppName = "React App" val deepLinkUri = getWcUri() @@ -37,19 +38,28 @@ class WalletConnectTest : BaseTestCase() { openAppByDeepLink(deepLinkUri) } step("Check 'Wallet Connect' bottom sheet") { - checkWalletConnectBottomSheet() + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectBottomSheet() + } + } + step("Assert 'Connect' button is enabled") { + onWalletConnectBottomSheet { connectButton.assertIsEnabled() } } step("Click on 'Connect' button") { + waitForIdle() onWalletConnectBottomSheet { connectButton.performClick() } } - step("Click 'More' button on TopBar") { - onTopBar { moreButton.clickWithAssertion() } + step("Assert 'Connect' button is not displayed") { + waitForIdle() + onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() } } - step("Click on 'Wallet Connect' button") { - onDetailsScreen { walletConnectButton.clickWithAssertion() } + step("Open 'Wallet Connect' screen") { + openWalletConnectScreen() } - step("Check 'Wallet Connect' screen") { - checkWalletConnectScreen() + step("Check 'Wallet Connect' screen with connections") { + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectScreen(withConnections = true) + } } step("Click on app icon") { onWalletConnectScreen { appIcon.performClick() } @@ -57,11 +67,11 @@ class WalletConnectTest : BaseTestCase() { step("Check 'Wallet Connect' details bottom sheet") { checkWalletConnectDetailsBottomSheet(dAppName) } - step("Click on 'Disconnect button' is displayed") { + step("Click on 'Disconnect' button") { onWalletConnectDetailsBottomSheet { disconnectButton.performClick() } } - step("Assert connection is not displayed") { - onWalletConnectScreen { appName.assertIsNotDisplayed() } + step("Check 'Wallet Connect' screen without connections") { + checkWalletConnectScreen(withConnections = false) } } } @@ -70,7 +80,7 @@ class WalletConnectTest : BaseTestCase() { @DisplayName("WC (React App): open session from deeplink not on main screen") @Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work") @Test - fun openWalletConnectSessionNotOnMainScreen() { + fun openWalletConnectSessionNotOnMainScreenTest() { val balance = TOTAL_BALANCE val dAppName = "React App" val deepLinkUri = getWcUri() @@ -82,41 +92,44 @@ class WalletConnectTest : BaseTestCase() { step("Synchronize addresses") { synchronizeAddresses(balance) } - step("Click on 'Buy' button") { - onMainScreen { buyButton.clickWithAssertion() } + step("Open 'Wallet Connect' screen") { + openWalletConnectScreen() + checkWalletConnectScreen(false) } step("Create WC session buy deeplink") { openAppByDeepLink(deepLinkUri) } step("Check 'Wallet Connect' bottom sheet") { - checkWalletConnectBottomSheet() + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectBottomSheet() + } } step("Click on 'Connect' button") { + waitForIdle() onWalletConnectBottomSheet { connectButton.performClick() } } - step("Click 'More' button on TopBar") { - onTopBar { moreButton.clickWithAssertion() } + step("Assert 'Connect' button is not displayed") { + waitForIdle() + onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() } } - step("Click on 'Wallet Connect' button") { - onDetailsScreen { walletConnectButton.clickWithAssertion() } - } - step("Assert 'Wallet Connect' bottom sheet is displayed") { - onWalletConnectBottomSheet { connectButton.clickWithAssertion() } - } - step("Check 'Wallet Connect' screen") { - checkWalletConnectScreen() + step("Check 'Wallet Connect' screen with connections") { + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectScreen(withConnections = true) + } } step("Click on app icon") { onWalletConnectScreen { appIcon.performClick() } } step("Check 'Wallet Connect' details bottom sheet") { - checkWalletConnectDetailsBottomSheet(dAppName) + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectDetailsBottomSheet(dAppName) + } } - step("Click on 'Disconnect button' is displayed") { + step("Click on 'Disconnect' button") { onWalletConnectDetailsBottomSheet { disconnectButton.performClick() } } - step("Assert connection is not displayed") { - onWalletConnectScreen { appName.assertIsNotDisplayed() } + step("Check 'Wallet Connect' screen without connections") { + checkWalletConnectScreen(withConnections = false) } } } @@ -125,9 +138,10 @@ class WalletConnectTest : BaseTestCase() { @DisplayName("WC (React App): open session from deeplink ") @Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work") @Test - fun openWalletConnectSession() { + fun openWalletConnectSessionTest() { val balance = TOTAL_BALANCE val dAppName = "React App" + val packageName = BuildConfig.APPLICATION_ID val deepLinkUri = getWcUri() setupHooks().run { @@ -137,32 +151,28 @@ class WalletConnectTest : BaseTestCase() { step("Synchronize addresses") { synchronizeAddresses(balance) } - step("Open recent apps") { - device.uiDevice.pressRecentApps() - } - step("Stop app by swipe") { - swipeVertical(SwipeDirection.UP, startHeightRatio = 0.8f) + step("Kill app") { + device.apps.kill(packageName) } step("Create WC session buy deeplink") { openAppByDeepLink(deepLinkUri) } - step("Open 'Main Screen'") { - openMainScreen() - } step("Check 'Wallet Connect' bottom sheet") { - checkWalletConnectBottomSheet() + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectBottomSheet() + } } step("Click on 'Connect' button") { onWalletConnectBottomSheet { connectButton.performClick() } } - step("Click 'More' button on TopBar") { - onTopBar { moreButton.clickWithAssertion() } + step("Assert 'Connect' button is not displayed") { + onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() } } - step("Click on 'Wallet Connect' button") { - onDetailsScreen { walletConnectButton.clickWithAssertion() } + step("Open 'Wallet Connect' screen") { + openWalletConnectScreen() } - step("Check 'Wallet Connect' screen") { - checkWalletConnectScreen() + step("Check 'Wallet Connect' screen with connections") { + checkWalletConnectScreen(withConnections = true) } step("Click on app icon") { onWalletConnectScreen { appIcon.performClick() } @@ -170,11 +180,72 @@ class WalletConnectTest : BaseTestCase() { step("Check 'Wallet Connect' details bottom sheet") { checkWalletConnectDetailsBottomSheet(dAppName) } - step("Click on 'Disconnect button' is displayed") { + step("Click on 'Disconnect' button") { onWalletConnectDetailsBottomSheet { disconnectButton.performClick() } } - step("Assert connection is not displayed") { - onWalletConnectScreen { appName.assertIsNotDisplayed() } + step("Check 'Wallet Connect' screen without connections") { + checkWalletConnectScreen(withConnections = false) + } + } + } + + @AllureId("887") + @DisplayName("WC: open session by 'Paste from clipboard' button") + @Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work") + @Test + fun openWalletConnectSessionByClipboardLinkTest() { + val balance = TOTAL_BALANCE + val dAppName = "React App" + val context = device.context + val deepLinkUri = getWcUri() + + setupHooks().run { + step("Set URI to clipboard") { + setClipboardText(context, deepLinkUri) + } + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Open 'Wallet Connect' screen") { + openWalletConnectScreen() + } + step("Click 'New connection' button") { + onWalletConnectScreen { newConnectionButton.performClick() } + } + step("CLick 'Paste from clipboard' button") { + onWalletConnectScanQrScreen { pasteFromClipboardButton.clickWithAssertion() } + } + step("Check 'Wallet Connect' bottom sheet") { + waitForIdle() + flakySafely(WAIT_UNTIL_TIMEOUT) { + checkWalletConnectBottomSheet() + } + } + step("Click on 'Connect' button") { + waitForIdle() + onWalletConnectBottomSheet { connectButton.performClick() } + } + step("Assert 'Connect' button is not displayed") { + waitForIdle() + onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() } + } + step("Check 'Wallet Connect' screen with connections") { + checkWalletConnectScreen(withConnections = true) + } + step("Click on app icon") { + onWalletConnectScreen { appIcon.performClick() } + } + step("Check 'Wallet Connect' details bottom sheet") { + checkWalletConnectDetailsBottomSheet(dAppName) + } + step("Click on 'Disconnect' button") { + onWalletConnectDetailsBottomSheet { disconnectButton.performClick() } + } + step("Check 'Wallet Connect' screen without connections") { + checkWalletConnectScreen(withConnections = false) } } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/WalletConnectScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/WalletConnectScreenTestTags.kt index 8280fc5021..53400f7588 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/WalletConnectScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/WalletConnectScreenTestTags.kt @@ -7,4 +7,5 @@ object WalletConnectScreenTestTags { const val APP_NAME = "WALLET_CONNECT_SCREEN_APP_NAME" const val APPROVE_ICON = "WALLET_CONNECT_SCREEN_APPROVE_ICON" const val APP_URL = "WALLET_CONNECT_SCREEN_APP_URL" + const val WALLET_CONNECT_IMAGE = "WALLET_CONNECT_SCREEN_WALLET_CONNECT_IMAGE" } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt index f079b379c0..8afd70342c 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt @@ -110,7 +110,9 @@ private fun EmptyConnectionsBlock(onNewConnectionClick: () -> Unit, modifier: Mo Image( painter = painterResource(R.drawable.img_wallet_connect_76), contentDescription = "Wallet Connect", - modifier = Modifier.size(76.dp), + modifier = Modifier + .size(76.dp) + .testTag(WalletConnectScreenTestTags.WALLET_CONNECT_IMAGE), ) Text( modifier = Modifier.padding(top = TangemTheme.dimens.spacing24),