Updated on 2026-08-14
This commit is contained in:
parent
97f2810845
commit
91200c4973
20 changed files with 843 additions and 5 deletions
|
|
@ -201,4 +201,8 @@ Delete anything explaining WHAT a step does.
|
|||
- **`reference/running-and-debugging.md`** — read when building, installing, running tests (orchestrator
|
||||
vs. raw `am instrument`), running against a local WireMock, interpreting CLI/Allure output, using
|
||||
`@Ignore`, or driving WireMock scenarios. Includes how to find app-side root causes when the UI fails
|
||||
silently (the app log in `files/log.txt`, and the WireMock journal).
|
||||
silently (the app log in `files/log.txt`, and the WireMock journal).
|
||||
- **`reference/yield-mode.md`** — read before writing any **Yield Mode (yield-supply / "Earning")** test.
|
||||
Covers the hot-wallet activation flow, why Ethereum (not Polygon — gasless), the full mock set +
|
||||
scenarios, the `isActive`-semantics / lowercase-address / hold-timing / Pill-testTag gotchas, and the
|
||||
production testTags already added. Mirror `tests/yield/YieldModeTest.kt`.
|
||||
96
.claude/skills/write-ui-test/reference/yield-mode.md
Normal file
96
.claude/skills/write-ui-test/reference/yield-mode.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Yield Mode (yield-supply) UI tests
|
||||
|
||||
Hard-won specifics for testing the Yield Mode feature (`features/yield-supply`, analytics category
|
||||
"Earning"). The first test (`app/.../tests/yield/YieldModeTest.kt`, case #4938 "first-time landing
|
||||
activation") is the reference — mirror it. Read this before writing any yield test.
|
||||
|
||||
## What the feature is
|
||||
|
||||
Depositing a stablecoin (USDC/USDT) into a DeFi protocol (Aave) from the app to earn APY. Distinct from
|
||||
Staking (`domain/staking`) but both render the shared `EarnBlock` (`common/ui/.../earn/`) on token
|
||||
details. Activation = a **real signed transaction** (approve + enter), so it needs a **hot wallet**
|
||||
(mock card can't sign). Use `openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12)`.
|
||||
|
||||
## Network: use Ethereum, NOT Polygon
|
||||
|
||||
- **Ethereum USDC** activation is a **native ETH** transaction → the standard send path works.
|
||||
- **Polygon USDC** activation routes through the **gasless** flow (`GASLESS_APPROVAL_ENABLED=true` +
|
||||
Polygon USDC is gasless-eligible) → `gaslessTransaction()`; the SDK's `EthereumTransactionValidator`
|
||||
throws `FailedToSendException` synchronously and the send never completes. Avoid Polygon for yield
|
||||
send/activation tests unless you specifically mock the whole gasless-v2 stack.
|
||||
- Wallet (SVS_SEED_PHRASE_12) EVM address: `0x3369554b994908d249d307b105f8e5e3115615c2`.
|
||||
- Ethereum USDC contract: `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` (chainId 1, decimals 6).
|
||||
|
||||
## Running (same as the general doc, repeated because it bit us)
|
||||
|
||||
Run **via the orchestrator**, never raw `am instrument` (with `am instrument` the fresh-wallet portfolio
|
||||
silently never loads):
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8081/__admin/mappings/reset # after editing mocks
|
||||
./gradlew :app:connectedGoogleMockedAndroidTest \
|
||||
-Pandroid.testInstrumentationRunnerArguments.class=com.tangem.tests.yield.YieldModeTest \
|
||||
-Pandroid.testInstrumentationRunnerArguments.wiremockBaseUrl=http://10.0.2.2:8081
|
||||
```
|
||||
|
||||
WireMock on 8081 is a docker container bind-mounting the `tangem-api-mocks` working tree — `mappings/reset`
|
||||
reloads from disk, no rebuild. App-side logs (`TangemLogger`) also reach logcat; the failure semantics
|
||||
tree is dumped to logcat (`ComposeTree`).
|
||||
|
||||
## Mock set (in tangem-api-mocks, branch `feature/AND-16082_yield_mode_mocks`)
|
||||
|
||||
- **Yield API** (`mappings/yield-api/api/v1/`): `yield/markets`, `yield/token/1/{usdc}` (+ `/chart`),
|
||||
`module/activate`, `module/deactivate`. Base URL is `wiremock.tests-d.com` (see infra fix below) so the
|
||||
path is `/api/v1/yield/...`.
|
||||
- **On-chain eth_call** (`mappings/providers/ethereum/eth-call-yield.json`, priority 1 to beat the generic
|
||||
`eth-call.json`): factory `getModule` (to=`0xd8972a45...`), processor service-fee (to=`0x4ff6178b...`),
|
||||
yield status `0xf8e8be9c` + balances `0x16a398f7`/`0x5002bb7e` (to = module `0x1111…`), allowance
|
||||
`0xdd62ed3e` (to = USDC) → 0. Status/balances gated by scenario `yield_supply_status` (NotActive=zeros,
|
||||
Active=`…01`,`…01`,`…8ac7230489e80000`).
|
||||
- **History** (`mappings/eth-blockbook.nownodes.io/`): NowNodes BlockBook v2. Ethereum mainnet tx history
|
||||
is **NowNodes eth-blockbook**, NOT Etherscan and NOT `/v2/transaction-events` (the latter is a
|
||||
push-dedup POST). The host is redirected by the interceptor (infra fix below).
|
||||
- **Portfolio**: scenario `user_tokens_api` state `YieldUSDCEthereum` (accounts API; native ETH + USDC),
|
||||
balances via `moralis_evm_token_balances_api=NonZeroEvmBalances`.
|
||||
|
||||
## Scenarios the test drives
|
||||
|
||||
`user_tokens_api=YieldUSDCEthereum`, `moralis_evm_token_balances_api=NonZeroEvmBalances`, and
|
||||
`yield_supply_status` **NotActive → (after activation) Active**. The Active flip is what turns the
|
||||
EarnBlock from "available" into the active "Yield Mode enabled / Average APY" state AND surfaces the
|
||||
history row.
|
||||
|
||||
## Gotchas that cost real time
|
||||
|
||||
1. **`isActive` in `yield/markets` & `yield/token` means "market is available", not "user activated".**
|
||||
It MUST be `true` or `YieldSupplyTokenStatusSuccessTransformer` returns `Unavailable` and the block
|
||||
never renders. User activation is tracked separately by the on-chain `eth_call` status flip.
|
||||
2. **`yieldSupplyKey` matching is case-sensitive string equality** (`"${backendId}_$tokenAddress"` vs
|
||||
`"${network.rawId}_$contractAddress"`). All addresses in mocks must be **lowercase** — every existing
|
||||
mock is. A checksummed address yields `yieldSupplyApy size=0` and no available block.
|
||||
3. **Active vs available is driven by `CryptoCurrencyStatus.value.yieldSupplyStatus.isActive`** (on-chain
|
||||
eth_call `0xf8e8be9c`), not the API `isActive`. Flip the `yield_supply_status` scenario + pull-to-refresh.
|
||||
4. **Infra fix (production, already applied):** `YieldSupply` ApiConfig MOCK base URL was `yield.tests-d.com`
|
||||
(NOT redirected by `WireMockRedirectInterceptor`) → fixed to `wiremock.tests-d.com`. And
|
||||
`eth-blockbook.nownodes.io` was added to `REDIRECTABLE_THIRD_PARTY_HOSTS`. Without these the yield/history
|
||||
requests bypass WireMock entirely.
|
||||
5. **Hold-to-confirm must wait for the fee.** The "Start earning" `HoldToConfirmButton` is disabled
|
||||
(`holdToConfirmGestures(enabled=false)` swallows the gesture) until the fee is calculated. Don't gate on
|
||||
the high-fee notification (it's absent on cheap chains) — use the fee-agnostic retry: a single step that
|
||||
`flakySafely`-retries `longClick(HOLD_DURATION_MS)` then asserts the sheet closed
|
||||
(`startEarningButton.assertIsNotDisplayed()`). `state.isConfirmed` makes re-holds no-ops, so no double-send.
|
||||
A hold step that finishes in ~270 ms instead of ~2 s = the gesture was swallowed (button disabled).
|
||||
6. **The yield-enter history row is a `TransactionItemUM.Pill`** (converter maps `YieldSupply.Enter` →
|
||||
Pill, like Approve/Staking), rendered by `TransactionStatusPill`. It now carries
|
||||
`TransactionHistoryItemTestTags.ITEM` (added for parity with `ContentItem`) so `transactionItem(title)`
|
||||
finds it. Title string = `yield_module_transaction_enter` ("Yield Mode enabled" / "Режим доходности
|
||||
подключен"). The row opens the explorer on click.
|
||||
7. **"Nothing to add to TxHistory"** in logs is NOT an error — it refers to recent/pending txs, separate
|
||||
from the API history.
|
||||
|
||||
## testTags added to production (reuse, don't re-add)
|
||||
|
||||
`TokenDetailsScreenTestTags.YIELD_SUPPLY_BLOCK` / `YIELD_SUPPLY_AVAILABLE_BLOCK`; `YieldSupplyTestTags`
|
||||
(`PROMO_CONTINUE_BUTTON`, `START_EARNING_BUTTON`); `TransactionHistoryItemTestTags.ITEM` now on
|
||||
`TransactionStatusPill`. Page objects: yield locators in `TokenDetailsPageObject`,
|
||||
`YieldSupplyPromoPageObject`, `YieldSupplyStartEarningPageObject`.
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.test.BaseButtonTestTags
|
|||
import com.tangem.core.ui.test.NotificationTestTags
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
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
|
||||
|
|
@ -62,6 +63,33 @@ class TokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvide
|
|||
hasText(getResourceString(R.string.staking_enabled))
|
||||
}
|
||||
|
||||
val yieldSupplyAvailableBlock: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.YIELD_SUPPLY_AVAILABLE_BLOCK)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val yieldSupplyActiveBlock: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.YIELD_SUPPLY_BLOCK)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val earnBlockTitleIcon: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.EARN_BLOCK_TITLE_ICON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val yieldModeConnectedTitle: KNode = child {
|
||||
hasAnyAncestor(withTestTag(TokenDetailsScreenTestTags.YIELD_SUPPLY_BLOCK))
|
||||
hasText(getResourceString(CoreResR.string.yield_module_transaction_enter))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun yieldModeApy(apy: String): KNode = child {
|
||||
hasAnyAncestor(withTestTag(TokenDetailsScreenTestTags.YIELD_SUPPLY_BLOCK))
|
||||
hasText(getResourceString(CoreResR.string.yield_module_average_apy, apy))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TokenDetailsScreenTestTags.TOKEN_TITLE)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
import com.tangem.core.ui.test.NotificationTestTags
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
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 YieldSupplyActivePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<YieldSupplyActivePageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val stopEarningButton: KNode = child {
|
||||
hasTestTag(YieldSupplyTestTags.STOP_EARNING_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val approveButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(CoreResR.string.yield_module_approve_needed_notification_cta))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun notificationTitle(title: String): KNode = child {
|
||||
hasTestTag(NotificationTestTags.TITLE)
|
||||
hasText(title)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onYieldSupplyActiveScreen(function: YieldSupplyActivePageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
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
|
||||
|
||||
class YieldSupplyApprovePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<YieldSupplyApprovePageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val confirmButton: KNode = child {
|
||||
hasTestTag(YieldSupplyTestTags.APPROVE_CONFIRM_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onYieldSupplyApproveScreen(function: YieldSupplyApprovePageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
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
|
||||
|
||||
class YieldSupplyPromoPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<YieldSupplyPromoPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val continueButton: KNode = child {
|
||||
hasTestTag(YieldSupplyTestTags.PROMO_CONTINUE_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onYieldSupplyPromoScreen(function: YieldSupplyPromoPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
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
|
||||
|
||||
class YieldSupplyStartEarningPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<YieldSupplyStartEarningPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val startEarningButton: KNode = child {
|
||||
hasTestTag(YieldSupplyTestTags.START_EARNING_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onYieldSupplyStartEarningScreen(function: YieldSupplyStartEarningPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
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
|
||||
|
||||
class YieldSupplyStopEarningPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<YieldSupplyStopEarningPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val confirmButton: KNode = child {
|
||||
hasTestTag(YieldSupplyTestTags.STOP_EARNING_CONFIRM_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onYieldSupplyStopEarningScreen(function: YieldSupplyStopEarningPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,548 @@
|
|||
package com.tangem.tests.yield
|
||||
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.longClick
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.HOLD_DURATION_MS
|
||||
import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.pullToRefresh
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.openMainScreenWithExistingHotWallet
|
||||
import com.tangem.screens.onMainScreen
|
||||
import com.tangem.screens.onTokenDetailsScreen
|
||||
import com.tangem.screens.onTokenDetailsTopBar
|
||||
import com.tangem.screens.onTxHistoryScreen
|
||||
import com.tangem.screens.onYieldSupplyActiveScreen
|
||||
import com.tangem.screens.onYieldSupplyApproveScreen
|
||||
import com.tangem.screens.onYieldSupplyPromoScreen
|
||||
import com.tangem.screens.onYieldSupplyStartEarningScreen
|
||||
import com.tangem.screens.onYieldSupplyStopEarningScreen
|
||||
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
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
@HiltAndroidTest
|
||||
class YieldModeTest : BaseTestCase() {
|
||||
|
||||
@AllureId("7957")
|
||||
@DisplayName("Yield mode: first-time landing activation with zero balance")
|
||||
@Test
|
||||
fun firstTimeLandingActivationZeroBalanceTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val apy = "5.24"
|
||||
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereumZeroBalance"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "ZeroUsdcEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldNotActiveState = "NotActive"
|
||||
val yieldActiveState = "Active"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldNotActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldNotActiveState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Available yield block' is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click on 'Available yield block'") {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Continue' button") {
|
||||
onYieldSupplyPromoScreen { continueButton.clickWithAssertion() }
|
||||
}
|
||||
step("Hold 'Start earning' button to confirm activation") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyStartEarningScreen {
|
||||
startEarningButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
startEarningButton.assertIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldActiveState)
|
||||
}
|
||||
step("Assert 'Yield mode enabled' block is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldModeConnectedTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Average APY' is displayed in yield block") {
|
||||
onTokenDetailsScreen { yieldModeApy(apy).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4938")
|
||||
@DisplayName("Yield mode: first-time landing activation")
|
||||
@Test
|
||||
fun firstTimeLandingActivationTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val apy = "5.24"
|
||||
val enterTransactionTitle = getResourceString(CoreResR.string.yield_module_transaction_enter)
|
||||
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereum"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "NonZeroEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldNotActiveState = "NotActive"
|
||||
val yieldActiveState = "Active"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldNotActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldNotActiveState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Available yield block' is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click on 'Available yield block'") {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Continue' button") {
|
||||
onYieldSupplyPromoScreen { continueButton.clickWithAssertion() }
|
||||
}
|
||||
step("Hold 'Start earning' button to confirm activation") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyStartEarningScreen {
|
||||
startEarningButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
startEarningButton.assertIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldActiveState)
|
||||
}
|
||||
step("Perform pull to refresh") {
|
||||
pullToRefresh()
|
||||
}
|
||||
step("Assert 'Yield mode enabled' block is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldModeConnectedTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Average APY' is displayed in yield block") {
|
||||
onTokenDetailsScreen { yieldModeApy(apy).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Yield mode enabled' transaction is displayed in history") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTxHistoryScreen {
|
||||
transactionItem(enterTransactionTitle).assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("5473")
|
||||
@DisplayName("Yield mode: top-up for active landing")
|
||||
@Test
|
||||
fun topUpActiveLandingTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val topUpAmount = "600.00"
|
||||
val receivedTransactionTitle = getResourceString(CoreResR.string.common_received)
|
||||
val topUpTransactionTitle = getResourceString(CoreResR.string.yield_module_transaction_topup)
|
||||
val supplyingNotificationTitle = getResourceString(
|
||||
CoreResR.string.yield_module_amount_not_transfered_to_aave_title,
|
||||
topUpAmount,
|
||||
tokenTitle,
|
||||
)
|
||||
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereum"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "NonZeroEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldTopUpState = "TopUp"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldTopUpState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldTopUpState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Not supplied' info icon is displayed in yield block") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { earnBlockTitleIcon.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Received' transaction is displayed in history") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTxHistoryScreen { transactionItem(receivedTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Supply to Aave' transaction is displayed in history") {
|
||||
onTxHistoryScreen { transactionItem(topUpTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
|
||||
step("Click on 'Yield mode enabled' block") {
|
||||
onTokenDetailsScreen { yieldSupplyActiveBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Supplying to Aave' notification is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyActiveScreen { notificationTitle(supplyingNotificationTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("7960")
|
||||
@DisplayName("Yield mode: granting approval")
|
||||
@Test
|
||||
fun grantApprovalTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val approveNeededTitle = getResourceString(CoreResR.string.yield_module_approve_needed_notification_title)
|
||||
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereum"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "NonZeroEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldApproveNeededState = "Active"
|
||||
val yieldApproveGrantedState = "ApproveGranted"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldApproveNeededState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldApproveNeededState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Approve needed' info icon is displayed in yield block") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { earnBlockTitleIcon.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click on 'Yield mode enabled' block") {
|
||||
onTokenDetailsScreen { yieldSupplyActiveBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Approve needed' notification is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyActiveScreen { notificationTitle(approveNeededTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Approve' button") {
|
||||
onYieldSupplyActiveScreen { approveButton.clickWithAssertion() }
|
||||
}
|
||||
step("Hold 'Confirm' button to confirm approval") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyApproveScreen {
|
||||
confirmButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
confirmButton.assertIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldApproveGrantedState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldApproveGrantedState)
|
||||
}
|
||||
step("Perform pull to refresh") {
|
||||
pullToRefresh()
|
||||
}
|
||||
step("Assert 'Approve needed' info icon is not displayed in yield block") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { earnBlockTitleIcon.assertIsNotDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4940")
|
||||
@DisplayName("Yield mode: reopening landing")
|
||||
@Test
|
||||
fun reopenLandingActivationTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val nativeCoinTitle = "Ethereum"
|
||||
val apy = "5.24"
|
||||
val reactivateTransactionTitle = getResourceString(CoreResR.string.yield_module_transaction_reactivate)
|
||||
val enterTransactionTitle = getResourceString(CoreResR.string.yield_module_transaction_enter)
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereum"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "NonZeroEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldNotActiveState = "NotActive"
|
||||
val yieldActiveState = "Active"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldNotActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldNotActiveState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Available yield block' is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click on 'Available yield block'") {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Continue' button") {
|
||||
onYieldSupplyPromoScreen { continueButton.clickWithAssertion() }
|
||||
}
|
||||
step("Hold 'Start earning' button to confirm activation") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyStartEarningScreen {
|
||||
startEarningButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
startEarningButton.assertIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldActiveState)
|
||||
}
|
||||
step("Perform pull to refresh") {
|
||||
pullToRefresh()
|
||||
}
|
||||
step("Assert 'Yield mode enabled' block is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldModeConnectedTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Average APY' is displayed in yield block") {
|
||||
onTokenDetailsScreen { yieldModeApy(apy).assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Yield mode enabled' transaction is displayed in history") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTxHistoryScreen { transactionItem(enterTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click 'Back' button to return to 'Main Screen'") {
|
||||
onTokenDetailsTopBar { backButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on native coin with name: '$nativeCoinTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(nativeCoinTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Reactivate Token' transaction is displayed in history") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTxHistoryScreen { transactionItem(reactivateTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Assert 'Enter protocol' transaction is displayed in history") {
|
||||
onTxHistoryScreen { transactionItem(enterTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4937")
|
||||
@DisplayName("Yield mode: closing active landing")
|
||||
@Test
|
||||
fun closeActiveLandingTest() {
|
||||
val tokenTitle = "USDC"
|
||||
val nativeCoinTitle = "Ethereum"
|
||||
val exitTransactionTitle = getResourceString(CoreResR.string.yield_module_transaction_exit)
|
||||
val portfolioScenario = "user_tokens_api"
|
||||
val portfolioState = "YieldUSDCEthereum"
|
||||
val balancesScenario = "moralis_evm_token_balances_api"
|
||||
val balancesState = "NonZeroEvmBalances"
|
||||
val yieldScenario = "yield_supply_status"
|
||||
val yieldActiveState = "Active"
|
||||
val yieldExitedState = "Exited"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(portfolioScenario)
|
||||
resetWireMockScenarioState(balancesScenario)
|
||||
resetWireMockScenarioState(yieldScenario)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$portfolioScenario' to state: '$portfolioState'") {
|
||||
setWireMockScenarioState(scenarioName = portfolioScenario, state = portfolioState)
|
||||
}
|
||||
step("Set WireMock scenario: '$balancesScenario' to state: '$balancesState'") {
|
||||
setWireMockScenarioState(scenarioName = balancesScenario, state = balancesState)
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldActiveState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldActiveState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen' with existing hot wallet") {
|
||||
openMainScreenWithExistingHotWallet(seedPhrase = SVS_SEED_PHRASE_12)
|
||||
}
|
||||
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(tokenTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Yield mode enabled' block is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldModeConnectedTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Click on 'Yield mode enabled' block") {
|
||||
onTokenDetailsScreen { yieldSupplyActiveBlock.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Disable Yield Mode' button") {
|
||||
onYieldSupplyActiveScreen { stopEarningButton.clickWithAssertion() }
|
||||
}
|
||||
step("Hold 'Confirm' button to confirm exit") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onYieldSupplyStopEarningScreen {
|
||||
confirmButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
confirmButton.assertIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Set WireMock scenario: '$yieldScenario' to state: '$yieldExitedState'") {
|
||||
setWireMockScenarioState(scenarioName = yieldScenario, state = yieldExitedState)
|
||||
}
|
||||
|
||||
step("Assert 'Available yield block' is displayed") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTokenDetailsScreen { yieldSupplyAvailableBlock.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Click 'Back' button to return to 'Main Screen'") {
|
||||
onTokenDetailsTopBar { backButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on native coin with name: '$nativeCoinTitle'") {
|
||||
onMainScreen { tokenWithTitleAndAddress(nativeCoinTitle).clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Token details' screen is displayed") {
|
||||
onTokenDetailsScreen { screenContainer.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Yield mode disabled' transaction is displayed in history") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
onTxHistoryScreen { transactionItem(exitTransactionTitle).assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -304,7 +304,9 @@ private fun EarnBlockTitle(titleUM: EarnBlockUM.TitleUM, type: Type, modifier: M
|
|||
iconRes = icon.tone.iconRes(),
|
||||
tintReference = { icon.tone.tint() },
|
||||
),
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x4),
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x4)
|
||||
.testTag(TokenDetailsScreenTestTags.EARN_BLOCK_TITLE_ICON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class WireMockRedirectInterceptor : Interceptor {
|
|||
"deep-index.moralis.io",
|
||||
"solana-gateway.moralis.io",
|
||||
"api.etherscan.io",
|
||||
"eth-blockbook.nownodes.io",
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.test.TransactionHistoryItemTestTags
|
||||
import com.tangem.core.ui.components.icons.identicon.IdentIcon
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionItemUM
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionItemUM.Content.Status
|
||||
|
|
@ -45,6 +47,7 @@ internal fun TransactionStatusPill(
|
|||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TransactionHistoryItemTestTags.ITEM)
|
||||
.clickable(onClick = state.onClick)
|
||||
.padding(horizontal = TangemTheme.dimens2.x4, vertical = TangemTheme.dimens2.x2),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ object TokenDetailsScreenTestTags {
|
|||
const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE"
|
||||
const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON"
|
||||
|
||||
const val YIELD_SUPPLY_BLOCK = "TOKEN_DETAILS_SCREEN_YIELD_SUPPLY_BLOCK"
|
||||
const val YIELD_SUPPLY_AVAILABLE_BLOCK = "TOKEN_DETAILS_SCREEN_YIELD_SUPPLY_AVAILABLE_BLOCK"
|
||||
const val EARN_BLOCK_TITLE_ICON = "TOKEN_DETAILS_SCREEN_EARN_BLOCK_TITLE_ICON"
|
||||
|
||||
const val EXPRESS_STATUS_ITEM = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM"
|
||||
const val EXPRESS_STATUS_ITEM_TITLE = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_TITLE"
|
||||
const val EXPRESS_STATUS_ITEM_FROM_ICON = "TOKEN_DETAILS_SCREEN_EXPRESS_STATUS_ITEM_FROM_ICON"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object YieldSupplyTestTags {
|
||||
const val PROMO_CONTINUE_BUTTON = "YIELD_SUPPLY_PROMO_CONTINUE_BUTTON"
|
||||
const val START_EARNING_BUTTON = "YIELD_SUPPLY_START_EARNING_BUTTON"
|
||||
const val STOP_EARNING_BUTTON = "YIELD_SUPPLY_STOP_EARNING_BUTTON"
|
||||
const val STOP_EARNING_CONFIRM_BUTTON = "YIELD_SUPPLY_STOP_EARNING_CONFIRM_BUTTON"
|
||||
const val APPROVE_CONFIRM_BUTTON = "YIELD_SUPPLY_APPROVE_CONFIRM_BUTTON"
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.core.ui.components.SecondaryButton
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -101,7 +103,8 @@ internal class DefaultYieldSupplyActiveComponent @AssistedInject constructor(
|
|||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp,
|
||||
),
|
||||
)
|
||||
.testTag(YieldSupplyTestTags.STOP_EARNING_BUTTON),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package com.tangem.features.yield.supply.impl.main
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.earn.EarnBlock
|
||||
import com.tangem.common.ui.earn.EarnBlockUM
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import com.tangem.features.yield.supply.impl.main.model.YieldSupplyModel
|
||||
import com.tangem.features.yield.supply.impl.main.ui.YieldSupplyBlockContentLegacy
|
||||
|
|
@ -26,7 +29,16 @@ internal class DefaultYieldSupplyComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val earnBlockUM by model.uiState.collectAsStateWithLifecycle()
|
||||
earnBlockUM?.let { EarnBlock(state = it, modifier = modifier) }
|
||||
earnBlockUM?.let { blockUM ->
|
||||
val tag = if (blockUM is EarnBlockUM.Content &&
|
||||
blockUM.backgroundUM is EarnBlockUM.BackgroundUM.AccentSoft
|
||||
) {
|
||||
TokenDetailsScreenTestTags.YIELD_SUPPLY_AVAILABLE_BLOCK
|
||||
} else {
|
||||
TokenDetailsScreenTestTags.YIELD_SUPPLY_BLOCK
|
||||
}
|
||||
EarnBlock(state = blockUM, modifier = modifier.testTag(tag))
|
||||
}
|
||||
} else {
|
||||
val yieldSupplyUM by model.uiStateLegacy.collectAsStateWithLifecycle()
|
||||
YieldSupplyBlockContentLegacy(yieldSupplyUM = yieldSupplyUM, modifier = modifier)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
|
|
@ -37,6 +38,7 @@ import com.tangem.core.ui.components.label.entity.LabelUM
|
|||
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.YieldSupplyTestTags
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM
|
||||
import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoClickIntents
|
||||
|
|
@ -75,7 +77,8 @@ internal fun YieldSupplyPromoContent(
|
|||
end = 16.dp,
|
||||
bottom = 8.dp,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.testTag(YieldSupplyTestTags.PROMO_CONTINUE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -27,6 +28,7 @@ import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWi
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -78,6 +80,7 @@ internal class YieldSupplyApproveComponent(
|
|||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.testTag(YieldSupplyTestTags.APPROVE_CONFIRM_BUTTON)
|
||||
|
||||
if (state.isHoldToConfirmEnabled) {
|
||||
HoldToConfirmButton(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -24,6 +25,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
|||
import com.tangem.core.ui.decompose.ComposableModularContentComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -110,6 +112,7 @@ internal class YieldSupplyStartEarningComponent(
|
|||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.testTag(YieldSupplyTestTags.START_EARNING_BUTTON)
|
||||
|
||||
if (state.isHoldToConfirmEnabled) {
|
||||
HoldToConfirmButton(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -28,6 +29,7 @@ import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWi
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -82,6 +84,7 @@ internal class YieldSupplyStopEarningComponent(
|
|||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.testTag(YieldSupplyTestTags.STOP_EARNING_CONFIRM_BUTTON)
|
||||
|
||||
if (state.isHoldToConfirmEnabled) {
|
||||
HoldToConfirmButton(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue