Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QAA-377][Speculos][Detox] Adapt LLM send test #8796

Merged
merged 5 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions .github/workflows/test-mobile-e2e-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ jobs:
allure-report-ios:
name: "Allure Reports Export on Server"
runs-on: [ledger-live-medium]
if: ${{ always() && (inputs.slack_notif || github.event_name == 'push') }}
if: ${{ !inputs.speculos_tests && (inputs.slack_notif || github.event_name == 'push') }}
needs: [detox-tests-ios]
outputs:
report-url: ${{ steps.upload.outputs.report-url }}
Expand Down Expand Up @@ -200,7 +200,9 @@ jobs:
AVD_RAM_SIZE: 4096M
AVD_OPTIONS: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
outputs:
status: ${{ steps.detox.outcome }}
status_1: ${{ steps.set-output.outputs.status_1 }}
status_2: ${{ steps.set-output.outputs.status_2 }}
status_3: ${{ steps.set-output.outputs.status_3 }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -315,6 +317,10 @@ jobs:
with:
name: "android-test-artifacts-${{ matrix.shardIndex }}"
path: apps/ledger-live-mobile/artifacts/
- name: Set job output based on detox result
id: set-output
if: always()
run: echo "status_${{ matrix.shardIndex }}=${{ steps.detox.outcome }}" >> $GITHUB_OUTPUT

allure-report-android:
name: "Allure Reports Export on Server"
Expand All @@ -323,7 +329,7 @@ jobs:
outputs:
report-url: ${{ steps.upload.outputs.report-url }}
result: ${{ steps.summary.outputs.test_result }}
status: ${{ needs.detox-tests-android.outputs.status }}
finalStatus: ${{ steps.aggregate.outputs.finalStatus }}
needs: [detox-tests-android]
steps:
- name: checkout
Expand All @@ -344,6 +350,22 @@ jobs:
with:
allure-results-path: android-test-artifacts
platform: android
- name: Aggregate test results
id: aggregate
run: |
if [ "${{ env.SPECULOS_RUN }}" == "true" ]; then
statuses=("${{ needs.detox-tests-android.outputs.status_1 }}" "${{ needs.detox-tests-android.outputs.status_2 }}" "${{ needs.detox-tests-android.outputs.status_3 }}")
else
statuses=("${{ needs.detox-tests-android.outputs.status_1 }}")
fi
finalStatus="success"
for status in "${statuses[@]}"; do
if [ "$status" != "success" ]; then
finalStatus="failure"
break
fi
done
echo "finalStatus=$finalStatus" >> $GITHUB_OUTPUT

upload-to-xray:
name: "Upload to Xray"
Expand Down Expand Up @@ -409,7 +431,7 @@ jobs:
env:
IOS_STATUS: ${{ needs.allure-report-ios.outputs.status }}
IOS_REPORT_URL: ${{ needs.allure-report-ios.outputs.report-url }}
ANDROID_STATUS: ${{ needs.allure-report-android.outputs.status }}
ANDROID_STATUS: ${{ needs.allure-report-android.outputs.finalStatus }}
ANDROID_REPORT_URL: ${{ needs.allure-report-android.outputs.report-url }}
steps:
- name: format message
Expand Down
4 changes: 4 additions & 0 deletions apps/ledger-live-mobile/e2e/bridge/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ const retryDelay = 500; // Initial retry delay in milliseconds
export function init() {
const wsPort = LaunchArguments.value()["wsPort"] || "8099";
const mock = LaunchArguments.value()["mock"];
const disable_broadcast = LaunchArguments.value()["disable_broadcast"];

log(`[E2E Bridge Client]: wsPort=${wsPort}, mock=${mock}`);

if (mock == "0") setEnv("MOCK", "");
setEnv("DISABLE_TRANSACTION_BROADCAST", disable_broadcast != "0");

if (ws) {
ws.close();
}
Expand Down
14 changes: 14 additions & 0 deletions apps/ledger-live-mobile/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ export function getWebElementByTag(tag: string, index = 0) {
return web.element(by.web.tag(tag)).atIndex(index);
}

export async function IsIdVisible(id: string | RegExp) {
try {
await waitFor(element(by.id(id)))
.toBeVisible()
.withTimeout(1000);
return true;
} catch {
return false;
}
}

export async function tapById(id: string | RegExp, index = 0) {
return getElementById(id, index).tap();
}
Expand Down Expand Up @@ -180,6 +191,9 @@ export async function launchApp() {
detoxURLBlacklistRegex:
'\\(".*sdk.*.braze.*",".*.googleapis.com/.*",".*clients3.google.com.*"\\)',
mock: getEnv("MOCK") ? getEnv("MOCK") : "0",
disable_broadcast: getEnv("DISABLE_TRANSACTION_BROADCAST")
? getEnv("DISABLE_TRANSACTION_BROADCAST")
: "1",
},
languageAndLocale: {
language: "en-US",
Expand Down
43 changes: 43 additions & 0 deletions apps/ledger-live-mobile/e2e/models/send.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Currency } from "@ledgerhq/live-common/e2e/enum/Currency";
import { Application } from "../page";
import { Transaction } from "@ledgerhq/live-common/e2e/models/Transaction";

export async function verifyAppValidationSendInfo(
app: Application,
transaction: Transaction,
amount: string,
) {
const currenciesForValidationAmount = [
Currency.sepETH,
Currency.DOT,
Currency.POL,
Currency.ALGO,
Currency.ADA,
Currency.DOGE,
Currency.SOL,
Currency.TRX,
Currency.XLM,
Currency.XRP,
Currency.ATOM,
Currency.BCH,
];

const currenciesForValidationRecipient = [Currency.sepETH, Currency.POL];
const currenciesForValidationSender = [Currency.ATOM];

const currency = transaction.accountToCredit.currency;
const addressRecipient = transaction.accountToCredit.address;
const addressSender = transaction.accountToDebit.address;

if (currenciesForValidationAmount.includes(currency)) {
await app.send.expectValidationAmount(amount);
}

if (currenciesForValidationRecipient.includes(currency)) {
await app.send.expectValidationAddress(addressRecipient);
}

if (currenciesForValidationSender.includes(currency)) {
await app.send.expectValidationAddress(addressSender);
}
}
15 changes: 14 additions & 1 deletion apps/ledger-live-mobile/e2e/page/accounts/account.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ export default class AccountPage {
accountAdvancedLogRow = () => getElementById("account-advanced-log-row");
accountDeleteRow = () => getElementById("account-settings-delete-row");
accountDeleteConfirm = () => getElementById("delete-account-confirmation-button");
operationHistorySectionId = (accountId: string) => `operations-history-${accountId}`;
operationHistorySection = "operations-history-";
operationHistorySectionRegexp = new RegExp(this.operationHistorySection + ".*");
operationHistorySectionId = (accountId: string) => this.operationHistorySection + accountId;
accountScreenScrollView = "account-screen-scrollView";
accountAdvancedLogsId = "account-advanced-logs";
receiveButton = () => getElementById("account-quick-action-button-Receive");
sendButton = () => getElementById("account-quick-action-button-Send");

@Step("Open account settings")
async openAccountSettings() {
Expand Down Expand Up @@ -41,6 +44,11 @@ export default class AccountPage {
await expect(getElementById(id)).toBeVisible();
}

@Step("Scroll to transaction history")
async scrollToTransactions() {
await scrollToId(this.operationHistorySectionRegexp, this.accountScreenScrollView);
}

@Step("Expect account balance to be visible")
async expectAccountBalanceVisible(accountId: string) {
await expect(this.accountGraph(accountId)).toBeVisible();
Expand All @@ -60,4 +68,9 @@ export default class AccountPage {
async tapReceive() {
await tapByElement(this.receiveButton());
}

@Step("Tap on send button")
async tapSend() {
await tapByElement(this.sendButton());
}
}
7 changes: 7 additions & 0 deletions apps/ledger-live-mobile/e2e/page/common.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class CommonPage {
searchBarId = "common-search-field";
searchBar = () => getElementById(this.searchBarId);
successCloseButtonId = "success-close-button";
successViewDetailsButtonId = "success-view-details-button";
closeButton = () => getElementById("NavigationHeaderCloseButton");

accoundCardId = (id: string) => "account-card-" + id;
Expand Down Expand Up @@ -53,6 +54,12 @@ export default class CommonPage {
await tapById(this.successCloseButtonId);
}

@Step("Tap on view details")
async successViewDetails() {
await waitForElementById(this.successViewDetailsButtonId);
await tapById(this.successViewDetailsButtonId);
}

async selectAccount(accountId: string) {
const id = this.accoundCardId(accountId);
await waitForElementById(id);
Expand Down
4 changes: 2 additions & 2 deletions apps/ledger-live-mobile/e2e/page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ export class Application {
public transfertMenu = new TransfertMenuDrawer();
public walletTabNavigator = new WalletTabNavigatorPage();

constructor() {
constructor(userdata?: string) {
if (!getEnv("MOCK")) {
// Create a temporary userdata file for Speculos tests
const originalUserdata = "skip-onboarding";
const originalUserdata = userdata || "skip-onboarding";
this.userdataSpeculos = `temp-userdata-${Date.now()}`;
this.userdataPath = getUserdataPath(this.userdataSpeculos);
const originalFilePath = getUserdataPath(originalUserdata);
Expand Down
8 changes: 7 additions & 1 deletion apps/ledger-live-mobile/e2e/page/speculos.page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { expectValidAddressDevice } from "@ledgerhq/live-common/e2e/speculos";
import { expectValidAddressDevice, signSendTransaction } from "@ledgerhq/live-common/e2e/speculos";
import { Account } from "@ledgerhq/live-common/e2e/enum/Account";
import { Transaction } from "@ledgerhq/live-common/e2e/models/Transaction";

export default class SpeculosPage {
@Step("Verify receive address correctness on device")
async expectValidAddressDevice(account: Account, addressDisplayed: string) {
await expectValidAddressDevice(account, addressDisplayed);
}

@Step("Sign Send Transaction")
async signSendTransaction(tx: Transaction) {
await signSendTransaction(tx);
}
}
18 changes: 16 additions & 2 deletions apps/ledger-live-mobile/e2e/page/trade/operationDetails.page.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import { getElementById } from "../../helpers";
import { getElementById, scrollToId, waitForElementById } from "../../helpers";
import { expect } from "detox";

export default class OperationDetailsPage {
title = () => getElementById("operationDetails-title");
titleId = "operationDetails-title";
title = () => getElementById(this.titleId);
account = () => getElementById("operationDetails-account");
amount = () => getElementById("operationDetails-amount");
recipientId = "operationDetails-recipient0";

async isOpened() {
await expect(this.title()).toBeVisible();
}

@Step("Wait for operation details")
async waitForOperationDetails() {
await waitForElementById(this.titleId);
}

@Step("Check account details")
async checkAccount(account: string) {
await expect(this.account()).toHaveText(account);
}

async checkAmount(amount: string) {
await expect(this.amount()).toHaveText(amount);
}

@Step("Check recipient details")
async checkRecipient(recipient: string) {
await scrollToId(this.recipientId);
await expect(getElementById(this.recipientId)).toHaveText(recipient);
}
}
41 changes: 41 additions & 0 deletions apps/ledger-live-mobile/e2e/page/trade/send.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ import {
openDeeplink,
typeTextById,
tapByElement,
IsIdVisible,
} from "../../helpers";
import { expect } from "detox";

const baseLink = "send";

export default class SendPage {
summaryAmount = () => getElementById("send-summary-amount");
summaryRecipient = () => getElementById("send-summary-recipient");
validationAmountId = "send-validation-amount";
validationAddressId = "send-validation-address";
getStep1HeaderTitle = () => getElementById("send-header-step1-title");
recipientContinueButtonId = "recipient-continue-button";
recipientInputId = "recipient-input";
amountInputId = "amount-input";
amountContinueButton = () => getElementById("amount-continue-button");
summaryContinueButton = () => getElementById("summary-continue-button");
highFreeConfirmButtonID = "confirmation-modal-confirm-button";

async openViaDeeplink() {
await openDeeplink(baseLink);
Expand All @@ -42,6 +47,12 @@ export default class SendPage {
await tapById(this.recipientContinueButtonId);
}

@Step("Set recipient and continue")
async setRecipientAndContinue(address: string) {
await this.setRecipient(address);
await this.recipientContinue();
}

async setAmount(amount: string) {
const element = getElementById(this.amountInputId);
await element.replaceText(amount);
Expand All @@ -52,11 +63,41 @@ export default class SendPage {
await tapByElement(this.amountContinueButton());
}

@Step("Set amount and continue")
async setAmountAndContinue(amount: string) {
await this.setAmount(amount);
await this.amountContinue();
}

async summaryContinue() {
await tapByElement(this.summaryContinueButton());
}

@Step("Expect amount in summary")
async expectSummaryAmount(amount: string) {
await expect(this.summaryAmount()).toHaveText(amount);
}

@Step("Expect recipient in summary")
async expectSummaryRecepient(recipient: string) {
await expect(this.summaryRecipient()).toHaveText(recipient);
}

@Step("Dismiss high fee modal if visible")
async dismissHighFeeModal() {
if (await IsIdVisible(this.highFreeConfirmButtonID))
await tapById(this.highFreeConfirmButtonID);
}

@Step("Expect amount in device validation screen")
async expectValidationAmount(amount: string) {
await waitForElementById(this.validationAmountId);
await expect(getElementById(this.validationAmountId)).toHaveText(amount);
}

@Step("Expect address in device validation screen")
async expectValidationAddress(recipient: string) {
await waitForElementById(this.validationAddressId);
await expect(getElementById(this.validationAddressId)).toHaveText(recipient);
}
}
Loading
Loading