Skip to content
Closed
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
83 changes: 3 additions & 80 deletions test/helpers/hardware-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,11 @@ import {
tapFirstByAccessibilityIdPrefix,
typeText,
handleAndroidAlert,
waitForToast,
type BalanceCondition,
dismissBackupTimedSheet,
dismissBackgroundPaymentsTimedSheet,
dismissQuickPayIntro,
confirmInputOnKeyboard,
} from './actions';
import { openHomeWidgets, openSettings } from './navigation';
import { deposit, getBackend, mineBlocks } from './regtest';
import { deposit, mineBlocks } from './regtest';

const E2E_ROOT = path.resolve(__dirname, '..', '..');
const ARTIFACTS_DIR = path.join(E2E_ROOT, 'artifacts');
Expand Down Expand Up @@ -305,69 +301,12 @@ export async function sendOnchainFromHardwareWallet({
await tap('Close');
}

export async function transferHardwareWalletToSpending({
amountSats,
waitForSync,
}: {
amountSats: number;
waitForSync?: () => Promise<void>;
}) {
export async function openHardwareTransferUnavailableSheet() {
await doNavigationClose();
await tap('ActivityHardware');
await elementById('HardwareWalletScreen').waitForDisplayed();
await tap('HardwareTransferToSpending');

const hasSpendingIntro = await elementById('SpendingIntro-button')
.isDisplayed()
.catch(() => false);
if (hasSpendingIntro) {
await tap('SpendingIntro-button');
await sleep(800);
}

await elementById('HardwareTransferAmount').waitForDisplayed({ timeout: 60_000 });
await elementById('HardwareTransferAmountContinue').waitForEnabled({ timeout: 60_000 });
await sleep(1000);
await enterAmount(amountSats);
await elementById('HardwareTransferAmountContinue').waitForEnabled({ timeout: 60_000 });
await tap('HardwareTransferAmountContinue');
await elementById('HardwareTransferSign').waitForDisplayed({ timeout: 120_000 });
await tap('HardwareTransferOpenTrezorConnect');
await approveTrezorPromptsUntil([
'HardwareTransferSigned',
'LightningSettingUp',
'TransferSuccess',
]);
await waitForHardwareTransferProgress();
if (getBackend() === 'local') {
// Local backend does not have Blocktank,
// so we don't wait for the transfer success screen.
await tap('TransferSuccess-button');
await dismissBackupTimedSheet({ triggerTimedSheet: false });
} else {
await mineBlocks(1);
if (waitForSync) {
await waitForSync();
}
for (let i = 0; i < 10; i++) {
try {
await elementById('TransferSuccess').waitForDisplayed({ timeout: 5_000 });
break;
} catch {
console.info('→ Hardware transfer success not found, mining another block...');
await mineBlocks(1);
if (waitForSync) {
await waitForSync();
}
}
}
await elementById('TransferSuccess').waitForDisplayed();
await tap('TransferSuccess-button');
await dismissSpendingBalanceToastIfShown();
await dismissBackupTimedSheet({ triggerTimedSheet: false });
await dismissBackgroundPaymentsTimedSheet({ triggerTimedSheet: true });
await dismissQuickPayIntro({ triggerTimedSheet: true });
}
await elementById('ChannelsUnavailableSheet').waitForDisplayed();
}

export async function removeHardwareWalletFromSettings(label: string) {
Expand All @@ -394,14 +333,6 @@ async function approveTrezorPromptsUntil(testIds: string[]) {
throw new Error(`Timed out waiting for Trezor approval (${testIds.join(', ')})`);
}

async function waitForHardwareTransferProgress() {
await browser.waitUntil(async () => isAnyDisplayed(['LightningSettingUp', 'TransferSuccess']), {
timeout: 60_000,
interval: 500,
timeoutMsg: 'Timed out waiting for hardware transfer progress',
});
}

function pressTrezorYes() {
try {
runTrezorEmulatorQuiet(['send-json', JSON.stringify({ type: 'emulator-press-yes' })]);
Expand Down Expand Up @@ -435,11 +366,3 @@ async function isDisplayed(testId: string): Promise<boolean> {
return false;
}
}

async function dismissSpendingBalanceToastIfShown() {
try {
await waitForToast('SpendingBalanceReadyToast', { timeout: 30_000 });
} catch {
console.info('→ SpendingBalanceReadyToast not shown after hardware transfer.');
}
}
37 changes: 36 additions & 1 deletion test/helpers/lnd.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import BitcoinJsonRpc from 'bitcoin-json-rpc';
import {
acknowledgeExternalSuccess,
confirmInputOnKeyboard,
doNavigationClose,
dragOnElement,
elementById,
expectTextWithin,
sleep,
swipeFullScreen,
tap,
tryDismissBackgroundPaymentsIfVisible,
tryDismissQuickPayIntroIfVisible,
typeText,
} from './actions';
import { LndConfig, lndHost } from './constants';
import { LndConfig, lndConfig, lndHost } from './constants';
import { getBitcoinRpc, mineBlocks } from './regtest';
import { openSettings } from './navigation';
import createLndRpc, { LnRpc, WalletUnlockerRpc } from '@radar/lnrpc';

Expand Down Expand Up @@ -131,3 +136,33 @@ export async function checkChannelStatus({ size = '100 000' } = {}) {
await doNavigationClose();
await sleep(500);
}

/**
* Opens a channel from savings to the local LND node through manual setup, the way the app funds spending
* now that it no longer buys Blocktank channels. Spends the whole savings balance (ExternalAmountMax).
*/
export async function openChannelToLndFromSavings({
waitForSync,
}: {
waitForSync?: () => Promise<void>;
} = {}) {
const { lnd, lndNodeID } = await setupLND(getBitcoinRpc(), lndConfig);
if (waitForSync) await waitForSync();

const ldkNodeId = await getLDKNodeID();
await connectToLND(lndNodeID, { navigationClose: false });
await waitForPeerConnection(lnd, ldkNodeId);

await tap('ExternalAmountMax');
await tap('ExternalAmountContinue');
await sleep(500);
await dragOnElement('GRAB', 'right', 0.95);
await sleep(1000);
await acknowledgeExternalSuccess();

await mineBlocks(6);
if (waitForSync) await waitForSync();
await waitForActiveChannel(lnd, ldkNodeId);
await tryDismissBackgroundPaymentsIfVisible();
await tryDismissQuickPayIntroIfVisible();
}
38 changes: 18 additions & 20 deletions test/specs/hardware-wallet.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
completeOnboarding,
doNavigationClose,
elementById,
expectSavingsBalance,
expectSpendingBalance,
expectTotalBalance,
receiveOnchainFunds,
tap,
} from '../helpers/actions';
import initElectrum from '../helpers/electrum';
import {
Expand All @@ -24,9 +26,9 @@ import {
sendOnchainFromHardwareWallet,
startHardwareWalletFlowFromSuggestion,
stopTrezorEmulator,
transferHardwareWalletToSpending,
openHardwareTransferUnavailableSheet,
} from '../helpers/hardware-wallet';
import { ensureLocalFunds, getBackend, getExternalAddress } from '../helpers/regtest';
import { ensureLocalFunds, getExternalAddress } from '../helpers/regtest';
import { reinstallApp } from '../helpers/setup';
import { ciIt } from '../helpers/suite';

Expand Down Expand Up @@ -90,26 +92,22 @@ describe('@hardware_wallet @ios_gate - Hardware Wallet', () => {
await expectSpendingBalance(0);
});

ciIt('@hardware_wallet_3 - Can transfer hardware wallet funds to spending', async () => {
const fundingSats = 100_000;
const transferSats = 20_000;
ciIt(
'@hardware_wallet_3 - Hardware wallet transfer to spending explains it is unavailable',
async () => {
const fundingSats = 100_000;

await connectHardwareWalletFromSettings(walletLabel);
await fundHardwareWalletAndAcknowledge({ sats: fundingSats });
await expectHardwareWalletBalance(fundingSats);
await transferHardwareWalletToSpending({
amountSats: transferSats,
waitForSync: async () => {
await electrum?.waitForSync();
},
});
await doNavigationClose();
await expectHardwareWalletBalance(fundingSats, { condition: 'lt' });
if (getBackend() === 'regtest') {
// We only check balance on staging regtest backend, local does not have Blocktank.
await expectSpendingBalance(0, { condition: 'gt', timeout: 60_000 });
await connectHardwareWalletFromSettings(walletLabel);
await fundHardwareWalletAndAcknowledge({ sats: fundingSats });
await expectHardwareWalletBalance(fundingSats);
await openHardwareTransferUnavailableSheet();
await tap('ChannelsUnavailableSheetCancel');
await elementById('ChannelsUnavailableSheet').waitForDisplayed({ reverse: true });
await elementById('HardwareWalletScreen').waitForDisplayed();
await doNavigationClose();
await expectHardwareWalletBalance(fundingSats);
}
});
);

ciIt('@hardware_wallet_4 - Can send onchain from hardware wallet', async () => {
const fundingSats = 100_000;
Expand Down
86 changes: 44 additions & 42 deletions test/specs/multiaddress.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
switchAndFundEachAddressType,
swipeFullScreen,
tap,
transferSavingsToSpending,
transferSpendingToSavings,
type addressTypePreference,
getSpendingBalance,
Expand All @@ -41,6 +40,7 @@ import {
checkChannelStatus,
connectToLND,
getLDKNodeID,
openChannelToLndFromSavings,
setupLND,
waitForPeerConnection,
} from '../helpers/lnd';
Expand Down Expand Up @@ -71,49 +71,52 @@ describe('@multi_address - Multi address', () => {
await electrum?.stop();
});

ciIt('@multi_address_1 @ios_nightly - Receive to each address type and send max combined', async () => {
const addressTypes: addressTypePreference[] = ['p2pkh', 'p2sh-p2wpkh', 'p2wpkh', 'p2tr'];
const satsPerAddressType = 100_000;
const { totalFundedSats } = await switchAndFundEachAddressType({
addressTypes,
satsPerAddressType,
waitForSync: async () => {
await electrum?.waitForSync();
},
});

const totalBalance = await getTotalBalance();
const savingsBalance = await getSavingsBalance();
const spendingBalance = await getSpendingBalance();
await expect(savingsBalance).toEqual(totalFundedSats);
await expect(spendingBalance).toEqual(0);
await expect(totalBalance).toEqual(totalFundedSats);

const coreAddress = await getExternalAddress();
await enterAddress(coreAddress);
await tap('AvailableAmount');
await tap('ContinueAmount');
await dragOnElement('GRAB', 'right', 0.95);
await handleOver50PercentAlert();
await elementById('SendSuccess').waitForDisplayed();
await tap('Close');
await mineBlocks(1);
await electrum?.waitForSync();
ciIt(
'@multi_address_1 @ios_nightly - Receive to each address type and send max combined',
async () => {
const addressTypes: addressTypePreference[] = ['p2pkh', 'p2sh-p2wpkh', 'p2wpkh', 'p2tr'];
const satsPerAddressType = 100_000;
const { totalFundedSats } = await switchAndFundEachAddressType({
addressTypes,
satsPerAddressType,
waitForSync: async () => {
await electrum?.waitForSync();
},
});

await expectTotalBalance(0);
await expectSavingsBalance(0);
await expectSpendingBalance(0);
const totalBalance = await getTotalBalance();
const savingsBalance = await getSavingsBalance();
const spendingBalance = await getSpendingBalance();
await expect(savingsBalance).toEqual(totalFundedSats);
await expect(spendingBalance).toEqual(0);
await expect(totalBalance).toEqual(totalFundedSats);

const totalBalanceAfter = await getTotalBalance();
const savingsBalanceAfter = await getSavingsBalance();
const spendingBalanceAfter = await getSpendingBalance();
await expect(totalBalanceAfter).toEqual(0);
await expect(savingsBalanceAfter).toEqual(0);
await expect(spendingBalanceAfter).toEqual(0);
});
const coreAddress = await getExternalAddress();
await enterAddress(coreAddress);
await tap('AvailableAmount');
await tap('ContinueAmount');
await dragOnElement('GRAB', 'right', 0.95);
await handleOver50PercentAlert();
await elementById('SendSuccess').waitForDisplayed();
await tap('Close');
await mineBlocks(1);
await electrum?.waitForSync();

await expectTotalBalance(0);
await expectSavingsBalance(0);
await expectSpendingBalance(0);

const totalBalanceAfter = await getTotalBalance();
const savingsBalanceAfter = await getSavingsBalance();
const spendingBalanceAfter = await getSpendingBalance();
await expect(totalBalanceAfter).toEqual(0);
await expect(savingsBalanceAfter).toEqual(0);
await expect(spendingBalanceAfter).toEqual(0);
}
);

ciIt(
'@multi_address_2, @multi_address_staging, @staging - Receive to each address type, transfer all to spending, close channel to taproot',
'@multi_address_2 - Receive to each address type, open a channel with all savings, close channel to taproot',
async () => {
const addressTypes: addressTypePreference[] = ['p2pkh', 'p2sh-p2wpkh', 'p2wpkh', 'p2tr'];
// const addressTypes: addressTypePreference[] = ['p2tr'];
Expand All @@ -135,13 +138,12 @@ describe('@multi_address - Multi address', () => {
await mineBlocks(1);
await electrum?.waitForSync();

await transferSavingsToSpending({
await openChannelToLndFromSavings({
waitForSync: async () => {
await electrum?.waitForSync();
},
});
await expectSpendingBalance(0, { condition: 'gt' });
await expectSavingsBalance(0);

if (driver.isAndroid) {
// pull to refresh due to:
Expand Down
10 changes: 5 additions & 5 deletions test/specs/settings.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,19 +458,19 @@ describe('@settings @ios_nightly - Settings', () => {
await swipeFullScreen('up');
await elementById('SuggestionsWidget').waitForDisplayed();

// hide lightningTodo suggestion card
await (await elementByIdWithin('Suggestion-lightning', 'SuggestionDismiss')).click();
await elementById('Suggestion-lightning').waitForDisplayed({ reverse: true });
// hide the hardware wallet suggestion card
await (await elementByIdWithin('Suggestion-hardware', 'SuggestionDismiss')).click();
await elementById('Suggestion-hardware').waitForDisplayed({ reverse: true });

// reset suggestions (now in Widgets settings)
await openSettings();
await tap('WidgetsSettings');
await tap('ResetSuggestions');
await tap('DialogConfirm');

// lightning should be visible again
// the hardware wallet card should be visible again
await sleep(1000);
await elementById('Suggestion-lightning').waitForDisplayed();
await elementById('Suggestion-hardware').waitForDisplayed();
});
});

Expand Down
Loading