Skip to content

Commit 75ff1ea

Browse files
authored
fix: stabilize mainnet LN payment nightly tests (#130)
Addresses flaky LN payment tests (Strike/WOS) that were failing ~50% of nightly runs due to timing and network issues on CI runners. Changes to ln.e2e.ts: - Replace fixed sleep(15s) with explicit wallet readiness check - Add explicit waits between screen transitions (amount, confirm) - Increase payment timeout from 60s to 300s for mainnet routing - Add error toast detection (PaymentFailed, ExpiredLightning, etc.) - Add diagnostic logging at each step for CI failure diagnosis - Use 60s address resolution timeout for LNURL endpoints Changes to actions.ts: - Increase TOS Continue button timeout to 60s for slow CI emulators - Increase RestoreButton timeout to 60s - Dismiss Android keyboard after seed entry to prevent UI blocking - Replace Suggestions widget check with TotalBalance-primary - Add configurable timeout to typeAddressAndVerifyContinue - Add configurable addressTimeout to enterAddress Made-with: Cursor
1 parent d70cbd0 commit 75ff1ea

2 files changed

Lines changed: 89 additions & 12 deletions

File tree

test/helpers/actions.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ export async function restoreWallet(
671671
}
672672

673673
// Terms of service
674-
await elementById('Continue').waitForDisplayed();
674+
await elementById('Continue').waitForDisplayed({ timeout: 60_000 });
675675
await sleep(1000); // Wait for the app to settle
676676
await tap('Continue');
677677
await sleep(500);
@@ -688,6 +688,16 @@ export async function restoreWallet(
688688
await typeText('Word-0', seed);
689689
}
690690
await sleep(1500); // wait for the app to settle
691+
692+
if (driver.isAndroid) {
693+
try {
694+
await driver.hideKeyboard();
695+
} catch {
696+
// keyboard may already be hidden
697+
}
698+
await sleep(500);
699+
}
700+
691701
// Passphrase
692702
if (passphrase) {
693703
await tap('AdvancedButton');
@@ -696,7 +706,10 @@ export async function restoreWallet(
696706
}
697707

698708
// Restore wallet
699-
await tap('RestoreButton');
709+
const restoreBtn = await elementById('RestoreButton');
710+
await restoreBtn.waitForDisplayed({ timeout: 60_000 });
711+
await sleep(150);
712+
await restoreBtn.click();
700713
await waitForSetupWalletScreenFinish();
701714

702715
// Wait for Get Started
@@ -729,9 +742,7 @@ export async function restoreWallet(
729742
}
730743
}
731744

732-
// Wait for Suggestions Label to appear
733-
const suggestions = await elementById('Suggestions');
734-
await suggestions.waitForDisplayed();
745+
await elementById('TotalBalance-primary').waitForDisplayed({ timeout: 90_000 });
735746
}
736747

737748
type addressType = 'bitcoin' | 'lightning';
@@ -1352,23 +1363,28 @@ export async function typeRecipientInput(
13521363
export async function typeAddressAndVerifyContinue({
13531364
address,
13541365
reverse = false,
1366+
timeout = 30_000,
13551367
}: {
13561368
address: string;
13571369
reverse?: boolean;
1370+
timeout?: number;
13581371
}) {
13591372
await typeRecipientInput(address);
13601373
await sleep(1000);
1361-
await elementById('AddressContinue').waitForEnabled({ reverse });
1374+
await elementById('AddressContinue').waitForEnabled({ reverse, timeout });
13621375
}
13631376

1364-
export async function enterAddress(address: string, { acceptCameraPermission = true } = {}) {
1377+
export async function enterAddress(
1378+
address: string,
1379+
{ acceptCameraPermission = true, addressTimeout = 30_000 } = {},
1380+
) {
13651381
await tap('Send');
13661382
await sleep(700);
13671383
if (acceptCameraPermission) {
13681384
await handleAndroidAlert('permission_allow_one_time_button');
13691385
}
13701386
await tap('RecipientManual');
1371-
await typeAddressAndVerifyContinue({ address });
1387+
await typeAddressAndVerifyContinue({ address, timeout: addressTimeout });
13721388
await tap('AddressContinue');
13731389
}
13741390

test/specs/mainnet/ln.e2e.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import {
99
} from '../../helpers/actions';
1010
import { ciIt } from '../../helpers/suite';
1111

12+
const PAYMENT_TIMEOUT_MS = 300_000;
13+
const WALLET_SYNC_TIMEOUT_MS = 90_000;
14+
const SCREEN_TRANSITION_TIMEOUT_MS = 60_000;
15+
const LN_STABILIZE_DELAY_MS = 10_000;
16+
17+
const ERROR_TOASTS = ['PaymentFailedToast', 'ExpiredLightningToast', 'InsufficientSpendingToast'];
18+
1219
type MainnetLnSuiteConfig = {
1320
suiteTag: string;
1421
testTag: string;
@@ -52,22 +59,76 @@ function resolveMainnetLnReceiver(config: MainnetLnSuiteConfig): MainnetLnReceiv
5259
};
5360
}
5461

62+
async function waitForWalletReady(): Promise<void> {
63+
console.info('→ [LN] Waiting for wallet home screen...');
64+
await elementById('TotalBalance-primary').waitForDisplayed({ timeout: WALLET_SYNC_TIMEOUT_MS });
65+
console.info('→ [LN] Home screen ready, letting LN node stabilize...');
66+
await sleep(LN_STABILIZE_DELAY_MS);
67+
}
68+
69+
async function waitForAmountScreen(): Promise<void> {
70+
console.info('→ [LN] Waiting for amount entry screen...');
71+
await elementById('N0').waitForDisplayed({ timeout: SCREEN_TRANSITION_TIMEOUT_MS });
72+
}
73+
74+
async function waitForConfirmScreen(): Promise<void> {
75+
console.info('→ [LN] Waiting for send confirmation screen...');
76+
await elementById('GRAB').waitForDisplayed({ timeout: SCREEN_TRANSITION_TIMEOUT_MS });
77+
await sleep(500);
78+
}
79+
80+
async function waitForPaymentResult(): Promise<void> {
81+
console.info(`→ [LN] Waiting for payment result (timeout: ${PAYMENT_TIMEOUT_MS / 1000}s)...`);
82+
await browser.waitUntil(
83+
async () => {
84+
const success = await elementById('SendSuccess').isDisplayed().catch(() => false);
85+
if (success) {
86+
console.info('→ [LN] Payment succeeded');
87+
return true;
88+
}
89+
90+
for (const toastId of ERROR_TOASTS) {
91+
const visible = await elementById(toastId).isDisplayed().catch(() => false);
92+
if (visible) {
93+
throw new Error(`Payment failed with error toast: ${toastId}`);
94+
}
95+
}
96+
97+
return false;
98+
},
99+
{
100+
timeout: PAYMENT_TIMEOUT_MS,
101+
interval: 3_000,
102+
timeoutMsg: `Payment did not complete within ${PAYMENT_TIMEOUT_MS / 1000}s`,
103+
},
104+
);
105+
}
106+
55107
async function sendPaymentToLnAddress(receiver: MainnetLnReceiver): Promise<void> {
108+
console.info('→ [LN] Restoring wallet...');
56109
await restoreWallet(receiver.seed, {
57110
expectBackupSheet: false,
58111
reinstall: false,
59112
expectAndroidAlert: false,
60113
});
61114

62-
await sleep(15_000); // wait for wallet to stabilize
115+
await waitForWalletReady();
63116

64-
await enterAddress(receiver.lnAddress, { acceptCameraPermission: false });
65-
await enterAmount(receiver.amountSats);
117+
console.info(`→ [LN] Entering address: ${receiver.lnAddress}`);
118+
await enterAddress(receiver.lnAddress, { acceptCameraPermission: false, addressTimeout: 60_000 });
119+
await waitForAmountScreen();
66120

121+
console.info(`→ [LN] Entering amount: ${receiver.amountSats} sats`);
122+
await enterAmount(receiver.amountSats);
67123
await tap('ContinueAmount');
124+
125+
await waitForConfirmScreen();
126+
console.info('→ [LN] Swiping to send...');
68127
await dragOnElement('GRAB', 'right', 0.95);
69-
await elementById('SendSuccess').waitForDisplayed({ timeout: 60_000 });
128+
129+
await waitForPaymentResult();
70130
await tap('Close');
131+
console.info('→ [LN] Test complete');
71132
}
72133

73134
function defineMainnetLnSuite(config: MainnetLnSuiteConfig): void {

0 commit comments

Comments
 (0)