Skip to content

Commit

Permalink
swap most common currencies with speculos
Browse files Browse the repository at this point in the history
  • Loading branch information
bharamboure-ledger committed Oct 4, 2024
1 parent c641a41 commit ce53997
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 51 deletions.
5 changes: 5 additions & 0 deletions apps/ledger-live-desktop/tests/enum/Account.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Currency } from "./Currency";
import { AccountType } from "tests/enum/AccountType";

export class Account {
constructor(
public readonly currency: Currency,
public readonly accountName: string,
public readonly address: string,
public readonly accountType?: AccountType,
) {}

static readonly BTC_NATIVE_SEGWIT_1 = new Account(
Expand Down Expand Up @@ -257,12 +259,14 @@ export class Account {
Currency.ETH_USDT,
"Ethereum 1",
"0xB9051f83AC6e147924377BBEebd1Aa7aB43a67F6",
AccountType.ERC20,
);

static readonly ETH_USDT_2 = new Account(
Currency.ETH_USDT,
"Ethereum 2",
"0x43047a5023D55a8658Fcb1c1Cea468311AdAA3Ad",
AccountType.ERC20,
);

static readonly ETH_LIDO = new Account(
Expand Down Expand Up @@ -299,6 +303,7 @@ export class Account {
Currency.TRX_USDT,
"Tron 1",
"TDUKFB9wj3P5f2iNvkRuaDDeWVkTdUVhs1",
AccountType.TRC20,
);

static readonly TRX_BTT = new Account(
Expand Down
4 changes: 4 additions & 0 deletions apps/ledger-live-desktop/tests/enum/AccountType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum AccountType {
ERC20 = "erc20",
TRC20 = "trc20",
}
9 changes: 7 additions & 2 deletions apps/ledger-live-desktop/tests/enum/Swap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export enum Providers {
CHANGELLY = "changelly",
export class Provider {
constructor(
public readonly name: string,
public readonly uiName: string,
) {}
static readonly CHANGELLY = new Provider("changelly", "Changelly");
static readonly ONEINCH = new Provider("oneinch", "1inch");
}

export enum Rates {
Expand Down
6 changes: 5 additions & 1 deletion apps/ledger-live-desktop/tests/models/Swap.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Transaction } from "tests/models/Transaction";
import { Fee } from "tests/enum/Fee";
import { Account } from "../enum/Account";
import { Provider } from "../enum/Swap";

export class Swap extends Transaction {
provider: Provider;

constructor(
accountToDebit: Account,
accountToCredit: Account,
amount: string,
speed: Fee,
provider?: string,
provider: Provider,
public amountToReceive?: string,
public feesAmount?: string,
) {
super(accountToDebit, accountToCredit, amount, speed);
this.provider = provider;
}

public setAmountToReceive(value: string) {
Expand Down
44 changes: 34 additions & 10 deletions apps/ledger-live-desktop/tests/page/speculos.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,41 @@ export class SpeculosPage extends AppPage {
verifyAmount(`${swap.accountToDebit.currency.ticker} ${swap.amount}`, sendAmountScreen),
).toBeTruthy();
const getAmountScreen = await pressRightUntil(sendPattern[1]);
expect(
verifyAmount(
`${swap.accountToCredit.currency.ticker} ${extractNumberFromString(swap.amountToReceive)}`,
getAmountScreen,
),
).toBeTruthy();
const feesAmountScreen = await pressRightUntil(sendPattern[2]);
expect(
verifySwapFeesAmount(extractNumberFromString(swap.feesAmount), feesAmountScreen),
).toBeTruthy();
this.verifySwapGetAmountScreen(swap, getAmountScreen);
this.verifySwapFeesAmountScreen(swap, await pressRightUntil(sendPattern[2]));
await pressRightUntil(DeviceLabels.REJECT);
await pressBoth();
}

verifySwapGetAmountScreen(swap: Swap, getAmountScreen: string[]) {
if (swap.accountToCredit.currency.name === "Solana") {
expect(
verifyAmount(
`${extractNumberFromString(swap.amountToReceive)} ${swap.accountToCredit.currency.ticker}`,
getAmountScreen,
),
).toBeTruthy();
} else {
expect(
verifyAmount(
`${swap.accountToCredit.currency.ticker} ${extractNumberFromString(swap.amountToReceive)}`,
getAmountScreen,
),
).toBeTruthy();
}
}

verifySwapFeesAmountScreen(swap: Swap, feesAmountScreen: string[]) {
let speculosFeesAmount = "";
if (swap.feesAmount) {
//max number of chars on the screen
speculosFeesAmount =
extractNumberFromString(swap.feesAmount).length < 19
? extractNumberFromString(swap.feesAmount)
: extractNumberFromString(swap.feesAmount).substring(0, 18);
}
expect(
verifySwapFeesAmount(swap.accountToDebit.currency.name, speculosFeesAmount, feesAmountScreen),
).toBeTruthy();
}
}
38 changes: 29 additions & 9 deletions apps/ledger-live-desktop/tests/page/swap.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { waitFor } from "../utils/waitFor";
import { step } from "tests/misc/reporters/step";
import { ElectronApplication, expect } from "@playwright/test";
import { capitalizeFirstLetter } from "tests/utils/textParserUtils";
import { Account } from "tests/enum/Account";

export class SwapPage extends AppPage {
private currencyByName = (accountName: string) => this.page.getByText(accountName); // TODO: this is rubbish. Changed this
Expand Down Expand Up @@ -115,10 +116,7 @@ export class SwapPage extends AppPage {
}

@step("Select exchange quote $0 with rate $1")
async selectExchangeQuote(
providerName: "changelly" | "cic" | "oneinch" | "paraswap",
exchangeType: "fixed" | "float",
) {
async selectExchangeQuote(providerName: string, exchangeType: "fixed" | "float") {
await this.quoteContainer(providerName, exchangeType).click();
}

Expand Down Expand Up @@ -150,13 +148,35 @@ export class SwapPage extends AppPage {
return this.detailsSwapId.innerText();
}

@step("Select account to swap from: $0")
async selectAccountToSwapFrom(accountToSwapFrom: string) {
getAccountName(account: Account) {
//erc20 accounts names are stored in account currency property
return account.accountType ? account.currency.name : account.accountName;
}

async waitForAccountsBalance(accountAddress: string, timeout = 10000) {
const regex = new RegExp(`/blockchain/v\\d+/.+/${accountAddress}$`);
const fetchTxByAddressResponse = this.page.waitForResponse(response => {
return regex.test(response.url()) && response.status() === 200;
});
const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout, null));
return Promise.race([fetchTxByAddressResponse, timeoutPromise]);
}

@step("Select account to swap from")
async selectAccountToSwapFrom(accountToSwapFrom: Account) {
await this.waitForAccountsBalance(accountToSwapFrom.address);
await this.originCurrencyDropdown.click();
await this.dropdownOptions.locator(this.optionWithText(accountToSwapFrom)).click();
const accName = this.getAccountName(accountToSwapFrom);
if (accountToSwapFrom.accountType) {
await this.dropdownOptions
.locator(this.optionWithTextAndFollowingText(accountToSwapFrom.accountName, accName))
.first()
.click();
} else {
await this.dropdownOptions.locator(this.optionWithText(accName)).first().click();
}
const selectedAccountFrom = this.originCurrencyDropdown.locator(this.dropdownSelectedValue);
await expect(selectedAccountFrom).toHaveText(accountToSwapFrom);
await this.waitForPageLoadState();
await expect(selectedAccountFrom).toHaveText(accName);
}

@step("Fill in amount: $0")
Expand Down
105 changes: 79 additions & 26 deletions apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,94 @@ import { AppInfos } from "tests/enum/AppInfos";
import { setExchangeDependencies } from "@ledgerhq/live-common/e2e/speculos";
import { Fee } from "tests/enum/Fee";
import { Swap } from "tests/models/Swap";
import { Providers, Rates } from "tests/enum/Swap";
import { Provider, Rates } from "tests/enum/Swap";

const swaps = [
/*{
swap: new Swap(Account.ETH_1, Account.BTC_NATIVE_SEGWIT_1, "0.015", Fee.MEDIUM),
},*/
{
swap: new Swap(Account.BTC_NATIVE_SEGWIT_1, Account.ETH_1, "0.0006", Fee.MEDIUM),
swap: new Swap(
Account.ETH_1,
Account.BTC_NATIVE_SEGWIT_1,
"0.02",
Fee.MEDIUM,
Provider.CHANGELLY,
),
},
{
swap: new Swap(
Account.BTC_NATIVE_SEGWIT_1,
Account.ETH_1,
"0.00067",
Fee.MEDIUM,
Provider.CHANGELLY,
),
},
{
swap: new Swap(Account.ETH_USDT_1, Account.ETH_1, "35", Fee.MEDIUM, Provider.CHANGELLY),
},
{
swap: new Swap(Account.ETH_1, Account.SOL_1, "0.018", Fee.MEDIUM, Provider.CHANGELLY),
},
{
swap: new Swap(Account.ETH_1, Account.ETH_USDT_1, "0.02", Fee.MEDIUM, Provider.CHANGELLY),
},
{
swap: new Swap(
Account.BTC_NATIVE_SEGWIT_1,
Account.SOL_1,
"0.0006",
Fee.MEDIUM,
Provider.CHANGELLY,
),
},
{
swap: new Swap(
Account.BTC_NATIVE_SEGWIT_1,
Account.ETH_USDT_1,
"0.0006",
Fee.MEDIUM,
Provider.CHANGELLY,
),
},
{
swap: new Swap(
Account.ETH_USDT_1,
Account.BTC_NATIVE_SEGWIT_1,
"35",
Fee.MEDIUM,
Provider.CHANGELLY,
),
},
{
swap: new Swap(Account.ETH_USDT_1, Account.SOL_1, "40", Fee.MEDIUM, Provider.CHANGELLY),
},
];

const app: AppInfos = AppInfos.EXCHANGE;

for (const { swap } of swaps) {
test.beforeAll(async () => {
process.env.SWAP_DISABLE_APPS_INSTALL = "true";
process.env.SWAP_API_BASE = "https://swap-stg.ledger.com/v5";
});
test.describe("Swap", () => {
test.beforeAll(async () => {
process.env.SWAP_DISABLE_APPS_INSTALL = "true";
process.env.SWAP_API_BASE = "https://swap-stg.ledger.com/v5";
});

test.afterAll(async () => {
delete process.env.SWAP_DISABLE_APPS_INSTALL;
delete process.env.SWAP_API_BASE;
});
test.beforeEach(async () => {
setExchangeDependencies(
accPair.map(appName => ({
name: appName,
})),
);
});

test.afterAll(async () => {
delete process.env.SWAP_DISABLE_APPS_INSTALL;
delete process.env.SWAP_API_BASE;
});

test.describe.serial("Swap", () => {
const accPair: string[] = [swap.accountToDebit, swap.accountToCredit].map(acc =>
acc.currency.name.replace(/ /g, "_"),
);
setExchangeDependencies(
accPair.map(appName => ({
name: appName,
})),
acc.currency.speculosApp.name.replace(/ /g, "_"),
);

test.use({
userdata: "speculos-tests-app",
speculosApp: app,
Expand All @@ -46,15 +101,13 @@ for (const { swap } of swaps) {
app,
electronApp,
}) => {
const provider = Providers.CHANGELLY;
const rate = Rates.FLOAT;
await app.layout.goToSwap();
await app.swap.selectAccountToSwapFrom(swap.accountToDebit.accountName);
await app.swap.selectAccountToSwapFrom(swap.accountToDebit);
await app.swap.selectCurrencyToSwapTo(swap.accountToCredit.currency.name);

await app.swap.fillInOriginAmount(swap.amount);
await app.swap.selectExchangeQuote(provider, rate);
await app.swap.clickExchangeButton(electronApp, provider);
await app.swap.selectExchangeQuote(swap.provider.name, rate);
await app.swap.clickExchangeButton(electronApp, swap.provider.uiName);

Check failure on line 110 in apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts

View workflow job for this annotation

GitHub Actions / Desktop Tests E2E (Ubuntu) (3, 3)

[speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Bitcoin to Ethereum

1) [speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Bitcoin to Ethereum › Click Exchange button TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByText('Swap with Changelly') - locator resolved to <div class="sc-gKAaef hTujhK">Swap with Changelly</div> - attempting click action - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #1 - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #2 - waiting 20ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #3 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #4 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #5 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #6 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #7 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #8 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #9 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #10 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #11 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #12 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #13 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #14 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #15 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #16 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #17 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #18 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #19 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #20 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #21 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #22 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #23 - waiting 500

Check failure on line 110 in apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts

View workflow job for this annotation

GitHub Actions / Desktop Tests E2E (Ubuntu) (3, 3)

[speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Tether USD to Ethereum

2) [speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Tether USD to Ethereum › Click Exchange button TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByText('Swap with Changelly') - locator resolved to <div class="sc-gKAaef hTujhK">Swap with Changelly</div> - attempting click action - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #1 - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #2 - waiting 20ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #3 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #4 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #5 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #6 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #7 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #8 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #9 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #10 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #11 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #12 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #13 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #14 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #15 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #16 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #17 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #18 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #19 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #20 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #21 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #22 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #23 - waiting

Check failure on line 110 in apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts

View workflow job for this annotation

GitHub Actions / Desktop Tests E2E (Ubuntu) (3, 3)

[speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Bitcoin to Tether USD

3) [speculos_tests] › specs/speculos/swap.spec.ts:100:9 › Swap › Swap Bitcoin to Tether USD › Click Exchange button TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByText('Swap with Changelly') - locator resolved to <div class="sc-kEqXeH cQsTjH">Swap with Changelly</div> - attempting click action - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #1 - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #2 - waiting 20ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #3 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #4 - waiting 100ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #5 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #6 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #7 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #8 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #9 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #10 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #11 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #12 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #13 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #14 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #15 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #16 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #17 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #18 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #19 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #20 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #21 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #22 - waiting 500ms - waiting for element to be visible, enabled and stable - element is not enabled - retrying click action, attempt #23 - waiting 5
const amountTo = await app.swapDrawer.getAmountToReceive();
const fees = await app.swapDrawer.getFees();
swap.setAmountToReceive(amountTo);
Expand All @@ -63,7 +116,7 @@ for (const { swap } of swaps) {
await app.swapDrawer.verifyAmountSent(swap.amount, swap.accountToDebit.currency.ticker);
await app.swapDrawer.verifySourceAccount(swap.accountToDebit.currency.name);
await app.swapDrawer.verifyTargetCurrency(swap.accountToCredit.currency.name);
await app.swapDrawer.verifyProvider(provider);
await app.swapDrawer.verifyProvider(swap.provider.name);
await app.speculos.verifyAmountsAndRejectSwap(swap);
await app.swapDrawer.verifyExchangeErrorTextContent("Operation denied on device");
});
Expand Down
20 changes: 17 additions & 3 deletions libs/ledger-live-common/src/e2e/speculos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ export type Spec = {
export type Dependency = { name: string; appVersion?: string };

export function setExchangeDependencies(dependencies: Dependency[]) {
specs["Exchange"].dependencies = dependencies;
const map = new Map<string, Dependency>();
for (const dep of dependencies) {
if (!map.has(dep.name)) {
map.set(dep.name, dep);
}
}
specs["Exchange"].dependencies = Array.from(map.values());
}

type Specs = {
Expand Down Expand Up @@ -375,8 +381,16 @@ export function verifyAmount(amount: string, text: string[]): boolean {
return amountDevice.includes(amount);
}

export function verifySwapFeesAmount(amount: string, text: string[]): boolean {
const amountDevice = text[3];
export function verifySwapFeesAmount(currency: string, amount: string, text: string[]): boolean {
let amountDevice = text[1];
switch (currency) {
case "Ethereum":
amountDevice = text[3];
break;
case "Tether USD":
amountDevice = text[3];
break;
}
return amountDevice.includes(amount);
}

Expand Down
1 change: 1 addition & 0 deletions libs/speculos-transport/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export async function createSpeculosDevice(
"40000",
]),
];
console.log("speculos", `${speculosID}: spawning = ${params.join(" ")}`);

log("speculos", `${speculosID}: spawning = ${params.join(" ")}`);

Expand Down

0 comments on commit ce53997

Please sign in to comment.