Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-18 13:30:54 +03:00
parent ba3e7e4044
commit 58ac55c4ae
9 changed files with 26 additions and 20 deletions

View file

@ -62,6 +62,9 @@ When the user asks to **port** an iOS test to Android:
### Test class shape ### Test class shape
- **Test method names are camelCase and always end with `Test`** (e.g. `groupTokensTest()`,
`renameWalletTest()`). Never use `snake_case` and never the unit-test `GIVEN … WHEN … THEN …`
backtick phrasing — that GWT convention is for JVM unit tests only, not instrumentation tests.
- **Scenario state setup goes in the test body**, not inside the open-the-feature helper. Each test - **Scenario state setup goes in the test body**, not inside the open-the-feature helper. Each test
starts with explicit `step("Set WireMock scenario '$name' to '$state'") { setWireMockScenarioState(name, state) }` starts with explicit `step("Set WireMock scenario '$name' to '$state'") { setWireMockScenarioState(name, state) }`
calls, then calls a thin helper (e.g. `openTangemPay()`) that only opens the screen. Mirror the calls, then calls a thin helper (e.g. `openTangemPay()`) that only opens the screen. Mirror the

View file

@ -323,6 +323,7 @@ fun BaseTestCase.switchFeeTokenAndApply(currentFeeToken: String, newFeeToken: St
} }
} }
step("Click on 'Apply' button") { step("Click on 'Apply' button") {
waitForIdle()
onSendFeeSelectorBottomSheet { applyButton.performClick() } onSendFeeSelectorBottomSheet { applyButton.performClick() }
} }
} }

View file

@ -3,7 +3,6 @@ package com.tangem.screens
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import com.tangem.common.BaseTestCase import com.tangem.common.BaseTestCase
import com.tangem.core.ui.R import com.tangem.core.ui.R
import com.tangem.core.ui.test.AddFundsBottomSheetTestTags
import com.tangem.core.ui.test.BaseBottomSheetTestTags import com.tangem.core.ui.test.BaseBottomSheetTestTags
import com.tangem.core.ui.test.BuyTokenScreenTestTags import com.tangem.core.ui.test.BuyTokenScreenTestTags
import com.tangem.core.ui.test.MarketsTestTags import com.tangem.core.ui.test.MarketsTestTags
@ -38,7 +37,7 @@ class AddFundsBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractions
} }
val closeButton: KNode = child { val closeButton: KNode = child {
hasTestTag(AddFundsBottomSheetTestTags.CLOSE_BUTTON) hasTestTag(BaseBottomSheetTestTags.CLOSE_BUTTON)
useUnmergedTree = true useUnmergedTree = true
} }

View file

