diff --git a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt index 798d610177..5322b95a0c 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt @@ -1,8 +1,8 @@ package com.tangem.common import android.Manifest +import androidx.compose.ui.test.isRoot import androidx.compose.ui.test.junit4.createEmptyComposeRule -import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.printToLog import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents @@ -121,6 +121,8 @@ abstract class BaseTestCase : TestCase( /** * Prints the Compose semantics tree to logcat for debugging UI tests. * + * @param rootIndex Use rootIndex > 0, if you need to print semantics tree for bottom sheet. + * Default: 0. * @param useUnmergedTree When true, shows unmerged tree with all individual nodes. * Use for accessing inner elements of compound components. * Default: false (merged tree - accessibility view). @@ -129,11 +131,13 @@ abstract class BaseTestCase : TestCase( * Default: Int.MAX_VALUE (unlimited depth). */ fun printSemanticTree( + rootIndex: Int = 0, useUnmergedTree: Boolean = false, tag: String = "SEMANTIC_TREE", - maxDepth: Int = Int.MAX_VALUE) - { - composeTestRule.onRoot(useUnmergedTree = useUnmergedTree).printToLog(tag, maxDepth) + maxDepth: Int = Int.MAX_VALUE + ) { + composeTestRule.onAllNodes(isRoot(), useUnmergedTree = useUnmergedTree)[rootIndex] + .printToLog(tag, maxDepth) } fun waitForIdle() = composeTestRule.waitForIdle() diff --git a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt index 82b612164d..62e83ab9c4 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt @@ -4,6 +4,7 @@ object TestConstants { const val TOTAL_BALANCE = "$3,299.18" const val RECIPIENT_ADDRESS = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0" + const val BITCOIN_ADDRESS = "bc1qtg9aa6jcpqtvun0pe0uct7sxm8nq2nsxfmfxm3" const val CARDANO_ADDRESS = "addr1q8f9499e58k4hhfd9vhawprxt3xd94x7rmlyp33ee4xkatakcl2zgkrg0p6ceqkndtkw4cumfe9enhdph8yhuswn785srksm9p" const val WAIT_UNTIL_TIMEOUT = 20_000L diff --git a/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt b/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt index f4648d1cc9..c2e3981abc 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/utils/ClipboardUtils.kt @@ -3,6 +3,7 @@ package com.tangem.common.utils import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import androidx.test.core.app.ApplicationProvider fun getClipboardText(context: Context): String? { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager @@ -17,4 +18,21 @@ fun setClipboardText(context: Context, text: String?) { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("label", text) clipboard.setPrimaryClip(clip) +} + +fun clearClipboard( + context: Context = ApplicationProvider.getApplicationContext() +) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.clearPrimaryClip() +} + +fun assertClipboardTextEquals( + expected: String, + context: Context = ApplicationProvider.getApplicationContext() +) { + val actual = getClipboardText(context) + assert(actual == expected) { + "Clipboard text mismatch.\nExpected: '$expected'\nActual: '$actual'" + } } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/ChromeBrowserPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/ChromeBrowserPageObject.kt new file mode 100644 index 0000000000..78c2fa41dd --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/ChromeBrowserPageObject.kt @@ -0,0 +1,55 @@ +package com.tangem.screens + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until +import com.kaspersky.kaspresso.screens.KScreen + +object ChromeBrowserPageObject : KScreen() { + + override val layoutId: Int? = null + override val viewClass: Class<*>? = null + + private val device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + private const val TIMEOUT = 10000L + + fun assertChromeIsOpened() { + val chromePackage = "com.android.chrome" + device.wait(Until.hasObject(By.pkg(chromePackage).depth(0)), TIMEOUT) + assert(device.hasObject(By.pkg(chromePackage))) { + "Chrome browser is not opened" + } + } + + fun assertUrlContains(expectedUrl: String) { + val urlBar = device.findObject( + By.res("com.android.chrome:id/url_bar") + ) + urlBar?.let { + assert(it.text.contains(expectedUrl)) { + "URL doesn't contain expected: $expectedUrl, actual: ${it.text}" + } + } + } + + private fun findElementByText(text: String): UiObject2? { + device.wait(Until.hasObject(By.text(text)), TIMEOUT) + return device.findObject(By.text(text)) + } + + fun assertElementWithTextExists(text: String) { + val element = findElementByText(text) + assert(element != null) { "Element with text '$text' not found" } + } + + fun isElementWithTextExists(text: String): Boolean { + return device.wait(Until.hasObject(By.text(text)), TIMEOUT) + } + + fun clickOnElementWithText(text: String) { + findElementByText(text)?.click() + ?: throw AssertionError("Cannot click - element with text '$text' not found") + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt index da077e296f..d1ce4ef047 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt @@ -17,6 +17,7 @@ import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode import io.github.kakaocup.kakao.common.utilities.getResourceString import androidx.compose.ui.test.hasTestTag as withTestTag import androidx.compose.ui.test.hasText as withText +import com.tangem.core.ui.R as CoreUiR class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen(semanticsProvider = semanticsProvider) { @@ -200,6 +201,10 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) useUnmergedTree = true } + val snackbarCopiedAddressMessage: KNode = child { + hasText(getResourceString(CoreUiR.string.wallet_notification_address_copied)) + } + /** * Find token list item with title and address */ diff --git a/app/src/androidTest/kotlin/com/tangem/screens/BaseBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/ReceiveAssetsBottomSheetPageObject.kt similarity index 50% rename from app/src/androidTest/kotlin/com/tangem/screens/BaseBottomSheetPageObject.kt rename to app/src/androidTest/kotlin/com/tangem/screens/ReceiveAssetsBottomSheetPageObject.kt index d40a96c38a..acea682a27 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/BaseBottomSheetPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/ReceiveAssetsBottomSheetPageObject.kt @@ -3,20 +3,19 @@ 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 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 BaseBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : - ComposeScreen(semanticsProvider = semanticsProvider) { +class ReceiveAssetsBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { - val hideButton: KNode = child { - hasText(getResourceString(R.string.token_details_hide_token)) - hasTestTag(BaseBottomSheetTestTags.ACTION_TITLE) + val showQrCodeButton: KNode = child { + hasText(getResourceString(R.string.token_receive_show_qr_code_title)) + useUnmergedTree = true } } -internal fun BaseTestCase.onBottomSheet(function: BaseBottomSheetPageObject.() -> Unit) = +internal fun BaseTestCase.onReceiveAssetsBottomSheet(function: ReceiveAssetsBottomSheetPageObject.() -> Unit) = onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt index d069f7abe1..d2f4fa7b1b 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt @@ -71,6 +71,10 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) hasText(getResourceString(R.string.common_swap)) } + fun tokenSymbol(symbol: String): KNode = child { + hasTestTag(SwapTokenScreenTestTags.TOKEN_SYMBOL) + } + } internal fun BaseTestCase.onSwapTokenScreen(function: SwapTokenPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/TokenActionsBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/TokenActionsBottomSheetPageObject.kt new file mode 100644 index 0000000000..af3647e158 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/TokenActionsBottomSheetPageObject.kt @@ -0,0 +1,75 @@ +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 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 +import androidx.compose.ui.test.hasText as withText + +class TokenActionsBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val analyticsButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_analytics))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val copyAddressButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_copy_address))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val receiveButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_receive))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val sendButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_send))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val swapButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_swap))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val buyButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_buy))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val sellButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.common_sell))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } + + val hideTokenButton: KNode = child { + hasAnyChild(withText(getResourceString(R.string.token_details_hide_token))) + hasTestTag(BaseBottomSheetTestTags.ACTION_BUTTON) + hasAnyChild(withTestTag(BaseBottomSheetTestTags.ACTION_ICON)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onTokenActionsBottomSheet(function: TokenActionsBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveQrCodeBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveQrCodeBottomSheetPageObject.kt new file mode 100644 index 0000000000..e6bb3c228b --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveQrCodeBottomSheetPageObject.kt @@ -0,0 +1,56 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.BaseBottomSheetTestTags +import com.tangem.core.ui.test.BaseButtonTestTags +import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags +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 + +class TokenReceiveQrCodeBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val closeButton: KNode = child { + hasTestTag(BaseBottomSheetTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + + val title: KNode = child { + hasTestTag(TokenReceiveQrCodeBottomSheetTestTags.TITLE) + useUnmergedTree = true + } + + val qrCode: KNode = child { + hasTestTag(TokenReceiveQrCodeBottomSheetTestTags.QR_CODE) + useUnmergedTree = true + } + + val addressTitle: KNode = child { + hasText(getResourceString(R.string.wc_common_address)) + useUnmergedTree = true + } + + val address: KNode = child { + hasTestTag(TokenReceiveQrCodeBottomSheetTestTags.ADDRESS) + useUnmergedTree = true + } + + val copyButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(R.string.common_copy)) + useUnmergedTree = true + } + + val shareButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(R.string.common_share)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onTokenReceiveQrCodeBottomSheet(function: TokenReceiveQrCodeBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveWarningBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveWarningBottomSheetPageObject.kt new file mode 100644 index 0000000000..2a0148863b --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/TokenReceiveWarningBottomSheetPageObject.kt @@ -0,0 +1,28 @@ +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.TokenReceiveWarningBottomSheetTestTags +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 TokenReceiveWarningBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val bottomSheet: KNode = child { + hasTestTag(TokenReceiveWarningBottomSheetTestTags.BOTTOM_SHEET) + } + + val gotItButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(R.string.common_got_it)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onTokenReceiveWarningBottomSheet(function: TokenReceiveWarningBottomSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt new file mode 100644 index 0000000000..76debeb60a --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/actionButtons/MainScreenActionButtonsTest.kt @@ -0,0 +1,405 @@ +package com.tangem.tests.actionButtons + +import androidx.compose.ui.test.longClick +import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.BITCOIN_ADDRESS +import com.tangem.common.constants.TestConstants.TOTAL_BALANCE +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.utils.assertClipboardTextEquals +import com.tangem.common.utils.clearClipboard +import com.tangem.scenarios.openMainScreen +import com.tangem.scenarios.synchronizeAddresses +import com.tangem.screens.* +import com.tangem.screens.onTokenActionsBottomSheet +import com.tangem.screens.onBuyTokenDetailsScreen +import com.tangem.screens.onDialog +import com.tangem.screens.onMainScreen +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 MainScreenActionButtonsTest : BaseTestCase() { + + @AllureId("79") + @DisplayName("Action buttons (long tap): validate UI") + @Test + fun actionButtonsValidateLongTapUiTest() { + val tokenTitle = "Ethereum" + val balance = TOTAL_BALANCE + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Analytics' button is displayed") { + onTokenActionsBottomSheet { analyticsButton.assertIsDisplayed() } + } + step("Assert 'Copy address' button is displayed") { + onTokenActionsBottomSheet { copyAddressButton.assertIsDisplayed() } + } + step("Assert 'Receive' button is displayed") { + onTokenActionsBottomSheet { receiveButton.assertIsDisplayed() } + } + step("Assert 'Send' button is displayed") { + onTokenActionsBottomSheet { sendButton.assertIsDisplayed() } + } + step("Assert 'Swap' button is displayed") { + onTokenActionsBottomSheet { swapButton.assertIsDisplayed() } + } + step("Assert 'Buy' button is displayed") { + onTokenActionsBottomSheet { buyButton.assertIsDisplayed() } + } + step("Assert 'Sell' button is displayed") { + onTokenActionsBottomSheet { sellButton.assertIsDisplayed() } + } + step("Assert 'Hide token' button is displayed") { + onTokenActionsBottomSheet { hideTokenButton.assertIsDisplayed() } + } + } + } + + @AllureId("84") + @DisplayName("Action buttons (long tap): check 'Copy address' button") + @Test + fun clickOnCopyAddressButtonTest() { + val tokenTitle = "Bitcoin" + val balance = TOTAL_BALANCE + val bitcoinAddress = BITCOIN_ADDRESS + + setupHooks( + additionalBeforeSection = { + clearClipboard() + }, + additionalAfterSection = { + clearClipboard() + } + ).run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Copy address' button is displayed") { + onTokenActionsBottomSheet { copyAddressButton.assertIsDisplayed() } + } + step("Click on 'Copy address' button") { + onTokenActionsBottomSheet { copyAddressButton.performClick() } + } + step("Assert snack bar message is displayed") { + onMainScreen { snackbarCopiedAddressMessage.assertIsDisplayed() } + } + step("Check clipboard has '$tokenTitle' address '$bitcoinAddress'") { + waitForIdle() + assertClipboardTextEquals(expected = bitcoinAddress) + } + } + } + + @AllureId("82") + @DisplayName("Action buttons (long tap): check 'Buy' button") + @Test + fun clickOnBuyButtonTest() { + val tokenTitle = "Bitcoin" + val tokenSymbol = "BTC" + val balance = TOTAL_BALANCE + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Buy' button is displayed") { + onTokenActionsBottomSheet { buyButton.assertIsDisplayed() } + } + step("Click on 'Buy' button") { + onTokenActionsBottomSheet { buyButton.performClick() } + } + step("Click on 'Confirm' button in 'Dialog'") { + waitForIdle() + onDialog { confirmButton.clickWithAssertion() } + } + step("Assert top app bar title contains '$tokenTitle'") { + onBuyTokenDetailsScreen { topBarTitle.assertTextContains("Buy $tokenTitle") } + } + step("Assert fiat currency text field is displayed") { + onBuyTokenDetailsScreen { fiatAmountTextField.assertIsDisplayed() } + } + step("Assert fiat currency icon is displayed") { + onBuyTokenDetailsScreen { fiatCurrencyIcon.assertIsDisplayed() } + } + step("Assert token amount field is displayed") { + onBuyTokenDetailsScreen { tokenAmountField.assertTextContains(tokenSymbol, substring = true) } + } + step("Assert 'Continue' button") { + onBuyTokenDetailsScreen { continueButton.assertIsDisplayed() } + } + } + } + + @AllureId("87") + @DisplayName("Action buttons (long tap): check 'Swap' button") + @Test + fun clickOnSwapButtonTest() { + val tokenTitle = "Ethereum" + val tokenSymbol = "ETH" + val balance = TOTAL_BALANCE + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Swap' button is displayed") { + onTokenActionsBottomSheet { swapButton.assertIsDisplayed() } + } + step("Click on 'Swap' button") { + onTokenActionsBottomSheet { swapButton.performClick() } + } + step("Close 'Stories' screen") { + onSwapStoriesScreen { closeButton.clickWithAssertion() } + } + step("Assert 'Swap' screen title is displayed") { + onSwapTokenScreen { title.assertIsDisplayed() } + } + step("Assert token symbol: '$tokenSymbol' is displayed") { + onSwapTokenScreen { tokenSymbol(tokenSymbol).assertIsDisplayed() } + } + } + } + + @AllureId("83") + @DisplayName("Action buttons (long tap): check 'Send' button") + @Test + fun clickOnSendButtonTest() { + val tokenTitle = "Ethereum" + val tokenSymbol = "ETH" + val balance = TOTAL_BALANCE + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Send' button is displayed") { + onTokenActionsBottomSheet { sendButton.assertIsDisplayed() } + } + step("Click on 'Send' button") { + onTokenActionsBottomSheet { sendButton.performClick() } + } + step("Assert amount input text field contains token symbol: '$tokenSymbol'") { + onSendScreen { + amountInputTextField.assertTextContains(value = tokenSymbol, substring = true) + } + } + } + } + + @AllureId("86") + @DisplayName("Action buttons (long tap): check 'Receive' button") + @Test + fun clickOnReceiveButtonTest() { + val tokenTitle = "Bitcoin" + val balance = TOTAL_BALANCE + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Receive' button is displayed") { + onTokenActionsBottomSheet { receiveButton.assertIsDisplayed() } + } + step("Click on 'Receive' button") { + onTokenActionsBottomSheet { receiveButton.performClick() } + } + step("Assert 'Token receive warning' bottom sheet is displayed") { + onTokenReceiveWarningBottomSheet { bottomSheet.assertIsDisplayed() } + } + step("Click on 'Got it' button") { + onTokenReceiveWarningBottomSheet { gotItButton.performClick() } + } + step("Click on 'Show QR code' button") { + onReceiveAssetsBottomSheet { showQrCodeButton.clickWithAssertion() } + } + step("Assert bottom sheet with QR code title is displayed") { + onTokenReceiveQrCodeBottomSheet { title.assertIsDisplayed() } + } + step("Assert QR code is displayed") { + onTokenReceiveQrCodeBottomSheet { qrCode.assertIsDisplayed() } + } + step("Assert address title is displayed") { + onTokenReceiveQrCodeBottomSheet { addressTitle.assertIsDisplayed() } + } + step("Assert address is displayed") { + onTokenReceiveQrCodeBottomSheet { address.assertIsDisplayed() } + } + step("Assert 'Copy' button is displayed") { + onTokenReceiveQrCodeBottomSheet { copyButton.assertIsDisplayed() } + } + step("Assert 'Share' button is displayed") { + onTokenReceiveQrCodeBottomSheet { shareButton.assertIsDisplayed() } + } + } + } + + @AllureId("85") + @DisplayName("Action buttons (long tap): check 'Sell' button") + @Test + fun clickOnSellButtonTest() { + val tokenTitle = "Ethereum" + val tokenSymbol = "ETH" + val balance = TOTAL_BALANCE + val url = "sell.moonpay.com" + val useWithoutAccount = "Use without an account" + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Receive' button is displayed") { + onTokenActionsBottomSheet { sellButton.assertIsDisplayed() } + } + step("Click on 'Receive' button") { + onTokenActionsBottomSheet { sellButton.performClick() } + } + step("Assert Chrome Browser is opened") { + ChromeBrowserPageObject { assertChromeIsOpened() } + } + if (ChromeBrowserPageObject.isElementWithTextExists(useWithoutAccount)) { + step("Click on '$useWithoutAccount' button on Chrome browser") { + ChromeBrowserPageObject { clickOnElementWithText(useWithoutAccount) } + } + } + step("Assert url contains: '$url'") { + ChromeBrowserPageObject { assertUrlContains(url) } + } + step("Assert token symbol '$tokenSymbol' is displayed") { + ChromeBrowserPageObject { assertElementWithTextExists(tokenSymbol) } + } + } + } + + @AllureId("77") + @DisplayName("Action buttons (long tap): assert 'Sell' button is not displayed if token doesn't support it") + @Test + fun assertSellButtonIsNotDisplayedTest() { + val tokenTitle = "Bitcoin" + val balance = TOTAL_BALANCE + + setupHooks().run { + step("Open 'Main Screen'") { + openMainScreen() + } + step("Synchronize addresses") { + synchronizeAddresses(balance) + } + step("Long click on token with name: '$tokenTitle'") { + waitForIdle() + onMainScreen { + tokenWithTitleAndAddress(tokenTitle).performTouchInput { + longClick( + position = center, + durationMillis = 1000L + ) + } + } + } + step("Assert 'Sell' button is not displayed") { + onTokenActionsBottomSheet { sellButton.assertIsNotDisplayed() } + } + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/balance/TotalBalanceUpdateTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/balance/TotalBalanceUpdateTest.kt index 6fbc4b4812..756f9ea213 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/balance/TotalBalanceUpdateTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/balance/TotalBalanceUpdateTest.kt @@ -162,7 +162,7 @@ class TotalBalanceUpdateTest : BaseTestCase() { } } step("Click 'Hide token' button") { - onBottomSheet { hideButton.clickWithAssertion() } + onTokenActionsBottomSheet { hideTokenButton.clickWithAssertion() } } step("Click 'Hide' button in dialog") { onDialog { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/SettingsRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/SettingsRow.kt index f3bfb8028b..ebdceec152 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/SettingsRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/SettingsRow.kt @@ -40,7 +40,7 @@ fun SimpleSettingsRow( onItemsClick() } }, - ).testTag(BaseBottomSheetTestTags.ACTION_TITLE), + ).testTag(BaseBottomSheetTestTags.ACTION_BUTTON), horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically, ) { @@ -48,7 +48,8 @@ fun SimpleSettingsRow( painter = painterResource(id = icon), contentDescription = null, modifier = Modifier - .padding(horizontal = if (redesign) TangemTheme.dimens.spacing12 else TangemTheme.dimens.spacing20), + .padding(horizontal = if (redesign) TangemTheme.dimens.spacing12 else TangemTheme.dimens.spacing20) + .testTag(BaseBottomSheetTestTags.ACTION_ICON), tint = rowColors.iconColor(enabled = enabled).value, ) Column( diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/BaseBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/BaseBottomSheetTestTags.kt index d2d4a850de..ccf4a1033e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/BaseBottomSheetTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/BaseBottomSheetTestTags.kt @@ -1,7 +1,8 @@ package com.tangem.core.ui.test object BaseBottomSheetTestTags { - const val ACTION_TITLE = "BASE_BOTTOM_SHEET_ACTION_TITLE" + const val ACTION_BUTTON = "BASE_BOTTOM_SHEET_ACTION_BUTTON" + const val ACTION_ICON = "BASE_BOTTOM_SHEET_ACTION_ICON" const val TITLE = "BASE_BOTTOM_SHEET_TITLE" const val SUBTITLE = "BASE_BOTTOM_SHEET_SUBTITLE" const val CLOSE_BUTTON = "BASE_BOTTOM_SHEET_CLOSE_BUTTON" diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SwapTokenScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SwapTokenScreenTestTags.kt index cd6cf947d1..c64a02bb85 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/SwapTokenScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SwapTokenScreenTestTags.kt @@ -9,6 +9,6 @@ object SwapTokenScreenTestTags { 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" - const val TOKEN_NAME = "SWAP_TOKEN_SCREEN_TOKEN_NAME" + const val TOKEN_SYMBOL = "SWAP_TOKEN_SCREEN_TOKEN_SYMBOL" const val TOKEN_ICON = "SWAP_TOKEN_SCREEN_TOKEN_ICON" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveQrCodeBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveQrCodeBottomSheetTestTags.kt new file mode 100644 index 0000000000..0d9ae631fd --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveQrCodeBottomSheetTestTags.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.test + +object TokenReceiveQrCodeBottomSheetTestTags { + const val TITLE = "TOKEN_RECEIVE_QR_CODE_BOTTOM_SHEET_TITLE" + const val QR_CODE = "TOKEN_RECEIVE_QR_CODE_BOTTOM_SHEET_QR_CODE" + const val ADDRESS = "TOKEN_RECEIVE_QR_CODE_BOTTOM_SHEET_ADDRESS" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveWarningBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveWarningBottomSheetTestTags.kt new file mode 100644 index 0000000000..e644401122 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TokenReceiveWarningBottomSheetTestTags.kt @@ -0,0 +1,5 @@ +package com.tangem.core.ui.test + +object TokenReceiveWarningBottomSheetTestTags { + const val BOTTOM_SHEET = "TOKEN_RECEIVE_WARNING_BOTTOM_SHEET" +} \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index 8b8331fff7..6443a3dd5f 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -390,7 +390,7 @@ fun Token( textAlign = TextAlign.Center, modifier = Modifier .defaultMinSize(minWidth = TangemTheme.dimens.size80) - .testTag(SwapTokenScreenTestTags.TOKEN_NAME), + .testTag(SwapTokenScreenTestTags.TOKEN_SYMBOL), ) } } diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt index 959045d098..39b2363e21 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback +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 @@ -32,6 +33,7 @@ import com.tangem.core.ui.components.* import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TokenReceiveQrCodeBottomSheetTestTags import com.tangem.features.tokenreceive.impl.R import com.tangem.features.tokenreceive.ui.state.QrCodeUM import kotlinx.coroutines.launch @@ -88,6 +90,7 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, style = TangemTheme.typography.h3, + modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.TITLE), ) SpacerH(20.dp) @@ -99,7 +102,8 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net color = TangemTheme.colors.icon.constant, shape = RoundedCornerShape(8.dp), ) - .padding(8.dp), + .padding(8.dp) + .testTag(TokenReceiveQrCodeBottomSheetTestTags.QR_CODE), ) { Image( @@ -126,6 +130,7 @@ private fun QrCodePage(addressFullName: TextReference, addressValue: String, net color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, style = TangemTheme.typography.subtitle1, + modifier = Modifier.testTag(TokenReceiveQrCodeBottomSheetTestTags.ADDRESS), ) } } diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt index ed2a67a9be..6f4c57d692 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback +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.extensions.stringResourceSafe 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.TokenReceiveWarningBottomSheetTestTags import com.tangem.features.tokenreceive.impl.R import com.tangem.features.tokenreceive.ui.state.WarningUM @@ -40,7 +42,7 @@ internal fun TokenReceiveWarningContent(warningUM: WarningUM) { start = 16.dp, end = 16.dp, bottom = 16.dp, - ), + ).testTag(TokenReceiveWarningBottomSheetTestTags.BOTTOM_SHEET), horizontalAlignment = Alignment.CenterHorizontally, ) { CurrencyIcon(