Skip to content

Commit 13b89e6

Browse files
authored
Merge pull request #135 from synonymdev/feat/send-v59
Feat/send v59
2 parents 4e9a581 + ffd59e9 commit 13b89e6

11 files changed

Lines changed: 111 additions & 46 deletions

File tree

test/helpers/actions.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,15 @@ export async function typeText(testId: string, text: string) {
419419
await el.setValue(text);
420420
}
421421

422+
export async function addSendTag(tag: string) {
423+
if (driver.isIOS) {
424+
await tap('SendConfirmToggleDetails');
425+
}
426+
await tap('TagsAddSend');
427+
await typeText('TagInputSend', tag);
428+
await tap('SendTagsSubmit');
429+
}
430+
422431
export async function enterAmount(amountSats: number) {
423432
for (const digit of `${amountSats}`.split('')) {
424433
await tap(`N${digit}`);
@@ -1373,6 +1382,28 @@ export async function typeRecipientInput(
13731382
}
13741383
}
13751384

1385+
export async function editRecipientAddress(address: string) {
1386+
if (driver.isIOS) {
1387+
await tap('SendConfirmToggleDetails');
1388+
}
1389+
await tap('ReviewUri');
1390+
await sleep(2000);
1391+
await elementById('RecipientInput').waitForDisplayed();
1392+
await sleep(500);
1393+
try {
1394+
console.info('Typing on the RecipientInput...');
1395+
console.info({ address });
1396+
await typeRecipientInput(address);
1397+
await elementById('AddressContinue').waitForEnabled();
1398+
await sleep(500);
1399+
} catch {
1400+
console.warn('Typing on the RecipientInput failed, trying again...');
1401+
await typeRecipientInput(address);
1402+
await elementById('AddressContinue').waitForEnabled();
1403+
await sleep(500);
1404+
}
1405+
}
1406+
13761407
export async function typeAddressAndVerifyContinue({
13771408
address,
13781409
reverse = false,

test/helpers/setup.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,44 @@ import path from 'node:path';
44
import { sleep } from './actions';
55
import { getAppId, getAppPath } from './constants';
66

7+
function getIosSimulatorUdidForSimctl(): string {
8+
try {
9+
let udid =
10+
(driver.capabilities as Record<string, unknown>)['appium:udid']?.toString() ??
11+
(driver.capabilities as Record<string, unknown>).udid?.toString() ??
12+
(driver.capabilities as Record<string, unknown>).deviceUDID?.toString() ??
13+
process.env.SIMULATOR_UDID ??
14+
'';
15+
if (udid && udid !== 'auto') return udid;
16+
} catch {
17+
/* ignore */
18+
}
19+
try {
20+
const line = execSync('xcrun simctl list devices booted', { encoding: 'utf8' });
21+
const match = line.match(/\(([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})\)/i);
22+
if (match) return match[1] ?? '';
23+
} catch {
24+
/* ignore */
25+
}
26+
return '';
27+
}
28+
29+
export function grantIOSCameraPermission(appIdParam?: string) {
30+
if (typeof driver === 'undefined' || !driver.isIOS) return;
31+
const appId = appIdParam ?? getAppId();
32+
const udid = getIosSimulatorUdidForSimctl();
33+
if (!udid) {
34+
console.warn('⚠ grantIOSCameraPermission: could not resolve simulator UDID');
35+
return;
36+
}
37+
try {
38+
execSync(`xcrun simctl privacy "${udid}" grant camera "${appId}"`, { stdio: 'ignore' });
39+
console.info(`→ Granted iOS camera permission for '${appId}' (simulator ${udid})`);
40+
} catch (error) {
41+
console.warn('⚠ grantIOSCameraPermission failed', error);
42+
}
43+
}
44+
745
export async function launchFreshApp() {
846
const appId = getAppId();
947

@@ -23,6 +61,7 @@ export async function reinstallApp() {
2361
await driver.removeApp(appId);
2462
resetBootedIOSKeychain();
2563
await driver.installApp(appPath);
64+
grantIOSCameraPermission(appId);
2665
await driver.activateApp(appId);
2766
}
2867

@@ -52,6 +91,7 @@ export async function reinstallAppFromPath(appPath: string, appId: string = getA
5291
await driver.removeApp(appId);
5392
resetBootedIOSKeychain();
5493
await driver.installApp(appPath);
94+
grantIOSCameraPermission(appId);
5595
await driver.activateApp(appId);
5696
}
5797

test/specs/lightning.e2e.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
doNavigationClose,
2323
dismissBackgroundPaymentsTimedSheet,
2424
acknowledgeReceivedPayment,
25+
addSendTag,
2526
waitForBackup,
2627
waitForToast,
2728
} from '../helpers/actions';
@@ -174,9 +175,7 @@ describe('@lightning - Lightning', () => {
174175
await expect(reviewAmt).toHaveText('1 000');
175176
await console.info('I cannot edit the amount on Review screen');
176177
await tap('ReviewAmount-primary');
177-
await tap('TagsAddSend');
178-
await typeText('TagInputSend', 'stag');
179-
await tap('SendTagsSubmit');
178+
await addSendTag('stag');
180179
await sleep(500); // wait for keyboard to close
181180
await dragOnElement('GRAB', 'right', 0.95); // Swipe to confirm
182181
await elementById('SendSuccess').waitForDisplayed();

test/specs/mainnet/ln.e2e.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,18 @@ async function waitForPaymentResult(): Promise<void> {
8181
console.info(`→ [LN] Waiting for payment result (timeout: ${PAYMENT_TIMEOUT_MS / 1000}s)...`);
8282
await browser.waitUntil(
8383
async () => {
84-
const success = await elementById('SendSuccess').isDisplayed().catch(() => false);
84+
const success = await elementById('SendSuccess')
85+
.isDisplayed()
86+
.catch(() => false);
8587
if (success) {
8688
console.info('→ [LN] Payment succeeded');
8789
return true;
8890
}
8991

9092
for (const toastId of ERROR_TOASTS) {
91-
const visible = await elementById(toastId).isDisplayed().catch(() => false);
93+
const visible = await elementById(toastId)
94+
.isDisplayed()
95+
.catch(() => false);
9296
if (visible) {
9397
throw new Error(`Payment failed with error toast: ${toastId}`);
9498
}
@@ -100,7 +104,7 @@ async function waitForPaymentResult(): Promise<void> {
100104
timeout: PAYMENT_TIMEOUT_MS,
101105
interval: 3_000,
102106
timeoutMsg: `Payment did not complete within ${PAYMENT_TIMEOUT_MS / 1000}s`,
103-
},
107+
}
104108
);
105109
}
106110

test/specs/migration.e2e.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ciIt } from '../helpers/suite';
2626
import {
2727
getNativeAppPath,
2828
getRnAppPath,
29+
grantIOSCameraPermission,
2930
reinstallAppFromPath,
3031
resetBootedIOSKeychain,
3132
} from '../helpers/setup';
@@ -170,6 +171,7 @@ describe('@migration - Migration from legacy RN app to native app', () => {
170171
// Install native app
171172
console.info(`→ Installing native app from: ${getNativeAppPath()}`);
172173
await driver.installApp(getNativeAppPath());
174+
grantIOSCameraPermission();
173175
await driver.activateApp(getAppId());
174176

175177
// Restore wallet with mnemonic (uses custom flow to handle backup sheet)
@@ -194,6 +196,7 @@ describe('@migration - Migration from legacy RN app to native app', () => {
194196
// Install native app ON TOP of RN (upgrade)
195197
console.info(`→ Installing native app on top of RN: ${getNativeAppPath()}`);
196198
await driver.installApp(getNativeAppPath());
199+
grantIOSCameraPermission();
197200
await driver.activateApp(getAppId());
198201

199202
// Handle migration flow
@@ -213,6 +216,7 @@ describe('@migration - Migration from legacy RN app to native app', () => {
213216
// Install native app ON TOP of RN (upgrade)
214217
console.info(`→ Installing native app on top of RN: ${getNativeAppPath()}`);
215218
await driver.installApp(getNativeAppPath());
219+
grantIOSCameraPermission();
216220
await driver.activateApp(getAppId());
217221

218222
// Handle migration flow
@@ -234,6 +238,7 @@ describe('@migration - Migration from legacy RN app to native app', () => {
234238
// Install native app ON TOP of RN (upgrade)
235239
console.info(`→ Installing native app on top of RN: ${getNativeAppPath()}`);
236240
await driver.installApp(getNativeAppPath());
241+
grantIOSCameraPermission();
237242
await driver.activateApp(getAppId());
238243

239244
// Handle migration flow

test/specs/onchain.e2e.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
handleOver50PercentAlert,
2222
handleOver100Alert,
2323
acknowledgeReceivedPayment,
24+
addSendTag,
2425
enterAmount,
2526
formatSats,
2627
expectTotalBalance,
@@ -151,10 +152,7 @@ describe('@onchain - Onchain', () => {
151152
await tap('ContinueAmount');
152153

153154
// Review & Send
154-
await elementById('TagsAddSend').waitForDisplayed();
155-
await tap('TagsAddSend');
156-
await typeText('TagInputSend', 'stag');
157-
await elementByText('Add', 'exact').click();
155+
await addSendTag('stag');
158156
await dragOnElement('GRAB', 'right', 0.95);
159157

160158
await sleep(1000);

test/specs/send.e2e.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import {
2222
handleAndroidAlert,
2323
dismissBackgroundPaymentsTimedSheet,
2424
acknowledgeReceivedPayment,
25+
editRecipientAddress,
2526
typeRecipientInput,
27+
tap,
2628
enterAmount,
2729
verifyAmountToSend,
2830
} from '../helpers/actions';
2931
import { lndConfig } from '../helpers/constants';
3032
import { reinstallApp } from '../helpers/setup';
31-
import { confirmInputOnKeyboard, tap, typeText } from '../helpers/actions';
3233
import {
3334
connectToLND,
3435
getLDKNodeID,
@@ -309,24 +310,7 @@ describe('@send - Send', () => {
309310
const reviewAmt = await elementByIdWithin('ReviewAmount-primary', 'MoneyText');
310311
await reviewAmt.waitForDisplayed();
311312
await expect(reviewAmt).toHaveText('2 000');
312-
await tap('ReviewUri');
313-
await sleep(2000);
314-
await elementById('RecipientInput').waitForDisplayed();
315-
await sleep(500);
316-
try {
317-
console.info('Typing on the RecipientInput...');
318-
console.info({ onchainAddress });
319-
await typeText('RecipientInput', onchainAddress);
320-
await confirmInputOnKeyboard();
321-
await elementById('AddressContinue').waitForEnabled();
322-
await sleep(500);
323-
} catch {
324-
console.warn('Typing on the RecipientInput failed, trying again...');
325-
await typeText('RecipientInput', onchainAddress);
326-
await confirmInputOnKeyboard();
327-
await elementById('AddressContinue').waitForEnabled();
328-
await sleep(500);
329-
}
313+
await editRecipientAddress(onchainAddress);
330314
await tap('AddressContinue');
331315
await elementById('AssetButton-savings').waitForDisplayed();
332316
await tap('N2');

test/specs/settings.e2e.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@ describe('@settings - Settings', () => {
490490
});
491491

492492
ciIt('@settings_12 - Can reset suggestions', async () => {
493-
494493
await elementById('TotalBalance-primary').waitForDisplayed();
495494
await swipeFullScreen('up');
496495
await elementById('SuggestionsWidget').waitForDisplayed();

tools/seedkit/README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ seedkit preview --backend staging
5353

5454
## Scenarios
5555

56-
| Scenario | Description |
57-
|----------|-------------|
58-
| `first-time` | Clean wallet with one confirmed receive (50,000 sat) |
56+
| Scenario | Description |
57+
| ------------ | ----------------------------------------------------------- |
58+
| `first-time` | Clean wallet with one confirmed receive (50,000 sat) |
5959
| `fragmented` | 18 small UTXOs (2,000-9,100 sat) for coin selection testing |
60-
| `dust` | Tiny UTXOs at spendability edge cases (330-1,000 sat) |
61-
| `merchant` | 12 inbound payments across multiple blocks |
62-
| `savings` | Single large UTXO (1,000,000 sat) |
60+
| `dust` | Tiny UTXOs at spendability edge cases (330-1,000 sat) |
61+
| `merchant` | 12 inbound payments across multiple blocks |
62+
| `savings` | Single large UTXO (1,000,000 sat) |
6363

6464
## Backends
6565

@@ -86,9 +86,7 @@ When used with `--output json`, the `run` command outputs structured JSON for pr
8686
{
8787
"scenario": "first-time",
8888
"mnemonic": "word1 word2 ...",
89-
"addresses": [
90-
{"index": 0, "address": "bcrt1q...", "amountSat": 50000, "confirmed": true}
91-
],
89+
"addresses": [{ "index": 0, "address": "bcrt1q...", "amountSat": 50000, "confirmed": true }],
9290
"totalSat": 50000,
9391
"utxoCount": 1,
9492
"blocksMined": 1

tools/seedkit/docs/ARCHITECTURE.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ Connection details from existing infra:
4242

4343
These scenarios only need deposit + mine, no outgoing tx signing:
4444

45-
| Scenario | What it creates | Addresses | Deposits |
46-
|----------|----------------|-----------|----------|
47-
| **first-time** | Clean wallet, one confirmed receive | 1 | 1 x 50,000 sat, mine 1 block |
48-
| **fragmented** | Many small UTXOs for coin selection testing | 18 | 18 x 2,000-9,100 sat each, mine |
49-
| **dust** | Tiny UTXOs at spendability edge | 5 | Mix of 330, 546, 600, 800, 1000 sat, mine |
50-
| **merchant** | Many inbound payments, rich history | 12 | 12 varied amounts (2k-85k sat), mined across multiple blocks |
51-
| **savings** | Large single UTXO, simple balance | 1 | 1 x 1,000,000 sat, mine |
45+
| Scenario | What it creates | Addresses | Deposits |
46+
| -------------- | ------------------------------------------- | --------- | ------------------------------------------------------------ |
47+
| **first-time** | Clean wallet, one confirmed receive | 1 | 1 x 50,000 sat, mine 1 block |
48+
| **fragmented** | Many small UTXOs for coin selection testing | 18 | 18 x 2,000-9,100 sat each, mine |
49+
| **dust** | Tiny UTXOs at spendability edge | 5 | Mix of 330, 546, 600, 800, 1000 sat, mine |
50+
| **merchant** | Many inbound payments, rich history | 12 | 12 varied amounts (2k-85k sat), mined across multiple blocks |
51+
| **savings** | Large single UTXO, simple balance | 1 | 1 x 1,000,000 sat, mine |
5252

5353
### Future scenarios (require transaction building - Phase 2)
5454

0 commit comments

Comments
 (0)