From 1ac25df95858e70c61d48c7cfb855b7b26b37e0b Mon Sep 17 00:00:00 2001 From: Diyaeddine LAOUID Date: Mon, 18 May 2026 15:06:54 +0200 Subject: [PATCH 1/6] test(e2e): add detox for evm native staking --- .changeset/odd-pillows-smash.md | 7 + .../components/TransactionConfirm/index.tsx | 2 +- apps/ledger-live-mobile/e2e/page/index.ts | 6 + .../e2e/page/trade/evmDelegate.page.ts | 80 +++++++ .../e2e/specs/delegate/sei.spec.ts | 37 ++++ .../e2e/userdata/seiEvmStaking.json | 209 ++++++++++++++++++ e2e/desktop/tests/page/index.ts | 2 + .../tests/page/modal/evmDelegate.modal.ts | 45 ++++ e2e/desktop/tests/page/speculos.page.ts | 6 + e2e/desktop/tests/specs/delegateSEI.spec.ts | 49 ++++ e2e/mobile/page/index.ts | 6 + e2e/mobile/page/speculos.page.ts | 6 + e2e/mobile/page/trade/evmStake.page.ts | 82 +++++++ e2e/mobile/page/wallet/portfolio.page.ts | 2 +- e2e/mobile/specs/delegate/delegateSEI.spec.ts | 48 ++++ .../src/e2e/enum/Account.ts | 13 ++ .../src/e2e/enum/AppInfos.ts | 2 + .../src/e2e/enum/Currency.ts | 4 + .../src/e2e/enum/DeviceLabels.ts | 1 + .../src/e2e/enum/Network.ts | 1 + libs/ledger-live-common/src/e2e/speculos.ts | 46 ++++ .../src/families/evm/bridge/mock.ts | 3 + 22 files changed, 655 insertions(+), 2 deletions(-) create mode 100644 .changeset/odd-pillows-smash.md create mode 100644 apps/ledger-live-mobile/e2e/page/trade/evmDelegate.page.ts create mode 100644 apps/ledger-live-mobile/e2e/specs/delegate/sei.spec.ts create mode 100644 apps/ledger-live-mobile/e2e/userdata/seiEvmStaking.json create mode 100644 e2e/desktop/tests/page/modal/evmDelegate.modal.ts create mode 100644 e2e/desktop/tests/specs/delegateSEI.spec.ts create mode 100644 e2e/mobile/page/trade/evmStake.page.ts create mode 100644 e2e/mobile/specs/delegate/delegateSEI.spec.ts diff --git a/.changeset/odd-pillows-smash.md b/.changeset/odd-pillows-smash.md new file mode 100644 index 000000000000..b0e9045e67d2 --- /dev/null +++ b/.changeset/odd-pillows-smash.md @@ -0,0 +1,7 @@ +--- +"live-mobile": minor +"@ledgerhq/live-common": minor +"ledger-live-mobile-e2e-tests": minor +--- + +tests(e2e): add detox for evm native staking (sei_evm) and mock smoke under `apps/ledger-live-mobile/e2e` and Speculos delegate flow under `e2e/mobile` diff --git a/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx b/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx index cfe29301d907..4f2abef0e48f 100644 --- a/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx @@ -170,7 +170,7 @@ const TransactionConfirm = ({ const Footer = r?.footer; return ( - + diff --git a/apps/ledger-live-mobile/e2e/page/index.ts b/apps/ledger-live-mobile/e2e/page/index.ts index aac831fecb30..9b745b324b76 100644 --- a/apps/ledger-live-mobile/e2e/page/index.ts +++ b/apps/ledger-live-mobile/e2e/page/index.ts @@ -20,6 +20,7 @@ import SendPage from "./trade/send.page"; import SettingsGeneralPage from "./settings/settingsGeneral.page"; import SettingsPage from "./settings/settings.page"; import StakePage from "./trade/stake.page"; +import EvmDelegatePage from "./trade/evmDelegate.page"; import SwapPage from "./trade/swap.page"; import TransferMenuDrawer from "./wallet/transferMenu.drawer"; import WalletTabNavigatorPage from "./wallet/walletTabNavigator.page"; @@ -83,6 +84,7 @@ export class Application { private settingsPageInstance = lazyInit(SettingsPage); private settingsGeneralPageInstance = lazyInit(SettingsGeneralPage); private stakePageInstance = lazyInit(StakePage); + private evmDelegatePageInstance = lazyInit(EvmDelegatePage); private swapPageInstance = lazyInit(SwapPage); private transferMenuDrawerInstance = lazyInit(TransferMenuDrawer); private walletTabNavigatorPageInstance = lazyInit(WalletTabNavigatorPage); @@ -197,6 +199,10 @@ export class Application { return this.stakePageInstance(); } + public get evmDelegate() { + return this.evmDelegatePageInstance(); + } + public get swap() { return this.swapPageInstance(); } diff --git a/apps/ledger-live-mobile/e2e/page/trade/evmDelegate.page.ts b/apps/ledger-live-mobile/e2e/page/trade/evmDelegate.page.ts new file mode 100644 index 000000000000..b453fd9d100e --- /dev/null +++ b/apps/ledger-live-mobile/e2e/page/trade/evmDelegate.page.ts @@ -0,0 +1,80 @@ +import { expect } from "detox"; +import { Step } from "jest-allure2-reporter/api"; + +const FEE_ESTIMATION_TIMEOUT = 120000; + +export default class EvmDelegatePage { + startStakeButtonId = "account-quick-action-button-cta"; + addDelegationButtonId = "account-quick-action-button-addDelegation"; + validatorName = "Ledger by Figment"; + validatorRowId = "evm-validator-row-seivaloper1mockvalidator000000000000000000000001"; + amountInputId = "evm-delegation-amount-input"; + useMaxButtonId = "evm-delegation-use-max"; + amountContinueButtonId = "enabled-evm-delegation-amount-continue"; + delegationSummaryLabel = "Delegated assets"; + + @Step("Start EVM delegation flow from account quick actions") + async startDelegate() { + await waitForElementById(this.startStakeButtonId); + await tapById(this.startStakeButtonId); + } + + @Step("Wait for validator list to be visible") + async waitForValidatorListVisible() { + await waitForElementByText(this.validatorName); + } + + @Step("Select the first validator in the list") + async selectFirstValidator() { + await waitForElementByText(this.validatorName); + await tapById(this.validatorRowId); + if (await IsIdVisible(this.amountInputId)) { + return; + } + await tapByText(this.validatorName); + await waitForElementById(this.amountInputId); + } + + @Step("Set delegation amount to {amount}") + async setAmount(amount: string) { + await waitForElementById(this.amountInputId); + await typeTextById(this.amountInputId, amount); + } + + @Step("Wait until network fees are loaded and continue is enabled") + async waitForAmountContinueEnabled(timeout = FEE_ESTIMATION_TIMEOUT) { + await waitForElementById(this.amountContinueButtonId, timeout); + } + + @Step("Tap use max amount button") + async useMaxAmount() { + await waitForElementById(this.useMaxButtonId); + await tapById(this.useMaxButtonId); + await this.waitForAmountContinueEnabled(); + } + + @Step("Continue from amount screen") + async continueAmount() { + await waitForElementById(this.amountContinueButtonId); + await tapById(this.amountContinueButtonId); + } + + @Step("Set amount and continue once fees are ready") + async setAmountAndContinue(amount: string) { + await this.setAmount(amount); + await this.waitForAmountContinueEnabled(); + await this.continueAmount(); + } + + @Step("Expect delegated assets summary visible") + async expectDelegatedAssetsVisible() { + await waitForElementByText(this.delegationSummaryLabel); + await expect(getElementByText(this.delegationSummaryLabel)).toBeVisible(); + } + + @Step("Expect add delegation CTA visible") + async expectAddDelegationCtaVisible() { + await waitForElementById(this.addDelegationButtonId); + await expect(getElementById(this.addDelegationButtonId)).toBeVisible(); + } +} diff --git a/apps/ledger-live-mobile/e2e/specs/delegate/sei.spec.ts b/apps/ledger-live-mobile/e2e/specs/delegate/sei.spec.ts new file mode 100644 index 000000000000..fa15a4035c10 --- /dev/null +++ b/apps/ledger-live-mobile/e2e/specs/delegate/sei.spec.ts @@ -0,0 +1,37 @@ +describe("SEI EVM Native Staking - Delegate flow", () => { + const SEI_CURRENCY_ID = "sei_evm"; + const DELEGATION_AMOUNT = "2"; + const EMPTY_ACCOUNT_ID = "mock:2:sei_evm:0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707:"; + const STAKED_ACCOUNT_ID = "mock:2:sei_evm:0x18E9A4F2A5A2B01F35E7D086E75D7D01530A1A9F:"; + + async function selectAccount(accountId: string) { + const accountRowId = `account-row-${accountId}`; + if (!(await IsIdVisible(accountRowId))) { + await scrollToId(accountRowId); + } + await waitForElementById(accountRowId); + await tapById(accountRowId); + } + + beforeAll(async () => { + await app.init({ + userdata: "seiEvmStaking", + }); + await app.portfolio.waitForPortfolioPageToLoad(); + }); + + it("Delegate happy path: start delegating, validator list is shown, validator selected", async () => { + await app.assetAccountsPage.openViaDeeplink(SEI_CURRENCY_ID); + await selectAccount(EMPTY_ACCOUNT_ID); + await app.evmDelegate.startDelegate(); + await app.evmDelegate.selectFirstValidator(); + await app.evmDelegate.setAmountAndContinue(DELEGATION_AMOUNT); + }); + + it("Already-delegated validator is reflected on the account page", async () => { + await app.assetAccountsPage.openViaDeeplink(SEI_CURRENCY_ID); + await selectAccount(STAKED_ACCOUNT_ID); + await app.evmDelegate.expectDelegatedAssetsVisible(); + await app.evmDelegate.expectAddDelegationCtaVisible(); + }); +}); diff --git a/apps/ledger-live-mobile/e2e/userdata/seiEvmStaking.json b/apps/ledger-live-mobile/e2e/userdata/seiEvmStaking.json new file mode 100644 index 000000000000..ecc664af4517 --- /dev/null +++ b/apps/ledger-live-mobile/e2e/userdata/seiEvmStaking.json @@ -0,0 +1,209 @@ +{ + "data": { + "SPECTRON_RUN": { + "localStorage": { + "acceptedTermsVersion": "2019-12-04" + } + }, + "settings": { + "hasCompletedOnboarding": true, + "counterValue": "USD", + "language": "en", + "theme": "light", + "region": null, + "locale": "en-US", + "orderAccounts": "balance|desc", + "countervalueFirst": false, + "autoLockTimeout": 10, + "selectedTimeRange": "month", + "marketIndicator": "western", + "currenciesSettings": {}, + "pairExchanges": {}, + "developerMode": false, + "loaded": true, + "shareAnalytics": true, + "sentryLogs": true, + "readOnlyModeEnabled": false, + "hasOrderedNano": true, + "lastUsedVersion": "99.99.99", + "dismissedBanners": [], + "accountsViewMode": "list", + "showAccountsHelperBanner": true, + "hideEmptyTokenAccounts": false, + "sidebarCollapsed": false, + "discreetMode": false, + "preferredDeviceModel": "nanoX", + "hasInstalledApps": true, + "dismissedDynamicCards": [], + "seenDevices": [], + "blacklistedTokenIds": [], + "swapAcceptedProviderIds": [], + "deepLinkUrl": null, + "firstTimeLend": false, + "swapProviders": [], + "showClearCacheBanner": false, + "starredAccountIds": [], + "hasPassword": false + }, + "user": { + "id": "08cf3393-c5eb-4ea7-92de-0deea22e3971" + }, + "accounts": [ + { + "data": { + "id": "mock:2:sei_evm:0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707:", + "seedIdentifier": "0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707", + "name": "SEI Network (EVM) 1", + "starred": false, + "used": true, + "derivationMode": "", + "index": 0, + "freshAddress": "0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707", + "freshAddressPath": "44'/60'/0'/0/0", + "blockHeight": 100000, + "creationDate": "2024-01-01T00:00:00.000Z", + "operationsCount": 0, + "operations": [], + "pendingOperations": [], + "currencyId": "sei_evm", + "unitMagnitude": 18, + "lastSyncDate": "2024-01-01T00:00:00.000Z", + "balance": "10000000000000000000", + "spendableBalance": "10000000000000000000", + "xpub": "0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707", + "stakingResources": { + "delegations": [], + "redelegations": [], + "unbondings": [], + "delegatedBalance": "0", + "pendingRewardsBalance": "0", + "unbondingBalance": "0", + "validators": [ + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000001", + "name": "Ledger by Figment", + "votingPower": 0, + "commission": 0.05, + "estimatedYearlyRewardsRate": 0.12, + "tokens": 123456789000000 + }, + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000002", + "name": "Chorus One", + "votingPower": 1, + "commission": 0.07, + "estimatedYearlyRewardsRate": 0.1, + "tokens": 99000000000000 + }, + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000003", + "name": "Everstake", + "votingPower": 2, + "commission": 0.1, + "estimatedYearlyRewardsRate": 0.09, + "tokens": 77000000000000 + } + ] + }, + "swapHistory": [] + }, + "version": 1 + }, + { + "data": { + "id": "mock:2:sei_evm:0x18E9A4F2A5A2B01F35E7D086E75D7D01530A1A9F:", + "seedIdentifier": "0x6EB963EFD0FEF7A4CFAB6CE6F1421C3279D11707", + "name": "SEI Network (EVM) Staked", + "starred": false, + "used": true, + "derivationMode": "", + "index": 1, + "freshAddress": "0x18E9A4F2A5A2B01F35E7D086E75D7D01530A1A9F", + "freshAddressPath": "44'/60'/1'/0/0", + "blockHeight": 100000, + "creationDate": "2024-01-01T00:00:00.000Z", + "operationsCount": 0, + "operations": [], + "pendingOperations": [], + "currencyId": "sei_evm", + "unitMagnitude": 18, + "lastSyncDate": "2024-01-01T00:00:00.000Z", + "balance": "10000000000000000000", + "spendableBalance": "7500000000000000000", + "xpub": "0x18E9A4F2A5A2B01F35E7D086E75D7D01530A1A9F", + "stakingResources": { + "delegations": [ + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000001", + "amount": "2500000000000000000", + "pendingRewards": "120000000000000000", + "status": "bonded" + } + ], + "redelegations": [ + { + "validatorSrcAddress": "seivaloper1mockvalidator000000000000000000000002", + "validatorDstAddress": "seivaloper1mockvalidator000000000000000000000003", + "amount": "1000000000000000000", + "completionDate": "2042-01-02T03:04:05.000Z" + } + ], + "unbondings": [ + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000001", + "amount": "500000000000000000", + "completionDate": "2042-01-02T03:04:05.000Z" + } + ], + "delegatedBalance": "2500000000000000000", + "pendingRewardsBalance": "120000000000000000", + "unbondingBalance": "500000000000000000", + "validators": [ + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000001", + "name": "Ledger by Figment", + "votingPower": 0, + "commission": 0.05, + "estimatedYearlyRewardsRate": 0.12, + "tokens": 123456789000000 + }, + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000002", + "name": "Chorus One", + "votingPower": 1, + "commission": 0.07, + "estimatedYearlyRewardsRate": 0.1, + "tokens": 99000000000000 + }, + { + "validatorAddress": "seivaloper1mockvalidator000000000000000000000003", + "name": "Everstake", + "votingPower": 2, + "commission": 0.1, + "estimatedYearlyRewardsRate": 0.09, + "tokens": 77000000000000 + } + ] + }, + "swapHistory": [] + }, + "version": 1 + } + ], + "countervalues": {}, + "featureFlags": { + "overrides": { + "evmNativeStaking": { + "enabled": true, + "params": { + "supportedCurrencyIds": ["sei_evm"] + } + }, + "llmAccountListUI": { + "enabled": false + } + }, + "bannerVisible": false + } + } +} diff --git a/e2e/desktop/tests/page/index.ts b/e2e/desktop/tests/page/index.ts index df546a896659..7a4b7ffb0524 100644 --- a/e2e/desktop/tests/page/index.ts +++ b/e2e/desktop/tests/page/index.ts @@ -9,6 +9,7 @@ import { AssetPage } from "./asset.page"; import { BuyAndSellPage } from "./buyAndSell.page"; import { DelegateDrawer } from "./drawer/delegate.drawer"; import { DelegateModal } from "./modal/delegate.modal"; +import { EvmDelegateModal } from "./modal/evmDelegate.modal"; import { Drawer } from "tests/component/drawer.component"; import { EarnV2Page } from "./earn.v2.dashboard.page"; import { Layout } from "tests/component/layout.component"; @@ -53,6 +54,7 @@ export class Application extends PageHolder { public assetPage = new AssetPage(this.page); public buyAndSell = new BuyAndSellPage(this.page, this.electronApp); public delegate = new DelegateModal(this.page); + public evmDelegate = new EvmDelegateModal(this.page); public delegateDrawer = new DelegateDrawer(this.page); public drawer = new Drawer(this.page); public earnV2Dashboard = new EarnV2Page(this.page, this.electronApp); diff --git a/e2e/desktop/tests/page/modal/evmDelegate.modal.ts b/e2e/desktop/tests/page/modal/evmDelegate.modal.ts new file mode 100644 index 000000000000..a781fde87e1e --- /dev/null +++ b/e2e/desktop/tests/page/modal/evmDelegate.modal.ts @@ -0,0 +1,45 @@ +import { expect } from "@playwright/test"; +import { Modal } from "tests/component/modal.component"; +import { step } from "tests/misc/reporters/step"; + +export class EvmDelegateModal extends Modal { + private earnRewardsEmptyStateButton = this.page.getByTestId("evm-earn-rewards-button"); + private rewardsInfoContinueButton = this.page.getByTestId("modal-continue-button"); + private validatorList = this.page.getByTestId("validator-list"); + private delegateContinueButton = this.page.locator("#delegate-continue-button"); + private amountField = this.page.getByTestId("modal-amount-field"); + private amountContinueButton = this.page.locator("#send-amount-continue-button"); + private transactionConfirm = this.page.getByTestId("device-action-transaction-confirm"); + + @step("Start EVM delegation flow from account empty state") + async startFromEmptyState() { + await this.earnRewardsEmptyStateButton.scrollIntoViewIfNeeded(); + await this.earnRewardsEmptyStateButton.click(); + } + + @step("Continue from EVM rewards info modal") + async continueFromRewardsInfo() { + await this.rewardsInfoContinueButton.click(); + } + + @step("Expect validator list to be visible") + async expectValidatorListVisible() { + await expect(this.validatorList).toBeVisible(); + } + + @step("Continue from the validator step (first validator is auto-selected)") + async continueValidatorStep() { + await this.delegateContinueButton.click(); + } + + @step("Fill delegation amount $0 and continue") + async setAmountAndContinue(amount: string) { + await this.amountField.fill(amount); + await this.amountContinueButton.click(); + } + + @step("Expect device validation (sign transaction) screen to be displayed") + async expectDeviceValidationScreen() { + await expect(this.transactionConfirm).toBeVisible(); + } +} diff --git a/e2e/desktop/tests/page/speculos.page.ts b/e2e/desktop/tests/page/speculos.page.ts index 54776fb39174..826a902b23b2 100644 --- a/e2e/desktop/tests/page/speculos.page.ts +++ b/e2e/desktop/tests/page/speculos.page.ts @@ -16,6 +16,7 @@ import { shareViewKey, approveToken, signTypedMessage as signTypedMessageDevice, + acceptEnableTransactionCheck, } from "@ledgerhq/live-common/e2e/speculos"; import { Account } from "@ledgerhq/live-common/e2e/enum/Account"; import { Transaction } from "@ledgerhq/live-common/e2e/models/Transaction"; @@ -101,4 +102,9 @@ export class SpeculosPage extends AppPage { async signTypedMessage() { await signTypedMessageDevice(); } + + @step("Check and accept if available enable transaction check") + async acceptEnableTransactionCheck() { + await acceptEnableTransactionCheck(); + } } diff --git a/e2e/desktop/tests/specs/delegateSEI.spec.ts b/e2e/desktop/tests/specs/delegateSEI.spec.ts new file mode 100644 index 000000000000..5c9110443282 --- /dev/null +++ b/e2e/desktop/tests/specs/delegateSEI.spec.ts @@ -0,0 +1,49 @@ +import { test } from "tests/fixtures/common"; +import { Team } from "@ledgerhq/live-common/e2e/enum/Team"; +import { Account } from "@ledgerhq/live-common/e2e/enum/Account"; +import { Delegate } from "@ledgerhq/live-common/e2e/models/Delegate"; +import { liveDataWithAddressCommand } from "@ledgerhq/live-common/e2e/cliCommandsUtils"; + +const DELEGATION_AMOUNT = "1"; +const delegation = new Delegate(Account.SEI_EVM_1, DELEGATION_AMOUNT, "first-available"); + +test.use({ + teamOwner: Team.COIN_INTEGRATION, + userdata: "skip-onboarding-with-last-seen-device", + speculosApp: delegation.account.currency.speculosApp, + cliCommands: [liveDataWithAddressCommand(delegation.account, { currency: "sei_evm" })], + featureFlags: { + evmNativeStaking: { + enabled: true, + params: { supportedCurrencyIds: ["sei_evm"] }, + }, + }, +}); + +test.describe("SEI EVM Native Staking - Delegate flow", () => { + test( + `[${delegation.account.currency.name}] Delegate: start delegate, validator list shown, validator selected, reach device validation`, + { + tag: ["@Flex", "@sei_evm", "@family-evm"], + annotation: { + type: "TMS", + description: "B2CQA-5964", + }, + }, + async ({ app }) => { + await app.mainNavigation.openTargetFromMainNavigation("accounts"); + await app.accounts.navigateToAccountByName(delegation.account.accountName); + + await app.evmDelegate.startFromEmptyState(); + await app.evmDelegate.continueFromRewardsInfo(); + + await app.evmDelegate.expectValidatorListVisible(); + await app.evmDelegate.continueValidatorStep(); + await app.evmDelegate.setAmountAndContinue(DELEGATION_AMOUNT); + + await app.speculos.acceptEnableTransactionCheck(); + + await app.evmDelegate.expectDeviceValidationScreen(); + }, + ); +}); diff --git a/e2e/mobile/page/index.ts b/e2e/mobile/page/index.ts index 5000f789ee88..01c20431ef86 100644 --- a/e2e/mobile/page/index.ts +++ b/e2e/mobile/page/index.ts @@ -22,6 +22,7 @@ import SettingsHelpPage from "./settings/settingsHelp.page"; import SettingsPage from "./settings/settings.page"; import SpeculosPage from "./speculos.page"; import StakePage from "./trade/stake.page"; +import EvmStakePage from "./trade/evmStake.page"; import SwapPage from "./trade/swap.page"; import SwapLiveAppPage from "./liveApps/swapLiveApp"; import WalletTabNavigatorPage from "./wallet/walletTabNavigator.page"; @@ -76,6 +77,7 @@ export class Application { private settingsGeneralPageInstance = lazyInit(SettingsGeneralPage); private speculosPageInstance = lazyInit(SpeculosPage); private stakePageInstance = lazyInit(StakePage); + private evmStakePageInstance = lazyInit(EvmStakePage); private swapLiveAppInstance = lazyInit(SwapLiveAppPage); private swapPageInstance = lazyInit(SwapPage); private walletTabNavigatorPageInstance = lazyInit(WalletTabNavigatorPage); @@ -190,6 +192,10 @@ export class Application { return this.stakePageInstance(); } + public get evmStake() { + return this.evmStakePageInstance(); + } + public get swap() { return this.swapPageInstance(); } diff --git a/e2e/mobile/page/speculos.page.ts b/e2e/mobile/page/speculos.page.ts index c4ec46959bb6..c67251d9a8a4 100644 --- a/e2e/mobile/page/speculos.page.ts +++ b/e2e/mobile/page/speculos.page.ts @@ -11,6 +11,7 @@ import { verifyAmountsAndRejectSwap, approveToken, signTypedMessage as signTypedMessageDevice, + acceptEnableTransactionCheck, } from "@ledgerhq/live-common/e2e/speculos"; import { setExchangeDependencies } from "../utils/speculosUtils"; import { TransactionType } from "@ledgerhq/live-common/e2e/models/Transaction"; @@ -82,6 +83,11 @@ export default class SpeculosPage { await signTypedMessageDevice(); } + @Step("Check and accept if available enable transaction check") + async acceptEnableTransactionCheck() { + await acceptEnableTransactionCheck(); + } + async setExchangeDependencies(swapOrFromAccount: SwapType | Account, toAccount?: Account) { let accounts: Account[]; if (isSwap(swapOrFromAccount)) { diff --git a/e2e/mobile/page/trade/evmStake.page.ts b/e2e/mobile/page/trade/evmStake.page.ts new file mode 100644 index 000000000000..bf75ad6d87fa --- /dev/null +++ b/e2e/mobile/page/trade/evmStake.page.ts @@ -0,0 +1,82 @@ +import { Step } from "jest-allure2-reporter/api"; + +const VALIDATOR_ROW_REGEX = /^evm-validator-row-.+$/; +const FEE_ESTIMATION_TIMEOUT = 120000; + +export default class EvmStakePage { + startStakingCtaId = "account-quick-action-button-cta"; + addDelegationCtaId = "account-quick-action-button-addDelegation"; + amountInputId = "evm-delegation-amount-input"; + useMaxButtonId = "evm-delegation-use-max"; + amountContinueButtonId = "enabled-evm-delegation-amount-continue"; + delegationSummaryLabel = "Delegated assets"; + + @Step("Start EVM delegation flow from account quick actions") + async startDelegate() { + await waitForElementById(this.startStakingCtaId); + await tapById(this.startStakingCtaId); + } + + @Step("Tap on Add delegation CTA") + async tapAddDelegation() { + await waitForElementById(this.addDelegationCtaId); + await tapById(this.addDelegationCtaId); + } + + @Step("Wait for the validator list to be visible") + async waitForValidatorListVisible() { + await waitForElement(getElementById(VALIDATOR_ROW_REGEX)); + } + + @Step("Select the first validator displayed in the list") + async selectFirstValidator(): Promise { + await this.waitForValidatorListVisible(); + const firstValidatorRowId = await getIdByRegexp(VALIDATOR_ROW_REGEX); + await tapById(firstValidatorRowId); + await waitForElementById(this.amountInputId); + return firstValidatorRowId; + } + + @Step("Set delegation amount to {amount}") + async setAmount(amount: string) { + await waitForElementById(this.amountInputId); + await typeTextById(this.amountInputId, amount); + } + + @Step("Wait until network fees are loaded and continue is enabled") + async waitForAmountContinueEnabled(timeout = FEE_ESTIMATION_TIMEOUT) { + await waitForElementById(this.amountContinueButtonId, timeout); + } + + @Step("Tap use max amount button") + async useMaxAmount() { + await waitForElementById(this.useMaxButtonId); + await tapById(this.useMaxButtonId); + await this.waitForAmountContinueEnabled(); + } + + @Step("Continue from amount screen") + async continueAmount() { + await waitForElementById(this.amountContinueButtonId); + await tapById(this.amountContinueButtonId); + } + + @Step("Set amount and continue once fees are ready") + async setAmountAndContinue(amount: string) { + await this.setAmount(amount); + await this.waitForAmountContinueEnabled(); + await this.continueAmount(); + } + + @Step("Expect delegated assets summary to be visible on the account page") + async expectDelegatedAssetsVisible() { + await waitForElementByText(this.delegationSummaryLabel); + await detoxExpect(getElementByText(this.delegationSummaryLabel)).toBeVisible(); + } + + @Step("Expect Add delegation CTA to be visible on the account page") + async expectAddDelegationCtaVisible() { + await waitForElementById(this.addDelegationCtaId); + await detoxExpect(getElementById(this.addDelegationCtaId)).toBeVisible(); + } +} diff --git a/e2e/mobile/page/wallet/portfolio.page.ts b/e2e/mobile/page/wallet/portfolio.page.ts index ad99e7bcf3a9..74533580ded1 100644 --- a/e2e/mobile/page/wallet/portfolio.page.ts +++ b/e2e/mobile/page/wallet/portfolio.page.ts @@ -191,7 +191,7 @@ export default class PortfolioPage { @Step("Go to asset's accounts from portfolio wallet 40") async goToAccountsW40(currencyName: string) { await waitForElementById(this.accountsListView, 10000); - await scrollToId(this.assetItemId(currencyName)); + await scrollToId(this.assetItemId(currencyName), this.accountsListView); await tapById(this.assetItemId(currencyName)); } diff --git a/e2e/mobile/specs/delegate/delegateSEI.spec.ts b/e2e/mobile/specs/delegate/delegateSEI.spec.ts new file mode 100644 index 000000000000..8430b106b5c5 --- /dev/null +++ b/e2e/mobile/specs/delegate/delegateSEI.spec.ts @@ -0,0 +1,48 @@ +import { Account } from "@ledgerhq/live-common/e2e/enum/Account"; +import { AppInfos } from "@ledgerhq/live-common/e2e/enum/AppInfos"; +import { Team } from "@ledgerhq/live-common/e2e/enum/Team"; +import { Delegate } from "@ledgerhq/live-common/e2e/models/Delegate"; +import { setEnv } from "@ledgerhq/live-env"; +import { setTeamOwner } from "../../helpers/allure/allure-helper"; + +setEnv("DISABLE_TRANSACTION_BROADCAST", true); + +const DELEGATION_AMOUNT = "2"; +const delegation = new Delegate(Account.SEI_EVM_1, DELEGATION_AMOUNT, "first-available"); + +const tmsLinks: string[] = ["B2CQA-5740"]; +const tags = ["@Stax", "@Flex", "@NanoGen5", "@sei_evm", "@family-evm"]; + +describe("SEI EVM Native Staking - Delegate flow", () => { + setTeamOwner(Team.COIN_INTEGRATION); + tmsLinks.forEach(tmsLink => $TmsLink(tmsLink)); + tags.forEach(tag => $Tag(tag)); + + beforeAll(async () => { + await app.init({ + speculosApp: AppInfos.SEI, + featureFlags: { + evmNativeStaking: { + enabled: true, + params: { supportedCurrencyIds: ["sei_evm"] }, + }, + }, + cliCommands: [liveDataWithAddressCommand(delegation.account, { currency: "sei_evm" })], + }); + + await app.portfolio.waitForPortfolioPageToLoad(); + }); + + it(`Delegate on ${delegation.account.currency.name}: start delegate, validator list shown, validator selected`, async () => { + await app.portfolio.goToAccounts(delegation.account.currency.name); + await app.common.goToAccountByName(delegation.account.accountName); + + await app.evmStake.startDelegate(); + await app.evmStake.selectFirstValidator(); + await app.evmStake.setAmountAndContinue(DELEGATION_AMOUNT); + + await app.speculos.acceptEnableTransactionCheck(); + + await app.deviceValidation.expectDeviceValidationScreen(); + }); +}); diff --git a/libs/ledger-live-common/src/e2e/enum/Account.ts b/libs/ledger-live-common/src/e2e/enum/Account.ts index 1a9f490a46ee..dd62c911b5ea 100644 --- a/libs/ledger-live-common/src/e2e/enum/Account.ts +++ b/libs/ledger-live-common/src/e2e/enum/Account.ts @@ -283,6 +283,19 @@ export class Account { static readonly ICP_1 = new Account(Currency.ICP, "Internet Computer 1", 0, "44'/223'/0'/0/0"); static readonly ICP_2 = new Account(Currency.ICP, "Internet Computer 2", 1, "44'/223'/1'/0/0"); + static readonly SEI_EVM_1 = new Account( + Currency.SEI_EVM, + "SEI Network (EVM) 1", + 0, + "44'/60'/0'/0/0", + ); + static readonly SEI_EVM_2 = new Account( + Currency.SEI_EVM, + "SEI Network (EVM) 2", + 1, + "44'/60'/1'/0/0", + ); + static readonly EMPTY = new Account(Currency.BTC, "Empty", 0, ""); } diff --git a/libs/ledger-live-common/src/e2e/enum/AppInfos.ts b/libs/ledger-live-common/src/e2e/enum/AppInfos.ts index 0b3708bbc0d2..66cbc40e41fa 100644 --- a/libs/ledger-live-common/src/e2e/enum/AppInfos.ts +++ b/libs/ledger-live-common/src/e2e/enum/AppInfos.ts @@ -82,4 +82,6 @@ export class AppInfos { static readonly MINA = new AppInfos("Mina"); static readonly CONCORDIUM = new AppInfos("Concordium"); + + static readonly SEI = new AppInfos("Sei"); } diff --git a/libs/ledger-live-common/src/e2e/enum/Currency.ts b/libs/ledger-live-common/src/e2e/enum/Currency.ts index 1fd15d649e30..dfc63fd85ec7 100644 --- a/libs/ledger-live-common/src/e2e/enum/Currency.ts +++ b/libs/ledger-live-common/src/e2e/enum/Currency.ts @@ -228,4 +228,8 @@ export class Currency { AppInfos.CONCORDIUM, [Network.CONCORDIUM], ); + + static readonly SEI_EVM = new Currency("SEI Network (EVM)", "SEI", "sei_evm", AppInfos.SEI, [ + Network.SEI, + ]); } diff --git a/libs/ledger-live-common/src/e2e/enum/DeviceLabels.ts b/libs/ledger-live-common/src/e2e/enum/DeviceLabels.ts index db5546808124..2eb851f57751 100644 --- a/libs/ledger-live-common/src/e2e/enum/DeviceLabels.ts +++ b/libs/ledger-live-common/src/e2e/enum/DeviceLabels.ts @@ -21,6 +21,7 @@ export enum DeviceLabels { DELEGATE_STAKE = "Delegate stake", DEPOSIT = "Deposit", EXPERT_MODE = "Expert mode", + ENABLE_TRANSACTION_CHECK = "Enable Transaction Check", FEES = "Fees", GET = "Get", I_UNDERSTAND = "I understand", diff --git a/libs/ledger-live-common/src/e2e/enum/Network.ts b/libs/ledger-live-common/src/e2e/enum/Network.ts index dfa8731ff4f6..1303dca236b4 100644 --- a/libs/ledger-live-common/src/e2e/enum/Network.ts +++ b/libs/ledger-live-common/src/e2e/enum/Network.ts @@ -39,4 +39,5 @@ export enum Network { INTERNET_COMPUTER = "Internet Computer", MINA = "Mina", CONCORDIUM = "Concordium", + SEI = "SEI Network (EVM)", } diff --git a/libs/ledger-live-common/src/e2e/speculos.ts b/libs/ledger-live-common/src/e2e/speculos.ts index b5d74259b6d8..5c718220481b 100644 --- a/libs/ledger-live-common/src/e2e/speculos.ts +++ b/libs/ledger-live-common/src/e2e/speculos.ts @@ -320,6 +320,14 @@ export const specs: Specs = { }, dependencies: [], }, + Sei: { + currency: getCryptoCurrencyById("sei_evm"), + appQuery: { + model: getSpeculosModel(), + appName: "Sei", + }, + dependencies: [AppInfos.ETHEREUM], + }, Litecoin: { currency: getCryptoCurrencyById("litecoin"), appQuery: { @@ -868,6 +876,7 @@ export async function signSendTransaction(tx: Transaction) { case Currency.POL.id: case Currency.ETH.id: case Currency.ETH_USDT.id: + case Currency.SEI_EVM.id: await sendEVM(tx); break; case Currency.BTC.id: @@ -1105,4 +1114,41 @@ export const shareViewKey = withDeviceController(({ getButtonsController }) => a } }); +export const acceptEnableTransactionCheck = withDeviceController( + ({ getButtonsController }) => + async () => { + const buttons = getButtonsController(); + + // Wait for loading to finish: poll until either the Transaction Check prompt + // or the next (review transaction) screen is displayed. If the prompt never + // shows up, skip this step instead of waiting for it to appear. + const port = getEnv("SPECULOS_API_PORT"); + const enableLabel = DeviceLabels.ENABLE_TRANSACTION_CHECK.toLowerCase(); + const reviewLabel = DeviceLabels.REVIEW_TRANSACTION.toLowerCase(); + let isTransactionCheckDisplayed = false; + for (let attempt = 0; attempt < 60; attempt++) { + const texts = (await fetchCurrentScreenTexts(port)).toLowerCase(); + if (texts.includes(enableLabel)) { + isTransactionCheckDisplayed = true; + break; + } + if (texts.includes(reviewLabel)) { + break; + } + await sleep(500); + } + + if (!isTransactionCheckDisplayed) { + return; + } + + if (isTouchDevice()) { + await pressAndRelease(DeviceLabels.YES_ENABLE); + } else { + await pressUntilTextFound(DeviceLabels.CONFIRM); + await buttons.both(); + } + }, +); + export { approveToken, signTypedMessage }; diff --git a/libs/ledger-live-common/src/families/evm/bridge/mock.ts b/libs/ledger-live-common/src/families/evm/bridge/mock.ts index 167e32f44bfc..45aaf9203c4d 100644 --- a/libs/ledger-live-common/src/families/evm/bridge/mock.ts +++ b/libs/ledger-live-common/src/families/evm/bridge/mock.ts @@ -21,6 +21,7 @@ import { getTypedTransaction } from "@ledgerhq/coin-evm/transaction"; import { getCurrencyConfiguration } from "../../../config"; import { EvmConfigInfo, setCoinConfig } from "@ledgerhq/coin-evm/config"; import { validateAddress } from "../../../bridge/validateAddress"; +import { assignFromAccountRaw, assignToAccountRaw } from "@ledgerhq/coin-evm/staking/serialization"; const receive = makeAccountBridgeReceive(); const defaultGetFees = (_a, t: any) => { @@ -166,6 +167,8 @@ const accountBridge: AccountBridge = { getSerializedAddressParameters, validateAddress, getEstimationRecipient: account => getEvmDummyAddress(account.currency.id), + assignFromAccountRaw, + assignToAccountRaw, }; const currencyBridge: CurrencyBridge = { preload: () => Promise.resolve({}), From d7273df5ec02243d9731294c4dc5fec004b0c387 Mon Sep 17 00:00:00 2001 From: Henri Ly Date: Mon, 22 Jun 2026 13:19:11 +0200 Subject: [PATCH 2/6] test(sei-evm): add missing tags for ci --- e2e/desktop/tests/specs/delegateSEI.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/desktop/tests/specs/delegateSEI.spec.ts b/e2e/desktop/tests/specs/delegateSEI.spec.ts index 5c9110443282..8143aad4798e 100644 --- a/e2e/desktop/tests/specs/delegateSEI.spec.ts +++ b/e2e/desktop/tests/specs/delegateSEI.spec.ts @@ -24,7 +24,7 @@ test.describe("SEI EVM Native Staking - Delegate flow", () => { test( `[${delegation.account.currency.name}] Delegate: start delegate, validator list shown, validator selected, reach device validation`, { - tag: ["@Flex", "@sei_evm", "@family-evm"], + tag: ["@Flex", "@Stax", "@NanoGen5", "@sei_evm", "@family-evm"], annotation: { type: "TMS", description: "B2CQA-5964", From 04be5868a3f07974a6d0752e2d4373b16354d45f Mon Sep 17 00:00:00 2001 From: Henri Ly Date: Mon, 22 Jun 2026 13:36:13 +0200 Subject: [PATCH 3/6] test(sei-evm): copilot feedback --- e2e/desktop/tests/page/speculos.page.ts | 4 ++-- e2e/mobile/page/speculos.page.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/desktop/tests/page/speculos.page.ts b/e2e/desktop/tests/page/speculos.page.ts index 826a902b23b2..4a860fcce00f 100644 --- a/e2e/desktop/tests/page/speculos.page.ts +++ b/e2e/desktop/tests/page/speculos.page.ts @@ -16,7 +16,7 @@ import { shareViewKey, approveToken, signTypedMessage as signTypedMessageDevice, - acceptEnableTransactionCheck, + acceptEnableTransactionCheck as acceptEnableTransactionCheckDevice, } from "@ledgerhq/live-common/e2e/speculos"; import { Account } from "@ledgerhq/live-common/e2e/enum/Account"; import { Transaction } from "@ledgerhq/live-common/e2e/models/Transaction"; @@ -105,6 +105,6 @@ export class SpeculosPage extends AppPage { @step("Check and accept if available enable transaction check") async acceptEnableTransactionCheck() { - await acceptEnableTransactionCheck(); + await acceptEnableTransactionCheckDevice(); } } diff --git a/e2e/mobile/page/speculos.page.ts b/e2e/mobile/page/speculos.page.ts index c67251d9a8a4..7a5a0c5dd7d0 100644 --- a/e2e/mobile/page/speculos.page.ts +++ b/e2e/mobile/page/speculos.page.ts @@ -11,7 +11,7 @@ import { verifyAmountsAndRejectSwap, approveToken, signTypedMessage as signTypedMessageDevice, - acceptEnableTransactionCheck, + acceptEnableTransactionCheck as acceptEnableTransactionCheckDevice, } from "@ledgerhq/live-common/e2e/speculos"; import { setExchangeDependencies } from "../utils/speculosUtils"; import { TransactionType } from "@ledgerhq/live-common/e2e/models/Transaction"; @@ -85,7 +85,7 @@ export default class SpeculosPage { @Step("Check and accept if available enable transaction check") async acceptEnableTransactionCheck() { - await acceptEnableTransactionCheck(); + await acceptEnableTransactionCheckDevice(); } async setExchangeDependencies(swapOrFromAccount: SwapType | Account, toAccount?: Account) { From cda0d8a900dfd9d2df2e6f24d52058a0519126f6 Mon Sep 17 00:00:00 2001 From: Henri Ly Date: Tue, 23 Jun 2026 09:57:16 +0200 Subject: [PATCH 4/6] fix(sei-evm): add nano X and SP tags --- e2e/desktop/tests/specs/delegateSEI.spec.ts | 2 +- e2e/mobile/specs/delegate/delegateSEI.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/desktop/tests/specs/delegateSEI.spec.ts b/e2e/desktop/tests/specs/delegateSEI.spec.ts index 8143aad4798e..eefe3aa7606a 100644 --- a/e2e/desktop/tests/specs/delegateSEI.spec.ts +++ b/e2e/desktop/tests/specs/delegateSEI.spec.ts @@ -24,7 +24,7 @@ test.describe("SEI EVM Native Staking - Delegate flow", () => { test( `[${delegation.account.currency.name}] Delegate: start delegate, validator list shown, validator selected, reach device validation`, { - tag: ["@Flex", "@Stax", "@NanoGen5", "@sei_evm", "@family-evm"], + tag: ["@NanoSP", "@NanoX", "@Flex", "@Stax", "@NanoGen5", "@sei_evm", "@family-evm"], annotation: { type: "TMS", description: "B2CQA-5964", diff --git a/e2e/mobile/specs/delegate/delegateSEI.spec.ts b/e2e/mobile/specs/delegate/delegateSEI.spec.ts index 8430b106b5c5..22644f50ec20 100644 --- a/e2e/mobile/specs/delegate/delegateSEI.spec.ts +++ b/e2e/mobile/specs/delegate/delegateSEI.spec.ts @@ -11,7 +11,7 @@ const DELEGATION_AMOUNT = "2"; const delegation = new Delegate(Account.SEI_EVM_1, DELEGATION_AMOUNT, "first-available"); const tmsLinks: string[] = ["B2CQA-5740"]; -const tags = ["@Stax", "@Flex", "@NanoGen5", "@sei_evm", "@family-evm"]; +const tags = ["@NanoSP", "@NanoX", "@Stax", "@Flex", "@NanoGen5", "@sei_evm", "@family-evm"]; describe("SEI EVM Native Staking - Delegate flow", () => { setTeamOwner(Team.COIN_INTEGRATION); From 8fb66f647a34922896bd01967cf232f300f1cdc5 Mon Sep 17 00:00:00 2001 From: Diyaeddine LAOUID Date: Wed, 24 Jun 2026 12:23:08 +0200 Subject: [PATCH 5/6] feat(e2e): confirmation screen e2e desktop sei delegate --- e2e/desktop/tests/page/modal/evmDelegate.modal.ts | 6 ++++++ e2e/desktop/tests/page/speculos.page.ts | 5 +++++ e2e/desktop/tests/specs/delegateSEI.spec.ts | 4 +++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/e2e/desktop/tests/page/modal/evmDelegate.modal.ts b/e2e/desktop/tests/page/modal/evmDelegate.modal.ts index a781fde87e1e..4b11d3efc534 100644 --- a/e2e/desktop/tests/page/modal/evmDelegate.modal.ts +++ b/e2e/desktop/tests/page/modal/evmDelegate.modal.ts @@ -10,6 +10,7 @@ export class EvmDelegateModal extends Modal { private amountField = this.page.getByTestId("modal-amount-field"); private amountContinueButton = this.page.locator("#send-amount-continue-button"); private transactionConfirm = this.page.getByTestId("device-action-transaction-confirm"); + private successMessageLabel = this.page.getByTestId("success-message-label"); @step("Start EVM delegation flow from account empty state") async startFromEmptyState() { @@ -42,4 +43,9 @@ export class EvmDelegateModal extends Modal { async expectDeviceValidationScreen() { await expect(this.transactionConfirm).toBeVisible(); } + + @step("Expect EVM delegation success message") + async expectSuccessMessage() { + await expect(this.successMessageLabel).toBeVisible(); + } } diff --git a/e2e/desktop/tests/page/speculos.page.ts b/e2e/desktop/tests/page/speculos.page.ts index 4a860fcce00f..b269399f1bbd 100644 --- a/e2e/desktop/tests/page/speculos.page.ts +++ b/e2e/desktop/tests/page/speculos.page.ts @@ -98,6 +98,11 @@ export class SpeculosPage extends AppPage { await approveToken(); } + @step("Sign EVM contract transaction") + async signEvmContractTransaction() { + await approveToken(); + } + @step("Sign typed message on device") async signTypedMessage() { await signTypedMessageDevice(); diff --git a/e2e/desktop/tests/specs/delegateSEI.spec.ts b/e2e/desktop/tests/specs/delegateSEI.spec.ts index eefe3aa7606a..6c9a087c9983 100644 --- a/e2e/desktop/tests/specs/delegateSEI.spec.ts +++ b/e2e/desktop/tests/specs/delegateSEI.spec.ts @@ -22,7 +22,7 @@ test.use({ test.describe("SEI EVM Native Staking - Delegate flow", () => { test( - `[${delegation.account.currency.name}] Delegate: start delegate, validator list shown, validator selected, reach device validation`, + `[${delegation.account.currency.name}] Delegate: start delegate, validator selected, confirm transaction`, { tag: ["@NanoSP", "@NanoX", "@Flex", "@Stax", "@NanoGen5", "@sei_evm", "@family-evm"], annotation: { @@ -44,6 +44,8 @@ test.describe("SEI EVM Native Staking - Delegate flow", () => { await app.speculos.acceptEnableTransactionCheck(); await app.evmDelegate.expectDeviceValidationScreen(); + await app.speculos.signEvmContractTransaction(); + await app.evmDelegate.expectSuccessMessage(); }, ); }); From 80323abb88d2e9a5342bdf5f21aa1d7862f8d100 Mon Sep 17 00:00:00 2001 From: Henri Ly Date: Wed, 24 Jun 2026 14:30:02 +0200 Subject: [PATCH 6/6] feat(sei-evm): add more accurate test for mobile part --- e2e/desktop/tests/specs/delegateSEI.spec.ts | 9 +++++++++ e2e/mobile/models/stake.ts | 1 + e2e/mobile/page/speculos.page.ts | 5 +++++ e2e/mobile/page/trade/evmStake.page.ts | 15 +++++++++++++-- e2e/mobile/specs/delegate/delegateSEI.spec.ts | 8 ++++++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/e2e/desktop/tests/specs/delegateSEI.spec.ts b/e2e/desktop/tests/specs/delegateSEI.spec.ts index 6c9a087c9983..865509ed3e84 100644 --- a/e2e/desktop/tests/specs/delegateSEI.spec.ts +++ b/e2e/desktop/tests/specs/delegateSEI.spec.ts @@ -46,6 +46,15 @@ test.describe("SEI EVM Native Staking - Delegate flow", () => { await app.evmDelegate.expectDeviceValidationScreen(); await app.speculos.signEvmContractTransaction(); await app.evmDelegate.expectSuccessMessage(); + await app.delegate.clickViewDetailsButton(); + + await app.drawer.waitForDrawerToBeVisible(); + await app.delegateDrawer.verifyTxTypeIsVisible(); + + await app.delegateDrawer.providerIsVisible(delegation); + await app.delegateDrawer.amountValueIsVisible(delegation.account.currency.ticker); + await app.delegateDrawer.operationTypeIsCorrect("Delegated"); + await app.drawer.closeDrawer(); }, ); }); diff --git a/e2e/mobile/models/stake.ts b/e2e/mobile/models/stake.ts index e4d613159e6a..1fcf9dacf4a3 100644 --- a/e2e/mobile/models/stake.ts +++ b/e2e/mobile/models/stake.ts @@ -61,6 +61,7 @@ export async function verifyStakeOperationDetailsInfo( Currency.OSMO, Currency.ADA, Currency.MULTIVERS_X, + Currency.SEI_EVM, ]; const currenciesForStakeType = [Currency.NEAR]; const currenciesForLockType = [Currency.CELO]; diff --git a/e2e/mobile/page/speculos.page.ts b/e2e/mobile/page/speculos.page.ts index 7a5a0c5dd7d0..9be4053c5a71 100644 --- a/e2e/mobile/page/speculos.page.ts +++ b/e2e/mobile/page/speculos.page.ts @@ -78,6 +78,11 @@ export default class SpeculosPage { await approveToken(); } + @Step("Sign EVM contract transaction on device") + async signEvmContractTransaction() { + await approveToken(); + } + @Step("Sign typed message on device") async signTypedMessage() { await signTypedMessageDevice(); diff --git a/e2e/mobile/page/trade/evmStake.page.ts b/e2e/mobile/page/trade/evmStake.page.ts index bf75ad6d87fa..52157c7acb78 100644 --- a/e2e/mobile/page/trade/evmStake.page.ts +++ b/e2e/mobile/page/trade/evmStake.page.ts @@ -1,6 +1,9 @@ import { Step } from "jest-allure2-reporter/api"; const VALIDATOR_ROW_REGEX = /^evm-validator-row-.+$/; +// The staking quick-action testID suffix depends on account state: `cta` when the account has no +// delegation yet, `addDelegation` once it already delegates. Both navigate to the validator list. +const START_DELEGATE_CTA_REGEX = /^account-quick-action-button-(cta|addDelegation)$/; const FEE_ESTIMATION_TIMEOUT = 120000; export default class EvmStakePage { @@ -10,11 +13,13 @@ export default class EvmStakePage { useMaxButtonId = "evm-delegation-use-max"; amountContinueButtonId = "enabled-evm-delegation-amount-continue"; delegationSummaryLabel = "Delegated assets"; + successScreenId = "validate-success-screen"; @Step("Start EVM delegation flow from account quick actions") async startDelegate() { - await waitForElementById(this.startStakingCtaId); - await tapById(this.startStakingCtaId); + await waitForElement(getElementById(START_DELEGATE_CTA_REGEX)); + const ctaId = await getIdByRegexp(START_DELEGATE_CTA_REGEX); + await tapById(ctaId); } @Step("Tap on Add delegation CTA") @@ -79,4 +84,10 @@ export default class EvmStakePage { await waitForElementById(this.addDelegationCtaId); await detoxExpect(getElementById(this.addDelegationCtaId)).toBeVisible(); } + + @Step("Expect EVM delegation success message") + async expectSuccessMessage() { + await waitForElementById(this.successScreenId); + await detoxExpect(getElementById(this.successScreenId)).toBeVisible(); + } } diff --git a/e2e/mobile/specs/delegate/delegateSEI.spec.ts b/e2e/mobile/specs/delegate/delegateSEI.spec.ts index 22644f50ec20..7cdd9944c69b 100644 --- a/e2e/mobile/specs/delegate/delegateSEI.spec.ts +++ b/e2e/mobile/specs/delegate/delegateSEI.spec.ts @@ -4,6 +4,7 @@ import { Team } from "@ledgerhq/live-common/e2e/enum/Team"; import { Delegate } from "@ledgerhq/live-common/e2e/models/Delegate"; import { setEnv } from "@ledgerhq/live-env"; import { setTeamOwner } from "../../helpers/allure/allure-helper"; +import { verifyStakeOperationDetailsInfo } from "../../models/stake"; setEnv("DISABLE_TRANSACTION_BROADCAST", true); @@ -34,6 +35,8 @@ describe("SEI EVM Native Staking - Delegate flow", () => { }); it(`Delegate on ${delegation.account.currency.name}: start delegate, validator list shown, validator selected`, async () => { + const amountWithCode = DELEGATION_AMOUNT + " " + delegation.account.currency.ticker; + await app.portfolio.goToAccounts(delegation.account.currency.name); await app.common.goToAccountByName(delegation.account.accountName); @@ -44,5 +47,10 @@ describe("SEI EVM Native Staking - Delegate flow", () => { await app.speculos.acceptEnableTransactionCheck(); await app.deviceValidation.expectDeviceValidationScreen(); + await app.speculos.signEvmContractTransaction(); + await app.evmStake.expectSuccessMessage(); + await app.common.successViewDetails(); + + await verifyStakeOperationDetailsInfo(delegation, amountWithCode); }); });