Skip to content
Open
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
125 changes: 102 additions & 23 deletions test/helpers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,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.
*/
Expand All @@ -1421,17 +1425,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;
}

Expand All @@ -1444,6 +1462,23 @@ export async function acknowledgeReceivedPayment({ timeout = 30_000 }: { timeout
await sleep(300);
}

/** Dismisses the received payment sheet when it shows, and does nothing when it does not.
*
* Use this where the sheet is platform- or build-dependent, such as a deposit the wallet first sees
* already confirmed (synonymdev/bitkit-android#797), which Android notifies and iOS does not yet.
*/
export async function dismissReceivedPaymentIfShown({
timeout = 20_000,
}: { timeout?: number } = {}): Promise<boolean> {
try {
await acknowledgeReceivedPayment({ timeout });
return true;
} catch {
console.info('→ No received payment sheet shown, continuing.');
return false;
}
}

export async function acknowledgeReceivedPaymentIfPresent(): Promise<boolean> {
if (getBackend() === 'local') {
await acknowledgeReceivedPayment();
Expand Down Expand Up @@ -1471,28 +1506,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<boolean> {
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<boolean> {
Expand Down
6 changes: 6 additions & 0 deletions test/specs/boost.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
restoreWallet,
enterAddress,
waitForToast,
dismissReceivedPaymentIfShown,
} from '../helpers/actions';
import initElectrum from '../helpers/electrum';
import { reinstallApp } from '../helpers/setup';
Expand Down Expand Up @@ -111,6 +112,11 @@ describe('@boost - Boost', () => {

// mine new block
await mineBlocks(1);

// the restored wallet never saw the boosted tx in the mempool, so this confirmation is its
// first sight of the deposit and the received sheet opens over the screen
await dismissReceivedPaymentIfShown();

await doNavigationClose();
await sleep(500);

Expand Down