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
40 changes: 40 additions & 0 deletions test/helpers/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,44 @@ import path from 'node:path';
import { sleep } from './actions';
import { getAppId, getAppPath } from './constants';

function getIosSimulatorUdidForSimctl(): string {
try {
let udid =
(driver.capabilities as Record<string, unknown>)['appium:udid']?.toString() ??
(driver.capabilities as Record<string, unknown>).udid?.toString() ??
(driver.capabilities as Record<string, unknown>).deviceUDID?.toString() ??
process.env.SIMULATOR_UDID ??
'';
if (udid && udid !== 'auto') return udid;
} catch {
/* ignore */
}
try {
const line = execSync('xcrun simctl list devices booted', { encoding: 'utf8' });
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);
if (match) return match[1] ?? '';
} catch {
/* ignore */
}
return '';
}

export function grantIOSCameraPermission(appIdParam?: string) {
if (typeof driver === 'undefined' || !driver.isIOS) return;
const appId = appIdParam ?? getAppId();
const udid = getIosSimulatorUdidForSimctl();
if (!udid) {
console.warn('⚠ grantIOSCameraPermission: could not resolve simulator UDID');
return;
}
try {
execSync(`xcrun simctl privacy "${udid}" grant camera "${appId}"`, { stdio: 'ignore' });
console.info(`→ Granted iOS camera permission for '${appId}' (simulator ${udid})`);
} catch (error) {
console.warn('⚠ grantIOSCameraPermission failed', error);
}
}

export async function launchFreshApp() {
const appId = getAppId();

Expand All @@ -23,6 +61,7 @@ export async function reinstallApp() {
await driver.removeApp(appId);
resetBootedIOSKeychain();
await driver.installApp(appPath);
grantIOSCameraPermission(appId);
await driver.activateApp(appId);
}

Expand Down Expand Up @@ -52,6 +91,7 @@ export async function reinstallAppFromPath(appPath: string, appId: string = getA
await driver.removeApp(appId);
resetBootedIOSKeychain();
await driver.installApp(appPath);
grantIOSCameraPermission(appId);
await driver.activateApp(appId);
}

Expand Down
10 changes: 7 additions & 3 deletions test/specs/mainnet/ln.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,18 @@ async function waitForPaymentResult(): Promise<void> {
console.info(`→ [LN] Waiting for payment result (timeout: ${PAYMENT_TIMEOUT_MS / 1000}s)...`);
await browser.waitUntil(
async () => {
const success = await elementById('SendSuccess').isDisplayed().catch(() => false);
const success = await elementById('SendSuccess')
.isDisplayed()
.catch(() => false);
if (success) {
console.info('→ [LN] Payment succeeded');
return true;
}

for (const toastId of ERROR_TOASTS) {
const visible = await elementById(toastId).isDisplayed().catch(() => false);
const visible = await elementById(toastId)
.isDisplayed()
.catch(() => false);
if (visible) {
throw new Error(`Payment failed with error toast: ${toastId}`);
}
Expand All @@ -100,7 +104,7 @@ async function waitForPaymentResult(): Promise<void> {
timeout: PAYMENT_TIMEOUT_MS,
interval: 3_000,
timeoutMsg: `Payment did not complete within ${PAYMENT_TIMEOUT_MS / 1000}s`,
},
}
);
}

Expand Down
5 changes: 5 additions & 0 deletions test/specs/migration.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ciIt } from '../helpers/suite';
import {
getNativeAppPath,
getRnAppPath,
grantIOSCameraPermission,
reinstallAppFromPath,
resetBootedIOSKeychain,
} from '../helpers/setup';
Expand Down Expand Up @@ -170,6 +171,7 @@ describe('@migration - Migration from legacy RN app to native app', () => {
// Install native app
console.info(`→ Installing native app from: ${getNativeAppPath()}`);
await driver.installApp(getNativeAppPath());
grantIOSCameraPermission();
await driver.activateApp(getAppId());

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

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

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

// Handle migration flow
Expand Down
1 change: 0 additions & 1 deletion test/specs/settings.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,6 @@ describe('@settings - Settings', () => {
});

ciIt('@settings_12 - Can reset suggestions', async () => {

await elementById('TotalBalance-primary').waitForDisplayed();
await swipeFullScreen('up');
await elementById('SuggestionsWidget').waitForDisplayed();
Expand Down
16 changes: 7 additions & 9 deletions tools/seedkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ seedkit preview --backend staging

## Scenarios

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

## Backends

Expand All @@ -86,9 +86,7 @@ When used with `--output json`, the `run` command outputs structured JSON for pr
{
"scenario": "first-time",
"mnemonic": "word1 word2 ...",
"addresses": [
{"index": 0, "address": "bcrt1q...", "amountSat": 50000, "confirmed": true}
],
"addresses": [{ "index": 0, "address": "bcrt1q...", "amountSat": 50000, "confirmed": true }],
"totalSat": 50000,
"utxoCount": 1,
"blocksMined": 1
Expand Down
14 changes: 7 additions & 7 deletions tools/seedkit/docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ Connection details from existing infra:

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

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

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

Expand Down
7 changes: 7 additions & 0 deletions wdio.conf.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import path from 'node:path';
import fs from 'node:fs';

import { grantIOSCameraPermission } from './test/helpers/setup';

const isAndroid = process.env.PLATFORM === 'android';
const iosDeviceName = process.env.SIMULATOR_NAME || 'iPhone 17';
const iosPlatformVersion = process.env.SIMULATOR_OS_VERSION || '26.0.1';
Expand Down Expand Up @@ -334,6 +336,11 @@ export const config: WebdriverIO.Config = {
*/
// afterAssertion: function(params) {
// }
before: async function () {
if (!isAndroid) {
grantIOSCameraPermission();
}
},
beforeTest: async function (test) {
if (process.env.RECORD_VIDEO === 'true') {
const recordingOptions = isAndroid
Expand Down