Updated on 2026-08-14
This commit is contained in:
commit
8c84593e15
11 changed files with 286 additions and 27 deletions
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.common.extensions
|
||||
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.AppSettingsScreenTestTags
|
||||
import com.tangem.core.ui.test.TokenDetailsTopBarTestTags
|
||||
import com.tangem.core.ui.test.TopAppBarTestTags
|
||||
import com.tangem.core.ui.test.TopNavigationTestTags
|
||||
|
||||
fun BaseTestCase.tapBackButton() {
|
||||
waitForIdle()
|
||||
composeTestRule.onNode(
|
||||
hasTestTag(TopNavigationTestTags.BACK_BUTTON)
|
||||
or hasTestTag(TopAppBarTestTags.CLOSE_BUTTON)
|
||||
or hasTestTag(TokenDetailsTopBarTestTags.BACK_BUTTON)
|
||||
or hasTestTag(AppSettingsScreenTestTags.BACK_BUTTON),
|
||||
useUnmergedTree = true,
|
||||
).performClick()
|
||||
}
|
||||
|
|
@ -42,6 +42,17 @@ object AddressComparisonHelper {
|
|||
assertTrue(report, false)
|
||||
}
|
||||
|
||||
fun derivationPathForBlockchain(json: String, blockchain: String): String {
|
||||
val array = JSONArray(json)
|
||||
for (i in 0 until array.length()) {
|
||||
val entry = array.getJSONObject(i)
|
||||
if (entry.getString("blockchain").equals(blockchain, ignoreCase = true)) {
|
||||
return entry.getString("derivationPath").trim()
|
||||
}
|
||||
}
|
||||
error("No entry with blockchain '$blockchain' found in addresses JSON")
|
||||
}
|
||||
|
||||
private fun parseAndNormalize(json: String): List<AddressEntry> {
|
||||
val array = JSONArray(json)
|
||||
val entries = mutableListOf<AddressEntry>()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import android.view.KeyEvent
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
|
|
@ -57,31 +56,6 @@ fun BaseTestCase.verifyAddresses(seedPhrase: String, apiAddressesJson: String) {
|
|||
}
|
||||
}
|
||||
|
||||
private const val TESTER_MENU_MAX_ATTEMPTS = 3
|
||||
|
||||
/**
|
||||
* Presses 'Volume Down' twice to open tester menu.
|
||||
* Retries up to [TESTER_MENU_MAX_ATTEMPTS] times if the menu doesn't appear.
|
||||
*/
|
||||
private fun BaseTestCase.openTesterMenu() {
|
||||
repeat(TESTER_MENU_MAX_ATTEMPTS) { attempt ->
|
||||
waitForIdle()
|
||||
device.uiDevice.pressKeyCode(KeyEvent.KEYCODE_VOLUME_DOWN)
|
||||
device.uiDevice.pressKeyCode(KeyEvent.KEYCODE_VOLUME_DOWN)
|
||||
|
||||
val opened = runCatching {
|
||||
onTesterMenuScreen { addressesInfoButton.assertIsDisplayed() }
|
||||
}.isSuccess
|
||||
|
||||
if (opened) {
|
||||
TangemLogger.i("Tester menu opened on attempt ${attempt + 1}")
|
||||
return
|
||||
}
|
||||
TangemLogger.w("Tester menu not opened on attempt ${attempt + 1}, retrying...")
|
||||
}
|
||||
error("Failed to open tester menu after $TESTER_MENU_MAX_ATTEMPTS attempts")
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls [walletManagersStore] until the wallet manager count stops growing for [WALLET_MANAGERS_SETTLE_MS].
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.screens.onDetailsScreen
|
||||
import com.tangem.screens.onMainScreenTopBar
|
||||
import com.tangem.screens.onReferralProgramScreen
|
||||
import com.tangem.screens.onWalletSettingsScreen
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
fun BaseTestCase.referralTakeParticipate() {
|
||||
step("Open 'Details' screen") {
|
||||
onMainScreenTopBar { moreButton.clickWithAssertion() }
|
||||
}
|
||||
step("Open 'Wallet settings' screen") {
|
||||
onDetailsScreen { walletNameButton.clickWithAssertion() }
|
||||
}
|
||||
step("Open 'Referral program' screen") {
|
||||
onWalletSettingsScreen { referralProgramButton.clickWithAssertion() }
|
||||
}
|
||||
step("Tap 'Participate'") {
|
||||
onReferralProgramScreen { participateButton.clickWithAssertion() }
|
||||
}
|
||||
step("Verify referral code is displayed") {
|
||||
onReferralProgramScreen { personalCodeCard.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import android.view.KeyEvent
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.screens.onTesterMenuScreen
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
||||
private const val TESTER_MENU_MAX_ATTEMPTS = 3
|
||||
|
||||
/**
|
||||
* Opens the tester menu by sending two 'Volume Down' key events in a single shell command.
|
||||
* VolumeButtonDoublePressObserver needs two ACTION_DOWN within 300ms; two separate pressKeyCode
|
||||
* calls are too slow, but both events in one `input keyevent` invocation arrive back-to-back within
|
||||
* the window. Retries up to [TESTER_MENU_MAX_ATTEMPTS] times if it doesn't appear.
|
||||
*/
|
||||
fun BaseTestCase.openTesterMenu() {
|
||||
repeat(TESTER_MENU_MAX_ATTEMPTS) { attempt ->
|
||||
waitForIdle()
|
||||
val volumeDown = KeyEvent.KEYCODE_VOLUME_DOWN
|
||||
device.uiDevice.executeShellCommand("input keyevent $volumeDown $volumeDown")
|
||||
|
||||
val opened = runCatching {
|
||||
onTesterMenuScreen { addressesInfoButton.assertIsDisplayed() }
|
||||
}.isSuccess
|
||||
|
||||
if (opened) {
|
||||
TangemLogger.i("Tester menu opened on attempt ${attempt + 1}")
|
||||
return
|
||||
}
|
||||
TangemLogger.w("Tester menu not opened on attempt ${attempt + 1}, retrying...")
|
||||
}
|
||||
error("Failed to open tester menu after $TESTER_MENU_MAX_ATTEMPTS attempts")
|
||||
}
|
||||
|
|
@ -78,6 +78,11 @@ class ReferralProgramPageObject(semanticsProvider: SemanticsNodeInteractionsProv
|
|||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val personalCodeCard: KNode = child {
|
||||
hasTestTag(ReferralProgramScreenTestTags.PERSONAL_CODE_CARD)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onReferralProgramScreen(function: ReferralProgramPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
package com.tangem.tests.referral
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.tapBackButton
|
||||
import com.tangem.common.utils.AddressComparisonHelper
|
||||
import com.tangem.common.utils.getClipboardText
|
||||
import com.tangem.common.utils.resetWireMockScenarios
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.openTesterMenu
|
||||
import com.tangem.scenarios.referralTakeParticipate
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.onDetailsScreen
|
||||
import com.tangem.screens.onMainScreen
|
||||
import com.tangem.screens.onMainScreenTopBar
|
||||
import com.tangem.screens.onTesterMenuScreen
|
||||
import com.tangem.screens.onWalletSettingsScreen
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class ReferralTest : BaseTestCase() {
|
||||
|
||||
@AllureId("3630")
|
||||
@DisplayName("Referral program: Token and Blockchain added on Main screen after participation")
|
||||
@Test
|
||||
fun referralTokenAndBlockchainAddedAfterParticipationTest() {
|
||||
val tokenNetwork = "Tron"
|
||||
val token = "Tether"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarios()
|
||||
}
|
||||
).run {
|
||||
step("Open 'Main' screen") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Verify network $tokenNetwork and token $token is not displaying") {
|
||||
onMainScreen {
|
||||
assertTokenDoesNotExist(tokenNetwork)
|
||||
assertTokenDoesNotExist(token)
|
||||
}
|
||||
}
|
||||
step("Take participate in Referral program") {
|
||||
referralTakeParticipate()
|
||||
}
|
||||
step("Return to the 'Main' screen") {
|
||||
tapBackButton()
|
||||
onWalletSettingsScreen { screenContainer.assertIsDisplayed() }
|
||||
tapBackButton()
|
||||
onDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
tapBackButton()
|
||||
onMainScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Verify network $tokenNetwork and token $token is displayed on 'Main' screen") {
|
||||
onMainScreen {
|
||||
tokenWithTitleAndAddress(tokenNetwork)
|
||||
tokenWithTitleAndAddress(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("10098")
|
||||
@DisplayName("Referral program: Token added on Main screen after participation")
|
||||
@Test
|
||||
fun referralTokenAddedAfterParticipationTest() {
|
||||
val userWalletScenarioName = "user_tokens_api"
|
||||
val userWalletState = "Tron"
|
||||
val tokenNetwork = "Tron"
|
||||
val token = "Tether"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarios()
|
||||
}
|
||||
).run {
|
||||
step("Set wiremock scenario: $userWalletScenarioName to state $userWalletState") {
|
||||
setWireMockScenarioState(scenarioName = userWalletScenarioName, state = userWalletState)
|
||||
}
|
||||
step("Open 'Main' screen") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Verify token $token is not displaying") {
|
||||
onMainScreen { assertTokenDoesNotExist(token) }
|
||||
}
|
||||
step("Verify network $tokenNetwork is displaying") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenNetwork) }
|
||||
}
|
||||
step("Take participate in Referral program") {
|
||||
referralTakeParticipate()
|
||||
}
|
||||
step("Return to the 'Main' screen") {
|
||||
tapBackButton()
|
||||
onWalletSettingsScreen { screenContainer.assertIsDisplayed() }
|
||||
tapBackButton()
|
||||
onDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
tapBackButton()
|
||||
onMainScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Verify $token is displayed on 'Main' screen") {
|
||||
onMainScreen { tokenWithTitleAndAddress(token) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("3629")
|
||||
@DisplayName("Referral program: Participating unavailable for No-Wallet cards")
|
||||
@Test
|
||||
fun referralUnavailableForNoWalletCardsTest() {
|
||||
setupHooks().run {
|
||||
step("Open 'Main' screen") {
|
||||
openMainScreen(productType = ProductType.Note)
|
||||
}
|
||||
step("Open 'Details' screen") {
|
||||
onMainScreenTopBar { moreButton.clickWithAssertion() }
|
||||
}
|
||||
step("Open 'Wallet settings' screen") {
|
||||
onDetailsScreen { walletNameButton.clickWithAssertion() }
|
||||
}
|
||||
step("Verify 'Referral program' button does not displaying") {
|
||||
onWalletSettingsScreen { referralProgramButton.assertDoesNotExist() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("3636")
|
||||
@DisplayName("Referral program: Verify wallet derivation")
|
||||
@Test
|
||||
fun referralVerifyWalletDerivationTest() {
|
||||
val tokenNetwork = "Tron"
|
||||
val expectedDerivationPath = "m/44'/195'/0'/0/0"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarios()
|
||||
}
|
||||
).run {
|
||||
step("Open 'Main' screen") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Take participate in Referral program") {
|
||||
referralTakeParticipate()
|
||||
}
|
||||
step("Open Debug menu") {
|
||||
openTesterMenu()
|
||||
}
|
||||
step("Open 'Addresses info' screen") {
|
||||
onTesterMenuScreen { addressesInfoButton.clickWithAssertion() }
|
||||
}
|
||||
step("Verify $tokenNetwork derivation path is '$expectedDerivationPath'") {
|
||||
onTesterMenuScreen { jsonTab.clickWithAssertion() }
|
||||
onTesterMenuScreen { copyButton.clickWithAssertion() }
|
||||
val addressesJson = getClipboardText(ApplicationProvider.getApplicationContext())
|
||||
?: error("Clipboard is empty after copying addresses")
|
||||
val actualDerivationPath =
|
||||
AddressComparisonHelper.derivationPathForBlockchain(addressesJson, tokenNetwork)
|
||||
assertEquals(expectedDerivationPath, actualDerivationPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,16 +3,18 @@ package com.tangem.core.ui.ds2.button
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.NonRestartableComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_arrow_left_20
|
||||
import com.tangem.core.ui.res.generated.icons.ic_cross_20
|
||||
import com.tangem.core.ui.test.TopNavigationTestTags
|
||||
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
fun TangemButton.Back(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
modifier = modifier.testTag(TopNavigationTestTags.BACK_BUTTON),
|
||||
variant = TangemButton.Variant.Material,
|
||||
iconStart = TangemIconUM.Icon(Icons.ic_arrow_left_20),
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ object ReferralProgramScreenTestTags {
|
|||
const val CONDITION_BLOCK = "REFERRAL_PROGRAM_SCREEN_CONDITION_BLOCK"
|
||||
const val INFO_FOR_YOU_TEXT = "REFERRAL_PROGRAM_SCREEN_INFO_FOR_YOU_TEXT"
|
||||
const val INFO_FOR_YOUR_FRIEND_TEXT = "REFERRAL_PROGRAM_INFO_FOR_YOUR_FRIEND_TEXT"
|
||||
const val PERSONAL_CODE_CARD = "REFERRAL_PROGRAM_SCREEN_PERSONAL_CODE_CARD"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TopNavigationTestTags {
|
||||
const val BACK_BUTTON = "TOP_NAVIGATION_BACK_BUTTON"
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -41,6 +42,7 @@ import com.tangem.core.ui.res.LocalRedesignEnabled
|
|||
import com.tangem.core.ui.res.LocalTopSnackbarHostState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.ReferralProgramScreenTestTags
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAward
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAwards
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder
|
||||
|
|
@ -261,6 +263,7 @@ private fun ExtraItems(extraItems: List<ExpectedAward>, overallItemsLastIndex: I
|
|||
private fun PersonalCodeCard(code: String, accountAward: ReferralStateHolder.AccountAward?) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.testTag(ReferralProgramScreenTestTags.PERSONAL_CODE_CARD)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius12))
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue