diff --git a/test/helpers/setup.ts b/test/helpers/setup.ts index 911d3e1..77b53c0 100644 --- a/test/helpers/setup.ts +++ b/test/helpers/setup.ts @@ -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)['appium:udid']?.toString() ?? + (driver.capabilities as Record).udid?.toString() ?? + (driver.capabilities as Record).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(); @@ -23,6 +61,7 @@ export async function reinstallApp() { await driver.removeApp(appId); resetBootedIOSKeychain(); await driver.installApp(appPath); + grantIOSCameraPermission(appId); await driver.activateApp(appId); } @@ -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); } diff --git a/test/specs/mainnet/ln.e2e.ts b/test/specs/mainnet/ln.e2e.ts index aee7fe7..d170353 100644 --- a/test/specs/mainnet/ln.e2e.ts +++ b/test/specs/mainnet/ln.e2e.ts @@ -81,14 +81,18 @@ async function waitForPaymentResult(): Promise { 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}`); } @@ -100,7 +104,7 @@ async function waitForPaymentResult(): Promise { timeout: PAYMENT_TIMEOUT_MS, interval: 3_000, timeoutMsg: `Payment did not complete within ${PAYMENT_TIMEOUT_MS / 1000}s`, - }, + } ); } diff --git a/test/specs/migration.e2e.ts b/test/specs/migration.e2e.ts index 30692a0..96f5581 100644 --- a/test/specs/migration.e2e.ts +++ b/test/specs/migration.e2e.ts @@ -26,6 +26,7 @@ import { ciIt } from '../helpers/suite'; import { getNativeAppPath, getRnAppPath, + grantIOSCameraPermission, reinstallAppFromPath, resetBootedIOSKeychain, } from '../helpers/setup'; @@ -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) @@ -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 @@ -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 @@ -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 diff --git a/test/specs/settings.e2e.ts b/test/specs/settings.e2e.ts index 1fa2118..3a37bff 100644 --- a/test/specs/settings.e2e.ts +++ b/test/specs/settings.e2e.ts @@ -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(); diff --git a/tools/seedkit/README.md b/tools/seedkit/README.md index 83cf575..e03ee8d 100644 --- a/tools/seedkit/README.md +++ b/tools/seedkit/README.md @@ -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 @@ -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 diff --git a/tools/seedkit/docs/ARCHITECTURE.md b/tools/seedkit/docs/ARCHITECTURE.md index 83bb165..456d4bb 100644 --- a/tools/seedkit/docs/ARCHITECTURE.md +++ b/tools/seedkit/docs/ARCHITECTURE.md @@ -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) diff --git a/wdio.conf.ts b/wdio.conf.ts index 6ab7aec..8140efe 100644 --- a/wdio.conf.ts +++ b/wdio.conf.ts @@ -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'; @@ -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