Skip to content

Commit

Permalink
[B2CQA-387] first detox cosmos delegation tests (#4974)
Browse files Browse the repository at this point in the history
* test: first detox cosmos delegation tests
* fix: fix remaining errors
* chore: update cosmos integration tests snapshots
  • Loading branch information
abdurrahman-ledger authored Nov 16, 2023
1 parent 81cd64d commit ee4a5a8
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 108 deletions.
2 changes: 1 addition & 1 deletion apps/ledger-live-mobile/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export async function scrollToId(

export async function getTextOfElement(id: string, index = 0) {
const attributes = await getElementById(id, index).getAttributes();
return !("elements" in attributes) ? attributes.text : attributes.elements[index].text;
return (!("elements" in attributes) ? attributes.text : attributes.elements[index].text) || "";
}

/**
Expand Down
52 changes: 52 additions & 0 deletions apps/ledger-live-mobile/e2e/models/stake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { getTextOfElement, tapById, waitForElementById } from "../helpers";

export default class StakePage {
cosmosDelegationSummaryValidatorId = "cosmos-delegation-summary-validator";
cosmosDelegationSummaryValidator = () => getTextOfElement("cosmos-delegation-summary-validator");
cosmosDelegationSummaryAmountId = "cosmos-delegation-summary-amount";
cosmosDelegationAmountValue = () => getTextOfElement(this.cosmosDelegationSummaryAmountId);
cosmosAssestsRemainingId = "cosmos-assets-remaining";
cosmosDelegatedRatioId = (delegatedPercent: number) => `delegate-ratio-${delegatedPercent}%`;
cosmosAllAssestsUsedText = "cosmos-all-assets-used-text";
summaryContinueButtonId = "cosmos-summary-continue-button";

async selectCurrency(currencyId: string) {
const id = "currency-row-" + currencyId;
await waitForElementById(id);
await tapById(id);
}

async selectAccount(accountId: string) {
const id = "account-card-" + accountId;
await waitForElementById(id);
await tapById(id);
}

async delegationStart() {
await tapById("cosmos-delegation-start-button");
await waitForElementById(this.cosmosDelegationSummaryValidatorId);
}

async setAmount(delegatedPercent: 25 | 50 | 75 | 100) {
await waitForElementById(this.cosmosDelegationSummaryAmountId);
await tapById(this.cosmosDelegationSummaryAmountId);
await tapById(this.cosmosDelegatedRatioId(delegatedPercent));
const max = delegatedPercent == 100;
const id = max ? this.cosmosAllAssestsUsedText : this.cosmosAssestsRemainingId;
await waitForElementById(id);
const assestsRemaining = max ? "0\u00a0ATOM" : (await getTextOfElement(id)).split(": ")[1];
await tapById("cosmos-delegation-amount-continue");
await waitForElementById(this.cosmosDelegationSummaryAmountId);
const assestsDelagated = await this.cosmosDelegationAmountValue();
return [assestsDelagated, assestsRemaining];
}

async summaryContinue() {
await tapById(this.summaryContinueButtonId);
}

async successClose() {
await waitForElementById("success-close-button");
await tapById("success-close-button");
}
}
9 changes: 9 additions & 0 deletions apps/ledger-live-mobile/e2e/models/wallet/portfolioPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
getElementById,
getTextOfElement,
openDeeplink,
scrollToId,
tapByElement,
waitForElementById,
} from "../../helpers";
Expand All @@ -12,10 +13,13 @@ export default class PortfolioPage {
graphCardBalanceId = "graphCard-balance";
assetBalanceId = "asset-balance";
readOnlyPortfolioId = "PortfolioReadOnlyList";
transferScrollListId = "transfer-scroll-list";
stakeMenuButtonId = "transfer-stake-button";
emptyPortfolioComponent = () => getElementById("PortfolioEmptyAccount");
portfolioSettingsButton = () => getElementById("settings-icon");
transferButton = () => getElementById("transfer-button");
swapTransferMenuButton = () => getElementById("swap-transfer-button");
stakeTransferMenuButton = () => getElementById(this.stakeMenuButtonId);
sendTransferMenuButton = () => getElementById("transfer-send-button");
sendMenuButton = () => getElementById("send-button");
marketTabButton = () => getElementById("tab-bar-market");
Expand All @@ -42,6 +46,11 @@ export default class PortfolioPage {
await tapByElement(this.sendTransferMenuButton());
}

async navigateToStakeFromTransferMenu() {
await scrollToId(this.stakeMenuButtonId, this.transferScrollListId);
return tapByElement(this.stakeTransferMenuButton());
}

async openAddAccount() {
const element = getElementById("add-account-button");
await element.tap();
Expand Down
83 changes: 83 additions & 0 deletions apps/ledger-live-mobile/e2e/specs/delegate/cosmos.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { genAccount } from "@ledgerhq/live-common/mock/account";
import {
getCryptoCurrencyById,
setSupportedCurrencies,
} from "@ledgerhq/live-common/currencies/index";
import { loadAccounts, loadBleState, loadConfig } from "../../bridge/server";
import PortfolioPage from "../../models/wallet/portfolioPage";
import StakePage from "../../models/stake";
import DeviceAction from "../../models/DeviceAction";
import { DeviceModelId } from "@ledgerhq/devices";
import { BigNumber } from "bignumber.js";
import { Unit } from "@ledgerhq/types-cryptoassets";
import { formatCurrencyUnit } from "@ledgerhq/live-common/currencies/index";

let portfolioPage: PortfolioPage;
let stakePage: StakePage;
let deviceAction: DeviceAction;

const knownDevice = {
name: "Nano X de test",
id: "mock_1",
modelId: DeviceModelId.nanoX,
};

const testedCurrency = "cosmos";
const id = "cosmosid";

setSupportedCurrencies([testedCurrency]);
const testedAccount = genAccount(id, {
currency: getCryptoCurrencyById(testedCurrency),
});

const COSMOS_MIN_SAFE = new BigNumber(100000); // 100000 uAtom
const COSMOS_MIN_FEES = new BigNumber(6000);
const formattedAmount = (unit: Unit, amount: BigNumber, showAllDigits = false) =>
// amount formatted with the same unit as what the input should use
formatCurrencyUnit(unit, amount, {
showCode: true,
showAllDigits: showAllDigits,
disableRounding: false,
});

describe("Cosmos delegate flow", () => {
beforeAll(async () => {
loadConfig("onboardingcompleted", true);

loadBleState({ knownDevices: [knownDevice] });
loadAccounts([testedAccount]);

portfolioPage = new PortfolioPage();
deviceAction = new DeviceAction(knownDevice);
stakePage = new StakePage();

await portfolioPage.waitForPortfolioPageToLoad();
});

it("open account stake flow", async () => {
await portfolioPage.openTransferMenu();
await portfolioPage.navigateToStakeFromTransferMenu();
});

it("goes through the delegate flow", async () => {
const delegatedPercent = 50;
const usableAmount = testedAccount.spendableBalance
.minus(COSMOS_MIN_SAFE)
.minus(COSMOS_MIN_FEES);
const delegatedAmount = usableAmount.div(100 / delegatedPercent).integerValue();
const remainingAmount = usableAmount.minus(delegatedAmount);

await stakePage.selectCurrency(testedCurrency);
await stakePage.selectAccount(testedAccount.id);

const [assestsDelagated, assestsRemaining] = await stakePage.setAmount(delegatedPercent);
expect(await stakePage.cosmosDelegationSummaryValidator()).toEqual("Ledger");
expect(assestsRemaining).toEqual(formattedAmount(testedAccount.unit, remainingAmount));
expect(assestsDelagated).toEqual(formattedAmount(testedAccount.unit, delegatedAmount, true));

await stakePage.summaryContinue();
await deviceAction.selectMockDevice();
await deviceAction.openApp();
await stakePage.successClose();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function DelegationStarted({ navigation, route }: Props) {
title={t("cosmos.delegation.flow.steps.starter.warning.description")}
/>
</View>
<Button onPress={onNext} type="main" mt={6}>
<Button onPress={onNext} type="main" mt={6} testID="cosmos-delegation-start-button">
<Trans i18nKey="cosmos.delegation.flow.steps.starter.cta" />
</Button>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { accountScreenSelector } from "../../../reducers/accounts";
import ValidatorImage from "../shared/ValidatorImage";
import { StackNavigatorProps } from "../../../components/RootNavigator/types/helpers";
import { CosmosDelegationFlowParamList } from "./types";
import Config from "react-native-config";

type Props = StackNavigatorProps<
CosmosDelegationFlowParamList,
Expand Down Expand Up @@ -108,26 +109,28 @@ export default function DelegationSummary({ navigation, route }: Props) {

const [rotateAnim] = useState(() => new Animated.Value(0));
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(rotateAnim, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(rotateAnim, {
toValue: -1,
duration: 300,
useNativeDriver: true,
}),
Animated.timing(rotateAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.delay(1000),
]),
).start();
if (!Config.MOCK) {
Animated.loop(
Animated.sequence([
Animated.timing(rotateAnim, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(rotateAnim, {
toValue: -1,
duration: 300,
useNativeDriver: true,
}),
Animated.timing(rotateAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.delay(1000),
]),
).start();
}
return () => {
rotateAnim.setValue(0);
};
Expand Down Expand Up @@ -251,6 +254,7 @@ export default function DelegationSummary({ navigation, route }: Props) {
onPress={onContinue}
disabled={bridgePending || !!bridgeError || hasErrors}
pending={bridgePending}
testID="cosmos-summary-continue-button"
/>
</View>
</SafeAreaView>
Expand Down Expand Up @@ -364,15 +368,18 @@ function SummaryWords({
<Trans i18nKey={`cosmos.delegation.iDelegate`} />
</Words>
<Touchable onPress={onChangeAmount}>
<Selectable name={formattedAmount} />
<Selectable name={formattedAmount} testID="cosmos-delegation-summary-amount" />
</Touchable>
</Line>
<Line>
<Words>
<Trans i18nKey="delegation.to" />
</Words>
<Touchable onPress={onChangeValidator}>
<Selectable name={validator?.name ?? validator?.validatorAddress ?? "-"} />
<Selectable
name={validator?.name ?? validator?.validatorAddress ?? "-"}
testID="cosmos-delegation-summary-validator"
/>
</Touchable>
</Line>
</>
Expand Down Expand Up @@ -428,7 +435,7 @@ const Words = ({
</Text>
);

const Selectable = ({ name }: { name: string; readOnly?: boolean }) => {
const Selectable = ({ name, testID }: { name: string; readOnly?: boolean; testID?: string }) => {
const { colors } = useTheme();
return (
<View style={[styles.validatorSelection, { backgroundColor: rgba(colors.primary, 0.2) }]}>
Expand All @@ -437,6 +444,7 @@ const Selectable = ({ name }: { name: string; readOnly?: boolean }) => {
numberOfLines={1}
style={styles.validatorSelectionText}
color={colors.primary}
testID={testID}
>
{name}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ function DelegationAmount({ navigation, route }: Props) {
<LText
style={[styles.ratioLabel]}
color={value.eq(v) ? colors.neutral.c100 : colors.neutral.c60}
testID={"delegate-ratio-" + label}
>
{label}
</LText>
Expand Down Expand Up @@ -217,14 +218,18 @@ function DelegationAmount({ navigation, route }: Props) {
{max.isZero() && (
<View style={styles.labelContainer}>
<Check size={16} color={colors.success.c50} />
<LText style={[styles.assetsRemaining]} color={colors.success.c50}>
<LText
style={[styles.assetsRemaining]}
color={colors.success.c50}
testID="cosmos-all-assets-used-text"
>
<Trans i18nKey={`cosmos.${mode}.flow.steps.amount.allAssetsUsed`} />
</LText>
</View>
)}
{max.gt(0) && !isAmountOutOfRange && !isNotEnoughBalance && (
<View style={styles.labelContainer}>
<LText style={styles.assetsRemaining}>
<LText style={styles.assetsRemaining} testID="cosmos-assets-remaining">
<Trans
i18nKey="cosmos.delegation.flow.steps.amount.assetsRemaining"
values={{
Expand Down Expand Up @@ -263,6 +268,7 @@ function DelegationAmount({ navigation, route }: Props) {
onPress={onNext}
title={<Trans i18nKey="cosmos.delegation.flow.steps.amount.cta" />}
type="primary"
testID="cosmos-delegation-amount-continue"
/>
</View>
</View>
Expand Down
32 changes: 27 additions & 5 deletions libs/ledger-live-common/src/families/cosmos/bridge/mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { BigNumber } from "bignumber.js";
import { NotEnoughBalance, RecipientRequired, InvalidAddress, FeeTooHigh } from "@ledgerhq/errors";
import {
AmountRequired,
NotEnoughBalance,
RecipientRequired,
InvalidAddress,
FeeTooHigh,
} from "@ledgerhq/errors";
import type { CosmosAccount, CosmosValidatorItem, StatusErrorMap, Transaction } from "../types";
import {
scanAccounts,
Expand All @@ -14,6 +20,9 @@ import { setCosmosPreloadData, asSafeCosmosPreloadData } from "../preloadedData"
import { getMainAccount } from "../../../account";
import mockPreloadedData from "../preloadedData.mock";
import type { Account, AccountBridge, CurrencyBridge } from "@ledgerhq/types-live";
import { assignFromAccountRaw, assignToAccountRaw } from "../serialization";
import { CosmosValidatorsManager } from "../CosmosValidatorsManager";
import { getCryptoCurrencyById } from "../../../currencies";
const receive = makeAccountBridgeReceive();

const defaultGetFees = (a, t) => (t.fees || new BigNumber(0)).times(t.gas || new BigNumber(0));
Expand Down Expand Up @@ -58,10 +67,16 @@ const getTransactionStatus = (account: Account, t: Transaction) => {
}

// Fill up recipient errors...
if (!t.recipient) {
errors.recipient = new RecipientRequired("");
} else if (isInvalidRecipient(t.recipient)) {
errors.recipient = new InvalidAddress("");
if (t.mode === "send") {
if (!t.recipient) {
errors.recipient = new RecipientRequired("");
} else if (isInvalidRecipient(t.recipient)) {
errors.recipient = new InvalidAddress("");
}
}

if (amount.eq(0)) {
errors.amount = new AmountRequired();
}

return Promise.resolve({
Expand Down Expand Up @@ -99,6 +114,8 @@ const accountBridge: AccountBridge<Transaction> = {
receive,
signOperation,
broadcast,
assignFromAccountRaw,
assignToAccountRaw,
};
const currencyBridge: CurrencyBridge = {
scanAccounts,
Expand All @@ -107,6 +124,11 @@ const currencyBridge: CurrencyBridge = {
return Promise.resolve(mockPreloadedData);
},
hydrate: (data: { validators?: CosmosValidatorItem[] }) => {
if (!data || typeof data !== "object") return;
const { validators } = data;
if (!validators || typeof validators !== "object" || !Array.isArray(validators)) return;
const cosmosValidatorsManager = new CosmosValidatorsManager(getCryptoCurrencyById("cosmos"));
cosmosValidatorsManager.hydrateValidators(validators);
setCosmosPreloadData("cosmos", asSafeCosmosPreloadData(data));
},
};
Expand Down
Loading

1 comment on commit ee4a5a8

@vercel
Copy link

@vercel vercel bot commented on ee4a5a8 Nov 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.