From c90ba71c05dc630b13f562252023e8a0aeda8678 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Wed, 16 Sep 2026 15:46:49 -0300 Subject: [PATCH 1/4] fix: expect home after android transfer to savings Co-Authored-By: Claude Opus 5 (1M context) --- test/helpers/actions.ts | 4 ---- test/specs/transfer.e2e.ts | 1 - 2 files changed, 5 deletions(-) diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index c14555f..332f677 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -1184,10 +1184,6 @@ export async function transferSpendingToSavings() { await elementById('TransferSuccess-button').waitForDisplayed(); await tap('TransferSuccess-button'); - if (driver.isAndroid) { - await doNavigationClose(); - } - await sleep(1000); await expectSavingsBalance(0, { condition: 'gt' }); await expectSpendingBalance(0); diff --git a/test/specs/transfer.e2e.ts b/test/specs/transfer.e2e.ts index a58e7d9..2c727ce 100644 --- a/test/specs/transfer.e2e.ts +++ b/test/specs/transfer.e2e.ts @@ -418,7 +418,6 @@ describe('@transfer - Transfer', () => { await dragOnElement('GRAB', 'right', 0.95); await elementById('TransferSuccess').waitForDisplayed(); await tap('TransferSuccess-button'); - if (driver.isAndroid) await tap('NavigationBack'); await sleep(1000); // check channel is closed From 0daed3d5379c7c6caa36480443f59eacb3db8474 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 18 Sep 2026 14:49:07 +0000 Subject: [PATCH 2/4] test: soft-wait settings balance toasts Align settings_01 / settings_06 with the #232 waitForToastBestEffort pattern so a missed or overlaying BalanceUnitSwitchedToast / BalanceHiddenToast does not fail openSettings (DrawerSettings) after TotalBalance. UI unit text and ShowBalance remain the source of truth. Co-authored-by: piotr-iohk --- test/specs/settings.e2e.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/specs/settings.e2e.ts b/test/specs/settings.e2e.ts index 28fc6c1..3c2dd7e 100644 --- a/test/specs/settings.e2e.ts +++ b/test/specs/settings.e2e.ts @@ -16,6 +16,7 @@ import { getAccessibleText, doNavigationClose, waitForToast, + waitForToastBestEffort, ToastId, } from '../helpers/actions'; import { electrumHost, electrumPort } from '../helpers/constants'; @@ -44,9 +45,8 @@ describe('@settings - Settings', () => { await tap('TotalBalance'); } await expect(fiatSymbol).toHaveText('$'); - if (driver.isIOS) { - await waitForToast('BalanceUnitSwitchedToast'); - } + // UI unit text is the source of truth; a missed/auto-dismissed toast must not fail. + await waitForToastBestEffort('BalanceUnitSwitchedToast'); // - change settings (currency to EUR) // await openSettings(); @@ -207,9 +207,8 @@ describe('@settings - Settings', () => { await dragOnElement('TotalBalance', 'right', 0.5); } await elementById('ShowBalance').waitForDisplayed(); - if (driver.isIOS) { - await waitForToast('BalanceHiddenToast', { waitToDisappear: false, dismiss: true }); - } + // ShowBalance is the source of truth; a missed/auto-dismissed toast must not fail. + await waitForToastBestEffort('BalanceHiddenToast'); // Disable 'swipe to hide balance' await openSettings('security'); From 5491b7d6a0957e7245808fa010aa6d3b56af581e Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 18 Sep 2026 15:00:23 -0300 Subject: [PATCH 3/4] fix: dismiss best-effort toast so it stops covering header Co-Authored-By: Claude Opus 5 (1M context) (cherry picked from commit 9b95a973d585b771954a038e5df4910d504cc1a7) --- test/helpers/actions.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index 332f677..7555dbf 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -1392,6 +1392,10 @@ export async function waitForToast( * or dismissed too quickly). Does not throw on timeout—callers must handle * verification via other means (e.g., UI state confirmation). * + * Once the toast is observed it is cleared before returning, so it cannot keep + * covering the header: a toast overlays HeaderMenu for its whole lifetime and + * swallows the tap that opens the drawer. + * * On iOS, uses waitToDisappear pattern since toasts render in a separate window * where drag-dismiss hits wrong coordinates. */ @@ -1417,17 +1421,31 @@ export async function waitForToastBestEffort( }, { timeout, interval: pollingInterval } ); - - if (driver.isIOS) { - await el.waitForDisplayed({ reverse: true, timeout: 5_000 }).catch(() => { - // Toast may have dismissed immediately; that's fine - }); - } } catch { // Toast wasn't displayed within timeout—may have already appeared and dismissed // or never appeared. Caller should verify via other means. } + if (!toastSeen) { + return false; + } + + // Re-check instead of trusting toastSeen: dragOnElement waits on the element + // with the global 30s timeout, which would be spent in full on a toast that + // already went away. + if (driver.isAndroid && (await el.isDisplayed().catch(() => false))) { + // Drag it away like waitForToast does: an undismissed toast keeps covering + // HeaderMenu for its full duration, so the next openSettings() tap lands on + // the toast instead of the drawer button. + await dragOnElement(toastId, 'up', 0.2).catch(() => { + // Toast may have auto-dismissed mid-drag; the wait below settles it + }); + } + + await el.waitForDisplayed({ reverse: true, timeout: 5_000 }).catch(() => { + // Toast may have dismissed immediately; that's fine + }); + return toastSeen; } From 4f3af6e3b635acc09e5a42ad1cb22138955038be Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 18 Sep 2026 13:19:21 +0000 Subject: [PATCH 4/4] fix: best-effort dismiss of Background Payments intro sheet Hard-waiting on BackgroundPaymentsIntro-later flakes the Android send shard when the timed sheet is already gone or auto-dismissed. Poll like waitForToastBestEffort and still dismiss Later/Cancel when the intro is shown. Co-authored-by: piotr-iohk (cherry picked from commit fe1e0fc61b51441d7c11d2764a5b599c77071708) --- test/helpers/actions.ts | 78 ++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index 710f2ec..b546444 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -1485,28 +1485,72 @@ export async function acknowledgeExternalSuccess() { await sleep(300); } +/** + * Dismisses the Background Payments timed sheet if it is present. + * + * Best-effort: the intro can already be gone (auto-dismissed, previously + * acknowledged, or never queued after HeaderMenu). Does not throw when the + * dismiss control is missing — callers continue. When the intro IS shown, + * it is still dismissed via Later / Cancel so remaining coverage can run + * on an unobstructed home screen. + * + * Pattern matches waitForToastBestEffort: poll for visibility, act if seen, + * swallow timeout if the sheet never appeared or vanished mid-dismiss. + * + * @returns true if the sheet was observed and a dismiss was attempted, + * false if it was already gone. + */ export async function dismissBackgroundPaymentsTimedSheet({ triggerTimedSheet = false, -}: { triggerTimedSheet?: boolean } = {}) { - if (triggerTimedSheet) { - await doTriggerTimedSheet(); + timeout = 10_000, +}: { triggerTimedSheet?: boolean; timeout?: number } = {}): Promise { + const sheetId = driver.isAndroid + ? 'BackgroundPaymentsIntro-later' + : 'BackgroundPaymentsDescription'; + const dismissId = driver.isAndroid ? 'BackgroundPaymentsIntro-later' : 'BackgroundPaymentsCancel'; + + await triggerTimedSheetUnlessPresent(sheetId, triggerTimedSheet); + + const el = elementById(sheetId); + let sheetSeen = false; + + try { + await browser.waitUntil( + async () => { + const displayed = await el.isDisplayed().catch(() => false); + if (displayed) { + sheetSeen = true; + return true; + } + return false; + }, + { timeout, interval: 200 } + ); + } catch { + // Sheet wasn't displayed within timeout — may have already appeared and + // dismissed, or never appeared after the HeaderMenu trigger race. + console.info(`→ ${sheetId} not displayed; treating Background Payments intro as already gone`); + return false; } - if (driver.isAndroid) { - await elementById('BackgroundPaymentsIntro-later').waitForDisplayed(); - await sleep(500); // wait for the app to settle - await tap('BackgroundPaymentsIntro-later'); - } else { - const description = elementById('BackgroundPaymentsDescription'); - await description.waitForDisplayed(); - const cancel = elementById('BackgroundPaymentsCancel'); - await cancel.waitForDisplayed(); - // The iOS sheet animates while appearing. Click a fresh element reference - // immediately; the generic tap() delay makes this button prone to staleness. - await cancel.click(); - await description.waitForDisplayed({ reverse: true }); + if (!sheetSeen) { + return false; } - await sleep(500); + + await sleep(500); // wait for the app to settle + try { + const dismissEl = elementById(dismissId); + const stillShown = await dismissEl.isDisplayed().catch(() => false); + if (!stillShown) { + console.info(`→ ${dismissId} gone before dismiss tap; treating intro as already dismissed`); + return true; + } + await dismissEl.click(); + await sleep(500); + } catch (error) { + console.info(`→ ${dismissId} disappeared before dismiss tap; continuing`, error); + } + return true; } async function isTestIdDisplayed(testId: string): Promise {