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 7147ac9ef1..ad9a7ef27b 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt @@ -69,5 +69,7 @@ object TestConstants { "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" const val TANGEM_PAY_ELIGIBILITY_SCENARIO = "tangem_pay_eligibility" + const val TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO = "tangem_pay_eligibility_channels" + const val TANGEM_PAY_KYC_STATUS_SCENARIO = "tangem_pay_kyc_status" const val TANGEM_PAY_ACCESS_CODE = "517384" } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt index ce8ca04066..72fb5a77ab 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt @@ -10,6 +10,7 @@ import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onCompose import io.github.kakaocup.compose.node.element.KNode import io.github.kakaocup.kakao.common.utilities.getResourceString import androidx.compose.ui.test.hasText as withText +import com.tangem.core.res.R as CoreResR class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen(semanticsProvider = semanticsProvider) { @@ -65,6 +66,12 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : hasAnyDescendant(withText(name)) useUnmergedTree = true } + + val getTangemPayRow: KNode = child { + hasTestTag(DetailsScreenTestTags.SCREEN_ITEM) + hasAnyDescendant(withText(getResourceString(CoreResR.string.tangempay_get_tangem_pay))) + useUnmergedTree = true + } } internal fun BaseTestCase.onDetailsScreen(function: DetailsPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt index 7bf313c7df..9b2e52e48e 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt @@ -202,6 +202,12 @@ class MainScreenPageObject(private val semanticsProvider: SemanticsNodeInteracti useUnmergedTree = true } + val getTangemPayBanner: KNode = child { + hasTestTag(NotificationTestTags.TITLE) + hasText(getResourceString(CoreResR.string.tangempay_onboarding_banner_title)) + useUnmergedTree = true + } + val devCardNotificationIcon: KNode = child { hasAnySibling(withText(getResourceString(R.string.warning_developer_card_title))) hasTestTag(NotificationTestTags.ICON) diff --git a/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayKycSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayKycSheetPageObject.kt new file mode 100644 index 0000000000..cafa8ae9d2 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayKycSheetPageObject.kt @@ -0,0 +1,33 @@ +package com.tangem.screens.tangempay + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.WarningBottomSheetTestTags +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 androidx.compose.ui.test.hasText as withText + +class TangemPayKycSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + fun title(text: String): KNode = child { + hasTestTag(WarningBottomSheetTestTags.TITLE) + hasText(text) + useUnmergedTree = true + } + + fun primaryButtonWithText(text: String): KNode = child { + hasTestTag(WarningBottomSheetTestTags.BUTTON_PRIMARY) + hasAnyDescendant(withText(text)) + useUnmergedTree = true + } + + val closeButton: KNode = child { + hasTestTag(WarningBottomSheetTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onTangemPayKycSheet(function: TangemPayKycSheetPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayMainPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayMainPageObject.kt index caee319f6b..aad99a3d9e 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayMainPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayMainPageObject.kt @@ -20,6 +20,12 @@ class TangemPayMainPageObject(semanticsProvider: SemanticsNodeInteractionsProvid useUnmergedTree = true } + fun tileWithSubtitle(subtitle: String): KNode = child { + hasTestTag(TangemPayTestTags.MAIN_SCREEN_TILE) + hasAnyDescendant(withText(subtitle)) + useUnmergedTree = true + } + val balance: KNode = child { hasTestTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE) useUnmergedTree = true diff --git a/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayOnboardingPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayOnboardingPageObject.kt new file mode 100644 index 0000000000..8b724f628f --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/tangempay/TangemPayOnboardingPageObject.kt @@ -0,0 +1,21 @@ +package com.tangem.screens.tangempay + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.res.R as CoreResR +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 TangemPayOnboardingPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val title: KNode = child { + hasText(getResourceString(CoreResR.string.tangempay_onboarding_title)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onTangemPayOnboardingScreen(function: TangemPayOnboardingPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayOnboardingKycTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayOnboardingKycTest.kt new file mode 100644 index 0000000000..f6f0abc42a --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayOnboardingKycTest.kt @@ -0,0 +1,220 @@ +package com.tangem.tests.tangempay + +import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12 +import com.tangem.common.constants.TestConstants.TANGEM_PAY_ACCESS_CODE +import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO +import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_SCENARIO +import com.tangem.common.constants.TestConstants.TANGEM_PAY_KYC_STATUS_SCENARIO +import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG +import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_SHORT +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.utils.resetWireMockScenarioState +import com.tangem.common.utils.setWireMockScenarioState +import com.tangem.scenarios.openMainScreenWithExistingHotWallet +import com.tangem.screens.onDetailsScreen +import com.tangem.screens.onMainScreen +import com.tangem.screens.onMainScreenTopBar +import com.tangem.screens.tangempay.onTangemPayKycSheet +import com.tangem.screens.tangempay.onTangemPayMainScreen +import com.tangem.screens.tangempay.onTangemPayOnboardingScreen +import com.tangem.core.res.R as CoreResR +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 TangemPayOnboardingKycTest : BaseTestCase() { + + @AllureId("9630") + @DisplayName("Tangem Pay: existing customer authorization shows the tile on Main") + @Test + fun existingUserAuthorizationShowsTangemPayTileOnMainTest() { + val paeraCustomerState = "PaeraCustomer" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO) + }, + ).run { + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to '$paeraCustomerState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, paeraCustomerState) + } + step("Open 'Main' screen with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE) + } + step("Assert Tangem Pay tile is displayed on Main") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTangemPayMainScreen { mainScreenTile.assertIsDisplayed() } + } + } + } + } + + @AllureId("9632") + @DisplayName("Tangem Pay: Get Tangem Pay banner is shown on Main for the BANNER channel") + @Test + fun getTangemPayBannerShownOnMainWhenBannerChannelReceivedTest() { + val eligibilityStartedState = "Started" + val channelsBannerState = "Banner" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO) + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO) + }, + ).run { + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to '$eligibilityStartedState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityStartedState) + } + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO' to '$channelsBannerState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO, channelsBannerState) + } + step("Open 'Main' screen with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE) + } + step("Assert 'Get Tangem Pay' banner is displayed on Main") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onMainScreen { getTangemPayBanner.assertIsDisplayed() } + } + } + } + } + + @AllureId("9673") + @DisplayName("Tangem Pay: onboarding opens from app Details for the DETAILS channel") + @Test + fun onboardingOpensFromAppDetailsWhenDetailsChannelReceivedTest() { + val eligibilityStartedState = "Started" + val channelsBannerAndDetailsState = "BannerAndDetails" + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO) + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO) + }, + ).run { + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to '$eligibilityStartedState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityStartedState) + } + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO' to '$channelsBannerAndDetailsState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_CHANNELS_SCENARIO, channelsBannerAndDetailsState) + } + step("Open 'Main' screen with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE) + } + step("Click on 'More' button") { + onMainScreenTopBar { moreButton.clickWithAssertion() } + } + step("Assert 'Get Tangem Pay' row is displayed") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onDetailsScreen { getTangemPayRow.assertIsDisplayed() } + } + } + step("Click on 'Get Tangem Pay' row") { + onDetailsScreen { getTangemPayRow.clickWithAssertion() } + } + step("Assert Tangem Pay onboarding screen is displayed") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTangemPayOnboardingScreen { title.assertIsDisplayed() } + } + } + } + } + + @AllureId("9643") + @DisplayName("Tangem Pay: KYC in progress sheet closes and the status remains on Main") + @Test + fun kycInProgressSheetClosesAndStatusRemainsOnMainTest() { + val paeraCustomerState = "PaeraCustomer" + val kycInProgressState = "InProgress" + val kycInProgressText = getResourceString(CoreResR.string.tangempay_kyc_in_progress) + val viewStatusText = getResourceString(CoreResR.string.tangempay_kyc_in_progress_notification_button) + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO) + resetWireMockScenarioState(TANGEM_PAY_KYC_STATUS_SCENARIO) + }, + ).run { + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to '$paeraCustomerState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, paeraCustomerState) + } + step("Set WireMock scenario '$TANGEM_PAY_KYC_STATUS_SCENARIO' to '$kycInProgressState'") { + setWireMockScenarioState(TANGEM_PAY_KYC_STATUS_SCENARIO, kycInProgressState) + } + step("Open 'Main' screen with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE) + } + step("Assert Tangem Pay tile with '$kycInProgressText' is displayed") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTangemPayMainScreen { tileWithSubtitle(kycInProgressText).assertIsDisplayed() } + } + } + step("Click on Tangem Pay tile") { + onTangemPayMainScreen { mainScreenTile.clickWithAssertion() } + } + step("Assert KYC in progress sheet is displayed") { + onTangemPayKycSheet { + title(kycInProgressText).assertIsDisplayed() + primaryButtonWithText(viewStatusText).assertIsDisplayed() + } + } + step("Click on 'Close' button") { + onTangemPayKycSheet { closeButton.clickWithAssertion() } + } + step("Assert KYC in progress sheet is not displayed") { + flakySafely(WAIT_UNTIL_TIMEOUT_SHORT) { + onTangemPayKycSheet { primaryButtonWithText(viewStatusText).assertDoesNotExist() } + } + } + step("Assert Tangem Pay tile with '$kycInProgressText' is still displayed") { + onTangemPayMainScreen { tileWithSubtitle(kycInProgressText).assertIsDisplayed() } + } + } + } + + @AllureId("9517") + @DisplayName("Tangem Pay: KYC rejected status shown on Main and in the sheet") + @Test + fun kycRejectedStatusShownOnMainAndInSheetTest() { + val paeraCustomerState = "PaeraCustomer" + val kycDeclinedState = "Declined" + val kycRejectedTileText = getResourceString(CoreResR.string.tangempay_kyc_has_failed) + val rejectedSheetTitle = getResourceString(CoreResR.string.tangempay_kyc_rejected) + val goToSupportText = getResourceString(CoreResR.string.tangempay_go_to_support) + + setupHooks( + additionalAfterSection = { + resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO) + resetWireMockScenarioState(TANGEM_PAY_KYC_STATUS_SCENARIO) + }, + ).run { + step("Set WireMock scenario '$TANGEM_PAY_ELIGIBILITY_SCENARIO' to '$paeraCustomerState'") { + setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, paeraCustomerState) + } + step("Set WireMock scenario '$TANGEM_PAY_KYC_STATUS_SCENARIO' to '$kycDeclinedState'") { + setWireMockScenarioState(TANGEM_PAY_KYC_STATUS_SCENARIO, kycDeclinedState) + } + step("Open 'Main' screen with existing hot wallet") { + openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE) + } + step("Assert Tangem Pay tile with '$kycRejectedTileText' is displayed") { + flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { + onTangemPayMainScreen { tileWithSubtitle(kycRejectedTileText).assertIsDisplayed() } + } + } + step("Click on Tangem Pay tile") { + onTangemPayMainScreen { mainScreenTile.clickWithAssertion() } + } + step("Assert KYC rejected sheet is displayed") { + onTangemPayKycSheet { + title(rejectedSheetTitle).assertIsDisplayed() + primaryButtonWithText(goToSupportText).assertIsDisplayed() + } + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt index cf02bcd878..03e042b8bf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt @@ -63,6 +63,7 @@ fun MessageBottomSheetV2(state: MessageBottomSheetUM, onDismissRequest: () -> Un type = TangemTopBarType.BottomSheet, endContent = { TangemButton( + modifier = Modifier.testTag(WarningBottomSheetTestTags.CLOSE_BUTTON), iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24), onClick = stateWithOnDismiss.onDismissRequest, size = TangemButton.Size.X11, diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt index d8c3f37a55..c51b59be8d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/WarningBottomSheetTestTags.kt @@ -6,4 +6,5 @@ object WarningBottomSheetTestTags { const val MESSAGE = "BASE_WARNING_BOTTOM_SHEET_MESSAGE" const val BUTTON_PRIMARY = "BASE_WARNING_BOTTOM_SHEET_BUTTON_PRIMARY" const val BUTTON_SECONDARY = "BASE_WARNING_BOTTOM_SHEET_BUTTON_SECONDARY" + const val CLOSE_BUTTON = "BASE_WARNING_BOTTOM_SHEET_CLOSE_BUTTON" } \ No newline at end of file diff --git a/features/tangempay/main/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayMainBlockContent.kt b/features/tangempay/main/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayMainBlockContent.kt index bdb78bf1a1..6fc54eed14 100644 --- a/features/tangempay/main/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayMainBlockContent.kt +++ b/features/tangempay/main/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayMainBlockContent.kt @@ -149,7 +149,8 @@ private fun TangemPayStateRow( modifier = modifier .clip(RoundedCornerShape(size = 18.dp)) .background(TangemTheme.colors2.surface.level3) - .conditional(onClick != null && isEnabled) { clickableSingle(onClick = requireNotNull(onClick)) }, + .conditional(onClick != null && isEnabled) { clickableSingle(onClick = requireNotNull(onClick)) } + .testTag(TangemPayTestTags.MAIN_SCREEN_TILE), ) { Image( painter = getVisaIconPainter(),