@ -189,6 +189,7 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
val swapFiatAmount: KNode = child { val swapFiatAmount: KNode = child {
hasTestTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT) hasTestTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT)
useUnmergedTree = true
} }
val swapSelectTokenIcon: KNode = child { val swapSelectTokenIcon: KNode = child {

View file

@ -6,9 +6,9 @@ import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_SCENARIO
import com.tangem.common.extensions.assertTextContainsSafe import com.tangem.common.extensions.assertTextContainsSafe
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.extensions.extractText import com.tangem.common.extensions.extractText
import com.tangem.common.extensions.pullToRefresh
import com.tangem.common.utils.assertClipboardTextEquals import com.tangem.common.utils.assertClipboardTextEquals
import com.tangem.common.utils.resetWireMockScenarioState import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.resetWireMockScenarios
import com.tangem.common.utils.setWireMockScenarioState import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.scenarios.* import com.tangem.scenarios.*
import com.tangem.screens.tangempay.* import com.tangem.screens.tangempay.*
@ -23,7 +23,7 @@ class TangemPayTest : BaseTestCase() {
@AllureId("4549") @AllureId("4549")
@DisplayName("Tangem Pay: change PIN code from card details") @DisplayName("Tangem Pay: change PIN code from card details")
@Test @Test
fun changePin_SetsNewPinCode_FromCardDetails() { fun changePinSetsNewPinCodeFromCardDetailsTest() {
val newPin = "5217" val newPin = "5217"
val pinSetupScenario = "tangem_pay_pin_setup" val pinSetupScenario = "tangem_pay_pin_setup"
val pinNotSetState = "PinNotSet" val pinNotSetState = "PinNotSet"
@ -31,6 +31,7 @@ class TangemPayTest : BaseTestCase() {
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
setWireMockScenarioState(pinSetupScenario, pinNotSetState) setWireMockScenarioState(pinSetupScenario, pinNotSetState)
}, },
@ -67,7 +68,7 @@ class TangemPayTest : BaseTestCase() {
@AllureId("4969") @AllureId("4969")
@DisplayName("Tangem Pay: balance updates after transaction on payment account screen") @DisplayName("Tangem Pay: balance updates after transaction on payment account screen")
@Test @Test
fun balanceUpdatesAfterTransaction_OnPaymentAccountScreen() { fun balanceUpdatesAfterTransactionOnPaymentAccountScreenTest() {
val balanceScenario = "tangem_pay_balance_update" val balanceScenario = "tangem_pay_balance_update"
val initialState = "InitialBalance" val initialState = "InitialBalance"
val afterTransactionState = "AfterTransaction" val afterTransactionState = "AfterTransaction"
@ -75,6 +76,7 @@ class TangemPayTest : BaseTestCase() {
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
setWireMockScenarioState(balanceScenario, initialState) setWireMockScenarioState(balanceScenario, initialState)
}, },
@ -90,7 +92,7 @@ class TangemPayTest : BaseTestCase() {
step("Switch WireMock scenario '$balanceScenario' to '$afterTransactionState'") { step("Switch WireMock scenario '$balanceScenario' to '$afterTransactionState'") {
setWireMockScenarioState(balanceScenario, afterTransactionState) setWireMockScenarioState(balanceScenario, afterTransactionState)
} }
step("Pull to refresh") { pullToRefresh() } step("Pull to refresh") { pullToRefreshTangemPay() }
step("Assert updated balance contains '9'") { step("Assert updated balance contains '9'") {
onTangemPayMainScreen { balance.assertTextContainsSafe("9", substring = true) } onTangemPayMainScreen { balance.assertTextContainsSafe("9", substring = true) }
} }
@ -100,7 +102,7 @@ class TangemPayTest : BaseTestCase() {
@AllureId("4970") @AllureId("4970")
@DisplayName("Tangem Pay: new transaction appears after mocked charge") @DisplayName("Tangem Pay: new transaction appears after mocked charge")
@Test @Test
fun transactionList_NewTransactionAppears_AfterMockedCharge() { fun transactionListNewTransactionAppearsAfterMockedChargeTest() {
val historyScenario = "tangem_pay_transaction_history" val historyScenario = "tangem_pay_transaction_history"
val initialState = "InitialEmpty" val initialState = "InitialEmpty"
val afterTransactionState = "AfterTransaction" val afterTransactionState = "AfterTransaction"
@ -109,6 +111,7 @@ class TangemPayTest : BaseTestCase() {
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
setWireMockScenarioState(historyScenario, initialState) setWireMockScenarioState(historyScenario, initialState)
}, },
@ -126,7 +129,7 @@ class TangemPayTest : BaseTestCase() {
step("Switch WireMock scenario '$historyScenario' to '$afterTransactionState'") { step("Switch WireMock scenario '$historyScenario' to '$afterTransactionState'") {
setWireMockScenarioState(historyScenario, afterTransactionState) setWireMockScenarioState(historyScenario, afterTransactionState)
} }
step("Pull to refresh") { pullToRefresh() } step("Pull to refresh") { pullToRefreshTangemPay() }
step("Assert transaction from '$merchantName' is displayed") { step("Assert transaction from '$merchantName' is displayed") {
onTangemPayMainScreen { onTangemPayMainScreen {
transactionRowWithText(merchantName).assertIsDisplayed() transactionRowWithText(merchantName).assertIsDisplayed()
@ -138,12 +141,13 @@ class TangemPayTest : BaseTestCase() {
@AllureId("4974") @AllureId("4974")
@DisplayName("Tangem Pay: reveal and copy card number, expiration and CVC") @DisplayName("Tangem Pay: reveal and copy card number, expiration and CVC")
@Test @Test
fun revealAndCopyCardDetails_NumberExpirationCVC() { fun revealAndCopyCardDetailsNumberExpirationCVCTest() {
val context = InstrumentationRegistry.getInstrumentation().targetContext val context = InstrumentationRegistry.getInstrumentation().targetContext
val eligibilityState = "PaeraCustomer" val eligibilityState = "PaeraCustomer"
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
}, },
additionalAfterSection = { additionalAfterSection = {
@ -200,13 +204,14 @@ class TangemPayTest : BaseTestCase() {
@AllureId("4971") @AllureId("4971")
@DisplayName("Tangem Pay: freeze card via confirmation sheet") @DisplayName("Tangem Pay: freeze card via confirmation sheet")
@Test @Test
fun freezeUnfreezeCard_TogglesCardState() { fun freezeUnfreezeCardTogglesCardStateTest() {
val freezeScenario = "tangem_pay_card_freeze" val freezeScenario = "tangem_pay_card_freeze"
val startedState = "Started" val startedState = "Started"
val eligibilityState = "PaeraCustomer" val eligibilityState = "PaeraCustomer"
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
setWireMockScenarioState(freezeScenario, startedState) setWireMockScenarioState(freezeScenario, startedState)
}, },

View file

@ -9,6 +9,7 @@ import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
import com.tangem.common.extensions.assertTextContainsSafe import com.tangem.common.extensions.assertTextContainsSafe
import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.extensions.clickWithAssertion
import com.tangem.common.utils.resetWireMockScenarioState import com.tangem.common.utils.resetWireMockScenarioState
import com.tangem.common.utils.resetWireMockScenarios
import com.tangem.common.utils.setWireMockScenarioState import com.tangem.common.utils.setWireMockScenarioState
import com.tangem.core.res.R as CoreResR import com.tangem.core.res.R as CoreResR
import com.tangem.scenarios.* import com.tangem.scenarios.*
@ -26,7 +27,7 @@ class TangemPayTopUpTest : BaseTestCase() {
@AllureId("4973") @AllureId("4973")
@DisplayName("Tangem Pay: top up swaps Bitcoin to USDC and appends deposit to history") @DisplayName("Tangem Pay: top up swaps Bitcoin to USDC and appends deposit to history")
@Test @Test
fun topUpFromTangemPay_SwapsBitcoinToUSDC_AppendsDepositToHistory() { fun topUpFromTangemPaySwapsBitcoinToUSDCAppendsDepositToHistoryTest() {
val bitcoinScenario = "bitcoin_utxo" val bitcoinScenario = "bitcoin_utxo"
val expressAssetsScenario = "express_api_assets" val expressAssetsScenario = "express_api_assets"
val balanceScenario = "tangem_pay_balance_update" val balanceScenario = "tangem_pay_balance_update"
@ -43,6 +44,7 @@ class TangemPayTopUpTest : BaseTestCase() {
setupHooks( setupHooks(
additionalBeforeSection = { additionalBeforeSection = {
resetWireMockScenarios()
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
setWireMockScenarioState(bitcoinScenario, bitcoinBalanceState) setWireMockScenarioState(bitcoinScenario, bitcoinBalanceState)
setWireMockScenarioState(expressAssetsScenario, expressAssetsState) setWireMockScenarioState(expressAssetsScenario, expressAssetsState)

View file

@ -1,5 +0,0 @@
package com.tangem.core.ui.test
object AddFundsBottomSheetTestTags {
const val CLOSE_BUTTON = "ADD_FUNDS_BOTTOM_SHEET_CLOSE_BUTTON"
}

View file

@ -21,7 +21,7 @@ import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemeRedesign import com.tangem.core.ui.res.TangemThemeRedesign
import com.tangem.core.ui.test.AddFundsBottomSheetTestTags import com.tangem.core.ui.test.BaseBottomSheetTestTags
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent
import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel
@ -201,7 +201,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor(
}, },
endContent = { endContent = {
TangemButton( TangemButton(
modifier = Modifier.testTag(AddFundsBottomSheetTestTags.CLOSE_BUTTON), modifier = Modifier.testTag(BaseBottomSheetTestTags.CLOSE_BUTTON),
iconStart = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_close_24), iconStart = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_close_24),
onClick = onCloseClick, onClick = onCloseClick,
size = TangemButton.Size.X11, size = TangemButton.Size.X11,

View file

@ -177,7 +177,7 @@ private fun SimpleTransactionCardEmpty(
style = TangemTheme.typography.body2, style = TangemTheme.typography.body2,
modifier = Modifier modifier = Modifier
.defaultMinSize(minHeight = TangemTheme.dimens.size20) .defaultMinSize(minHeight = TangemTheme.dimens.size20)
.testTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT), .testTag(SwapTokenScreenTestTags.RECEIVE_FIAT_AMOUNT),
) )
} }
SecondarySmallButton( SecondarySmallButton(
@ -239,7 +239,7 @@ private fun SimpleTransactionCardLoading(modifier: Modifier = Modifier) {
style = TangemTheme.typography.body2, style = TangemTheme.typography.body2,
modifier = Modifier modifier = Modifier
.defaultMinSize(minHeight = 20.dp, minWidth = 40.dp) .defaultMinSize(minHeight = 20.dp, minWidth = 40.dp)
.testTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT), .testTag(SwapTokenScreenTestTags.RECEIVE_FIAT_AMOUNT),
) )
} }
SecondarySmallButton( SecondarySmallButton(