diff --git a/.claude/skills/write-ui-test/SKILL.md b/.claude/skills/write-ui-test/SKILL.md index fe33038941..86bf3b1cd0 100644 --- a/.claude/skills/write-ui-test/SKILL.md +++ b/.claude/skills/write-ui-test/SKILL.md @@ -62,6 +62,9 @@ When the user asks to **port** an iOS test to Android: ### 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 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 diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/SwapScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/SwapScenarios.kt index 91a2a2fa49..fa9a604135 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/SwapScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/SwapScenarios.kt @@ -323,6 +323,7 @@ fun BaseTestCase.switchFeeTokenAndApply(currentFeeToken: String, newFeeToken: St } } step("Click on 'Apply' button") { + waitForIdle() onSendFeeSelectorBottomSheet { applyButton.performClick() } } } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/AddFundsBottomSheetPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/AddFundsBottomSheetPageObject.kt index f27e8c0785..e2e2b20eb2 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/AddFundsBottomSheetPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/AddFundsBottomSheetPageObject.kt @@ -3,7 +3,6 @@ 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.AddFundsBottomSheetTestTags import com.tangem.core.ui.test.BaseBottomSheetTestTags import com.tangem.core.ui.test.BuyTokenScreenTestTags import com.tangem.core.ui.test.MarketsTestTags @@ -38,7 +37,7 @@ class AddFundsBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractions } val closeButton: KNode = child { - hasTestTag(AddFundsBottomSheetTestTags.CLOSE_BUTTON) + hasTestTag(BaseBottomSheetTestTags.CLOSE_BUTTON) useUnmergedTree = true } diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt index f9312fa84b..a0374130c3 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/SwapTokenPageObject.kt @@ -189,6 +189,7 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) val swapFiatAmount: KNode = child { hasTestTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT) + useUnmergedTree = true } val swapSelectTokenIcon: KNode = child { diff --git a/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTest.kt index f9925bcdcc..5d87dfedf1 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTest.kt @@ -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.clickWithAssertion import com.tangem.common.extensions.extractText -import com.tangem.common.extensions.pullToRefresh import com.tangem.common.utils.assertClipboardTextEquals import com.tangem.common.utils.resetWireMockScenarioState +import com.tangem.common.utils.resetWireMockScenarios import com.tangem.common.utils.setWireMockScenarioState import com.tangem.scenarios.* import com.tangem.screens.tangempay.* @@ -23,7 +23,7 @@ class TangemPayTest : BaseTestCase() { @AllureId("4549") @DisplayName("Tangem Pay: change PIN code from card details") @Test - fun changePin_SetsNewPinCode_FromCardDetails() { + fun changePinSetsNewPinCodeFromCardDetailsTest() { val newPin = "5217" val pinSetupScenario = "tangem_pay_pin_setup" val pinNotSetState = "PinNotSet" @@ -31,6 +31,7 @@ class TangemPayTest : BaseTestCase() { setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(pinSetupScenario, pinNotSetState) }, @@ -67,7 +68,7 @@ class TangemPayTest : BaseTestCase() { @AllureId("4969") @DisplayName("Tangem Pay: balance updates after transaction on payment account screen") @Test - fun balanceUpdatesAfterTransaction_OnPaymentAccountScreen() { + fun balanceUpdatesAfterTransactionOnPaymentAccountScreenTest() { val balanceScenario = "tangem_pay_balance_update" val initialState = "InitialBalance" val afterTransactionState = "AfterTransaction" @@ -75,6 +76,7 @@ class TangemPayTest : BaseTestCase() { setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(balanceScenario, initialState) }, @@ -90,7 +92,7 @@ class TangemPayTest : BaseTestCase() { step("Switch WireMock scenario '$balanceScenario' to '$afterTransactionState'") { setWireMockScenarioState(balanceScenario, afterTransactionState) } - step("Pull to refresh") { pullToRefresh() } + step("Pull to refresh") { pullToRefreshTangemPay() } step("Assert updated balance contains '9'") { onTangemPayMainScreen { balance.assertTextContainsSafe("9", substring = true) } } @@ -100,7 +102,7 @@ class TangemPayTest : BaseTestCase() { @AllureId("4970") @DisplayName("Tangem Pay: new transaction appears after mocked charge") @Test - fun transactionList_NewTransactionAppears_AfterMockedCharge() { + fun transactionListNewTransactionAppearsAfterMockedChargeTest() { val historyScenario = "tangem_pay_transaction_history" val initialState = "InitialEmpty" val afterTransactionState = "AfterTransaction" @@ -109,6 +111,7 @@ class TangemPayTest : BaseTestCase() { setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(historyScenario, initialState) }, @@ -126,7 +129,7 @@ class TangemPayTest : BaseTestCase() { step("Switch WireMock scenario '$historyScenario' to '$afterTransactionState'") { setWireMockScenarioState(historyScenario, afterTransactionState) } - step("Pull to refresh") { pullToRefresh() } + step("Pull to refresh") { pullToRefreshTangemPay() } step("Assert transaction from '$merchantName' is displayed") { onTangemPayMainScreen { transactionRowWithText(merchantName).assertIsDisplayed() @@ -138,12 +141,13 @@ class TangemPayTest : BaseTestCase() { @AllureId("4974") @DisplayName("Tangem Pay: reveal and copy card number, expiration and CVC") @Test - fun revealAndCopyCardDetails_NumberExpirationCVC() { + fun revealAndCopyCardDetailsNumberExpirationCVCTest() { val context = InstrumentationRegistry.getInstrumentation().targetContext val eligibilityState = "PaeraCustomer" setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) }, additionalAfterSection = { @@ -200,13 +204,14 @@ class TangemPayTest : BaseTestCase() { @AllureId("4971") @DisplayName("Tangem Pay: freeze card via confirmation sheet") @Test - fun freezeUnfreezeCard_TogglesCardState() { + fun freezeUnfreezeCardTogglesCardStateTest() { val freezeScenario = "tangem_pay_card_freeze" val startedState = "Started" val eligibilityState = "PaeraCustomer" setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(freezeScenario, startedState) }, diff --git a/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTopUpTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTopUpTest.kt index 4143126b3a..5c8ca21fe3 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTopUpTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/tangempay/TangemPayTopUpTest.kt @@ -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.clickWithAssertion import com.tangem.common.utils.resetWireMockScenarioState +import com.tangem.common.utils.resetWireMockScenarios import com.tangem.common.utils.setWireMockScenarioState import com.tangem.core.res.R as CoreResR import com.tangem.scenarios.* @@ -26,7 +27,7 @@ class TangemPayTopUpTest : BaseTestCase() { @AllureId("4973") @DisplayName("Tangem Pay: top up swaps Bitcoin to USDC and appends deposit to history") @Test - fun topUpFromTangemPay_SwapsBitcoinToUSDC_AppendsDepositToHistory() { + fun topUpFromTangemPaySwapsBitcoinToUSDCAppendsDepositToHistoryTest() { val bitcoinScenario = "bitcoin_utxo" val expressAssetsScenario = "express_api_assets" val balanceScenario = "tangem_pay_balance_update" @@ -43,6 +44,7 @@ class TangemPayTopUpTest : BaseTestCase() { setupHooks( additionalBeforeSection = { + resetWireMockScenarios() setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState) setWireMockScenarioState(bitcoinScenario, bitcoinBalanceState) setWireMockScenarioState(expressAssetsScenario, expressAssetsState) diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/AddFundsBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/AddFundsBottomSheetTestTags.kt deleted file mode 100644 index b7d46405a3..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/test/AddFundsBottomSheetTestTags.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.tangem.core.ui.test - -object AddFundsBottomSheetTestTags { - const val CLOSE_BUTTON = "ADD_FUNDS_BOTTOM_SHEET_CLOSE_BUTTON" -} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt index 0d1cc947db..011dbadec6 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt @@ -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.res.TangemTheme 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.choosetoken.ChooseTokenComponent import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel @@ -201,7 +201,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( }, endContent = { TangemButton( - modifier = Modifier.testTag(AddFundsBottomSheetTestTags.CLOSE_BUTTON), + modifier = Modifier.testTag(BaseBottomSheetTestTags.CLOSE_BUTTON), iconStart = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_close_24), onClick = onCloseClick, size = TangemButton.Size.X11, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCardSimple.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCardSimple.kt index 314841491e..6e5d64bbd8 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCardSimple.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCardSimple.kt @@ -177,7 +177,7 @@ private fun SimpleTransactionCardEmpty( style = TangemTheme.typography.body2, modifier = Modifier .defaultMinSize(minHeight = TangemTheme.dimens.size20) - .testTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT), + .testTag(SwapTokenScreenTestTags.RECEIVE_FIAT_AMOUNT), ) } SecondarySmallButton( @@ -239,7 +239,7 @@ private fun SimpleTransactionCardLoading(modifier: Modifier = Modifier) { style = TangemTheme.typography.body2, modifier = Modifier .defaultMinSize(minHeight = 20.dp, minWidth = 40.dp) - .testTag(SwapTokenScreenTestTags.SWAP_FIAT_AMOUNT), + .testTag(SwapTokenScreenTestTags.RECEIVE_FIAT_AMOUNT), ) } SecondarySmallButton(