|
| 1 | +# E2E Test Failures on `feat/loading-status-messages` |
| 2 | + |
| 3 | +## Root Cause |
| 4 | + |
| 5 | +The branch changed `shouldHoldOnLoading` in `App.tsx:223` from: |
| 6 | + |
| 7 | +```js |
| 8 | +// master: |
| 9 | +const shouldHoldOnLoading = hasStoredWallet && !initialized && authState !== 'locked' |
| 10 | +// branch: |
| 11 | +const shouldHoldOnLoading = hasStoredWallet && (!initialized || !dataReady) && authState !== 'locked' |
| 12 | +``` |
| 13 | + |
| 14 | +This keeps the loading screen visible (and `comp = null` — no page content rendered) until `reloadWallet()` completes and sets `dataReady = true`. On master, the wallet page renders immediately after `initialized` becomes true. |
| 15 | + |
| 16 | +Two consequences for tests: |
| 17 | + |
| 18 | +1. **Wallet readiness takes longer** — `createWallet()` and `restoreWallet()` waited for `text=Send` with a 30s timeout. On this branch, `Send` only appears after `reloadWallet` finishes (extra seconds). If `reloadWallet` fails, a `BootError` overlay ("Continue anyway" / "Retry") blocks the UI instead. |
| 19 | + |
| 20 | +2. **`reloadWallet` failure on first load blocks the app** — On master, failures were silently ignored. On this branch, `loadError` is set and `BootError` renders as a full-screen overlay. Tests don't handle this. |
| 21 | + |
| 22 | +## Fixes Applied |
| 23 | + |
| 24 | +### 1. `waitForWalletPage` helper (`src/test/e2e/utils.ts`) |
| 25 | + |
| 26 | +Handles both the normal path (`Send` visible) and the error path (`Continue anyway` visible): |
| 27 | + |
| 28 | +```ts |
| 29 | +export async function waitForWalletPage(page: Page, timeout = 60000): Promise<void> { |
| 30 | + const sendBtn = page.getByText('Send', { exact: true }) |
| 31 | + const continueBtn = page.getByText('Continue anyway') |
| 32 | + await sendBtn.or(continueBtn).first().waitFor({ state: 'visible', timeout }) |
| 33 | + if (await continueBtn.isVisible()) { |
| 34 | + await continueBtn.click() |
| 35 | + await sendBtn.waitFor({ state: 'visible', timeout: 30000 }) |
| 36 | + } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +Used in `createWallet()`, `restoreWallet()`, and `delegate.test.ts` (post-reload wait). |
| 41 | + |
| 42 | +### 2. `waitForPaymentReceived` timeout (`src/test/e2e/utils.ts`) |
| 43 | + |
| 44 | +Increased from default 30s to 60s. The Boltz reverse-swap claim path can exceed 30s on slow CI runners. |
| 45 | + |
| 46 | +## Remaining Issue: `restore.test.ts` Lightning payment not detected |
| 47 | + |
| 48 | +### Symptom |
| 49 | + |
| 50 | +`waitForPaymentReceived` times out even at 60s. The page snapshot shows the QrCode/Receive page is correctly displaying the Lightning invoice, but "Payment received!" never appears. |
| 51 | + |
| 52 | +### Key finding: `exec` vs `execSync` |
| 53 | + |
| 54 | +The test uses fire-and-forget `exec()` to pay the Lightning invoice via lnd: |
| 55 | +```js |
| 56 | +exec(`docker exec lnd lncli --network=regtest payinvoice ${invoice} --force`) |
| 57 | +``` |
| 58 | + |
| 59 | +- **`execSync`** (blocking): payment works instantly, test passes |
| 60 | +- **`exec`** (fire-and-forget): payment never completes, test times out |
| 61 | + |
| 62 | +On master, fire-and-forget `exec` works fine. On this branch, it does not. |
| 63 | + |
| 64 | +### Cascading failure |
| 65 | + |
| 66 | +Each failed attempt leaves an IN_FLIGHT HTLC in lnd that never settles. After several failed runs, lnd accumulates stuck HTLCs that block all new payments on the channel. This makes subsequent runs fail even more reliably. |
| 67 | + |
| 68 | +### Theories investigated but not confirmed |
| 69 | + |
| 70 | +- **Navigation effect re-firing**: The `wallet.tsx:256-278` effect navigates to `Pages.Wallet` when `initialized && dataReady`. If `initialized` flickers (status ping returning false temporarily), the effect could re-fire and bounce the user off the QrCode page, unmounting the payment listener. However, the same effect exists on master. |
| 71 | + |
| 72 | +- **QrCode component unmount**: If `shouldHoldOnLoading` becomes true mid-test (e.g., `initialized` flickers), `comp = null` destroys all page content including the QrCode component and its `listenForPayments` handler. The swap's `waitAndClaim` promise and the VTXO_UPDATE listener are lost. This is unique to the branch since master's `shouldHoldOnLoading` is less likely to re-trigger. |
| 73 | + |
| 74 | +- **`exec` child process blocking on stdout pipe**: Unlikely given the small output size of `payinvoice`. |
| 75 | + |
| 76 | +### Suggested next step |
| 77 | + |
| 78 | +Change the `exec` call at `restore.test.ts:44` (and similar in `swap.test.ts:40`, `nostr.test.ts:84`) to use `execAsync` so the payment is confirmed before waiting for wallet detection. The test already imports `execAsync = promisify(exec)`. |
| 79 | + |
| 80 | +Alternatively, investigate why `exec` (fire-and-forget) works on master but not on this branch — likely related to the `initialized` state flickering causing component unmounts via `shouldHoldOnLoading`. |
0 commit comments