add lnurl tests#564
Conversation
Co-authored-by: Copilot <[email protected]>
Deploying wallet-mutinynet with
|
| Latest commit: |
8efdc0f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d5d220c4.arkade-wallet.pages.dev |
| Branch Preview URL: | https://lnurl-tests.arkade-wallet.pages.dev |
WalkthroughThis change adds LNURL server support to the Playwright configuration, improves logging in the LNURL session hook, and introduces a comprehensive end-to-end test suite covering LNURL flows including condition discovery, invoice fetching, and regtest payment validation. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying tmp-boltz-upstream-mainnet-arkade-wallet with
|
| Latest commit: |
8efdc0f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a3fab531.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev |
| Branch Preview URL: | https://lnurl-tests.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev |
There was a problem hiding this comment.
✅ Approved — LNURL e2e tests
Clean PR. Tests are well-structured and follow the existing test patterns (receive.test.ts, send.test.ts). Not protocol-critical — no changes to VTXOs, signing, forfeit, rounds, or exits.
What's here
- 4 new e2e tests covering the LNURL receive flow: LNURL creation, condition checking, invoice fetching, and full payment roundtrip
playwright.config.ts: addsVITE_LNURL_SERVER_URL=http://localhost:9090to the dev server envuseLnurlSession.ts:146: minor logging fix — logs the coercedamountMsat.toString()instead of rawdata.amountMsat. Correct change since the validation operates on theNumber()-coerced value
Minor findings (non-blocking)
-
Magic number
1,992—src/test/e2e/lnurl.test.ts:115: The expected received amount1,992(from 2,000 sent) encodes an implicit 8-sat fee assumption. If the fee structure changes, this test will break silently with a confusing error. Consider extracting a constant or adding a comment explaining the expected fee deduction. -
Duplicate setup — Tests 1-4 repeat the same wallet-creation + LNURL-copy boilerplate (~10 lines each). Could extract a
getLnurl(page)helper intoutils.tsto keep tests focused on what they're actually testing. Non-blocking since the existing tests (receive.test.ts) follow a similar pattern. -
No negative cases — No tests for edge cases like amount=0, amount exceeding
maxSendable, or malformed LNURLs. Worth adding in a follow-up to exercise the error paths inuseLnurlSession.tsandlnurl.ts. -
Shell interpolation in tests —
lnurl.test.ts:108:execAsync(\docker exec lnd lncli ... ${invoice} --force`)interpolates the invoice directly into a shell command. Same pattern asreceive.test.ts:26so it's consistent with the codebase, but worth noting for future hardening (e.g., passing via--or usingexecFile` to avoid accidental metacharacter issues).
LGTM. Ship it.
🤖 Reviewed by Arkana
There was a problem hiding this comment.
✅ Approved — LNURL e2e tests
Clean PR. Tests are well-structured and follow the existing test patterns (receive.test.ts, send.test.ts). Not protocol-critical — no changes to VTXOs, signing, forfeit, rounds, or exits.
What's here
- 4 new e2e tests covering the LNURL receive flow: LNURL creation, condition checking, invoice fetching, and full payment roundtrip
playwright.config.ts: addsVITE_LNURL_SERVER_URL=http://localhost:9090to the dev server envuseLnurlSession.ts:146: minor logging fix — logs the coercedamountMsat.toString()instead of rawdata.amountMsat. Correct change since the validation operates on theNumber()-coerced value
Minor findings (non-blocking)
-
Magic number
1,992—src/test/e2e/lnurl.test.ts:115: The expected received amount1,992(from 2,000 sent) encodes an implicit 8-sat fee assumption. If the fee structure changes, this test will break silently with a confusing error. Consider extracting a constant or adding a comment explaining the expected fee deduction. -
Duplicate setup — Tests 1-4 repeat the same wallet-creation + LNURL-copy boilerplate (~10 lines each). Could extract a
getLnurl(page)helper intoutils.tsto keep tests focused on what they're actually testing. Non-blocking since the existing tests (receive.test.ts) follow a similar pattern. -
No negative cases — No tests for edge cases like amount=0, amount exceeding
maxSendable, or malformed LNURLs. Worth adding in a follow-up to exercise the error paths inuseLnurlSession.tsandlnurl.ts. -
Shell interpolation in tests —
lnurl.test.ts:108:execAsync(`docker exec lnd lncli ... ${invoice} --force`)interpolates the invoice directly into a shell command. Same pattern asreceive.test.ts:26so it's consistent with the codebase, but worth noting for future hardening (e.g., passing via--or usingexecFileto avoid accidental metacharacter issues).
LGTM. Ship it.
🤖 Reviewed by Arkana
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/useLnurlSession.ts (1)
144-150:⚠️ Potential issue | 🟡 MinorLog the raw payload value, not the coerced number.
amountMsathere is the already-coercedNumber(data.amountMsat), so when the payload is missing/invalid this logs"NaN"or"0"rather than what the server actually sent. Loggingdata.amountMsat(viaString(...), as the summary describes) is more useful for debugging malformed payloads.🔧 Suggested change
- consoleError('Invalid amountMsat in invoice request:', amountMsat.toString()) + consoleError('Invalid amountMsat in invoice request:', String(data.amountMsat))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useLnurlSession.ts` around lines 144 - 150, The log currently prints the coerced Number amountMsat which becomes "NaN" or "0" for invalid payloads; change the consoleError call in useLnurlSession's invoice handling to log the raw payload value instead by converting data.amountMsat to a string (e.g. String(data.amountMsat)) so you see exactly what the server sent; keep the rest of the flow the same (postError(sessionId, 'Invalid amount', abort.signal), clearing eventType and continue) and only adjust the consoleError argument.
🧹 Nitpick comments (3)
src/test/e2e/lnurl.test.ts (3)
106-106: Hard dependency on a runninglnddocker container — document and/or guard.
docker exec lnd lncli ...will fail with a confusing error if the container isn't running or is named differently in a contributor's environment. Consider:
- Documenting the required regtest setup (container name
lnd, networkregtest) in the test file header or a CONTRIBUTING note.- Optionally
test.skip-ing this case when the container is unavailable (e.g. a quickdocker pspre-check) so local runs of the other three tests don't fail spuriously.Also worth noting: the
invoicestring is interpolated directly into a shell command. It's bech32 so practically safe, but wrapping it in quotes ("${invoice}") is a cheap hardening step.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/e2e/lnurl.test.ts` at line 106, The e2e test in src/test/e2e/lnurl.test.ts has a hard dependency on a running Docker container named "lnd" and interpolates invoice directly into a shell command; update the test to (1) document the requirement (container name "lnd", network "regtest") in the file header or CONTRIBUTING, and (2) guard the failing call by adding a pre-check using docker ps (or docker inspect) to verify the "lnd" container is running and, if not, call test.skip for this specific test so other tests can run; also harden the execInvoke by wrapping the invoice variable in quotes when calling execAsync (the invocation using execAsync(`docker exec lnd lncli --network=regtest payinvoice ${invoice} --force`)).
9-115: Extract the shared setup into a helper/beforeEach.All four tests repeat the same ~10 lines:
createWallet, navigate to Receive, click Copy +lnurl-address-copy, read clipboard, then the same threeexpects on the LNURL. Consider extracting agetLnurlFromReceivePage(page)helper (or abeforeEachthat exposes the lnurl via a fixture) so the tests only contain the behavior unique to each case.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/e2e/lnurl.test.ts` around lines 9 - 115, The tests duplicate the same setup steps to create a wallet and extract/validate the LNURL; factor these into a helper (e.g., getLnurlFromReceivePage(page)) or a beforeEach that returns the lnurl so each test only asserts unique behavior; implement getLnurlFromReceivePage to call createWallet, click the wallet tab (getByTestId('tab-wallet')), click 'Receive' (getByText('Receive')), click 'Copy' and the lnurl copy button (getByTestId('lnurl-address-copy')), readClipboard(), and perform the three common expects (isValidLnUrl, contains 'LNURL', length > 100) before returning the lnurl, then replace the repeated block in each test with a single call to that helper (or use beforeEach to set a fixture variable).
18-19: Use a more specific locator thangetByText('Copy').The test uses
await page.getByText('Copy').click()withoutexact: true, which uses substring matching and could match both the "Copy" button and the "Copy address" modal header if timing causes both to be visible. Add adata-testidattribute (e.g.,lnurl-copy-trigger) to the Copy button for a stable, unambiguous selector.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/e2e/lnurl.test.ts` around lines 18 - 19, The test is using a fuzzy text locator await page.getByText('Copy').click() which can match multiple elements; add a stable data-testid like lnurl-copy-trigger to the actual Copy button in the UI and change the test to use that specific locator (replace the getByText call with getByTestId('lnurl-copy-trigger').click) so it reliably targets the Copy button before the existing await page.getByTestId('lnurl-address-copy').click() call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/test/e2e/lnurl.test.ts`:
- Line 114: The assertion uses a brittle exact string match via
expect(page.getByText('1,992', { exact: true })).toBeVisible() which will break
if the fee schedule changes; update the test to either (a) use a regex/text
match like expect(page.getByText(/1,9\d{2}/)).toBeVisible() or (b) read the
element text (via page.getByText or page.locator(...).innerText()) and compare
numerically against the known fee constant (e.g.,
parseInt(received.replace(/[,]/g, '')) <= MAX_AMOUNT && > MIN_AMOUNT or compute
expected = BASE - FEE_CONST and assert equality), referencing the page.getByText
call and the surrounding test in lnurl.test.ts to find where to replace the
exact match.
---
Outside diff comments:
In `@src/hooks/useLnurlSession.ts`:
- Around line 144-150: The log currently prints the coerced Number amountMsat
which becomes "NaN" or "0" for invalid payloads; change the consoleError call in
useLnurlSession's invoice handling to log the raw payload value instead by
converting data.amountMsat to a string (e.g. String(data.amountMsat)) so you see
exactly what the server sent; keep the rest of the flow the same
(postError(sessionId, 'Invalid amount', abort.signal), clearing eventType and
continue) and only adjust the consoleError argument.
---
Nitpick comments:
In `@src/test/e2e/lnurl.test.ts`:
- Line 106: The e2e test in src/test/e2e/lnurl.test.ts has a hard dependency on
a running Docker container named "lnd" and interpolates invoice directly into a
shell command; update the test to (1) document the requirement (container name
"lnd", network "regtest") in the file header or CONTRIBUTING, and (2) guard the
failing call by adding a pre-check using docker ps (or docker inspect) to verify
the "lnd" container is running and, if not, call test.skip for this specific
test so other tests can run; also harden the execInvoke by wrapping the invoice
variable in quotes when calling execAsync (the invocation using
execAsync(`docker exec lnd lncli --network=regtest payinvoice ${invoice}
--force`)).
- Around line 9-115: The tests duplicate the same setup steps to create a wallet
and extract/validate the LNURL; factor these into a helper (e.g.,
getLnurlFromReceivePage(page)) or a beforeEach that returns the lnurl so each
test only asserts unique behavior; implement getLnurlFromReceivePage to call
createWallet, click the wallet tab (getByTestId('tab-wallet')), click 'Receive'
(getByText('Receive')), click 'Copy' and the lnurl copy button
(getByTestId('lnurl-address-copy')), readClipboard(), and perform the three
common expects (isValidLnUrl, contains 'LNURL', length > 100) before returning
the lnurl, then replace the repeated block in each test with a single call to
that helper (or use beforeEach to set a fixture variable).
- Around line 18-19: The test is using a fuzzy text locator await
page.getByText('Copy').click() which can match multiple elements; add a stable
data-testid like lnurl-copy-trigger to the actual Copy button in the UI and
change the test to use that specific locator (replace the getByText call with
getByTestId('lnurl-copy-trigger').click) so it reliably targets the Copy button
before the existing await page.getByTestId('lnurl-address-copy').click() call.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 95958233-5e88-43fe-b232-c56e6d508c88
📒 Files selected for processing (3)
playwright.config.tssrc/hooks/useLnurlSession.tssrc/test/e2e/lnurl.test.ts
| // transaction should be visible on main page | ||
| await page.getByTestId('tab-wallet').click() | ||
| await page.waitForSelector('text=Received', { timeout: 10000 }) | ||
| await expect(page.getByText('1,992', { exact: true })).toBeVisible() |
There was a problem hiding this comment.
Brittle exact-amount assertion.
Asserting '1,992' hard-codes the current swap/service fee (2000 − 8). Any fee schedule change will break this test with a confusing "text not visible" failure. Consider asserting a range (e.g. toMatch(/1,9\d{2}/) or reading the element and checking received <= 2000 && received > 1900) or deriving the expected amount from the known fee constant.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/test/e2e/lnurl.test.ts` at line 114, The assertion uses a brittle exact
string match via expect(page.getByText('1,992', { exact: true })).toBeVisible()
which will break if the fee schedule changes; update the test to either (a) use
a regex/text match like expect(page.getByText(/1,9\d{2}/)).toBeVisible() or (b)
read the element text (via page.getByText or page.locator(...).innerText()) and
compare numerically against the known fee constant (e.g.,
parseInt(received.replace(/[,]/g, '')) <= MAX_AMOUNT && > MIN_AMOUNT or compute
expected = BASE - FEE_CONST and assert equality), referencing the page.getByText
call and the surrounding test in lnurl.test.ts to find where to replace the
exact match.
* lint * add lnurl tests Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
* remove ionic/normalize and ion-button * rebbit fixes * feat(wallet): prefix fiat amounts with currency symbols (#535) Amounts previously rendered with a trailing ISO code (`100.00 USD`, `50.00 EUR`) now use the common Unicode currency symbol as a prefix: `$100.00`, `€50.00`, `£25.00`, `¥1,000`. Currencies without a widely-used single-char symbol (CHF, CNY) keep the trailing-code form, so CNY does not collide with JPY's `¥`. The new formatters live alongside the existing generic ones: - `prettyFiatAmount(amount, currency)` and `prettyFiatHide(value, currency)` in `src/lib/format.ts` take the `Fiats` enum as a typed parameter. - The generic `prettyAmount` / `prettyHide` are intentionally left untouched so asset tickers (e.g. a stablecoin named `USD`) that flow through those helpers keep their trailing-code format. - Decimals are derived from the currency via `fiatDecimalsFor` in `src/lib/fiat.ts` (JPY = 0, others = 2) — shared between `FiatContext` and `prettyFiatAmount` so call sites no longer need to pass `fiatDecimals()`. Touched displays: hero balance (`Balance.tsx`), transaction list rows (`TransactionsList.tsx`), amount picker (`Keyboard.tsx`), transaction details (`Details.tsx`), Send/Receive/Notes success screens, and Send form available caption. Amount input labels (`InputAmount.tsx`) also use the symbol when available. Side fix: `Details.tsx` previously called `prettyAmount` without passing `fiatDecimals()`, hardcoding 2 decimals. JPY now renders as `¥174` / `¥0` in the transaction details instead of `174.00 JPY` / `0.00 JPY`. Hero alignment: the eye-toggle button wrapper now omits an empty unit `<Text>` when the symbol is inlined into the balance, and the button sits inside a `alignItems: center` flex container next to the unit text — keeping the icon centered with the smaller ISO code in the CHF case and snug against the balance in the `$7.30` case. Tests: - New coverage for `prettyFiatAmount` and `prettyFiatHide` in `src/test/lib/format.test.ts` (symbol + trailing-code + JPY 0-decimals). - Existing `prettyAmount` / `prettyHide` tests unchanged — the generic helpers still behave the same. - UI assertions updated to match the new rendered text: `src/test/screens/wallet/index.test.tsx` → `$0.00` `src/test/e2e/init.test.ts` → `$0.00` `src/test/e2e/keyboard.test.ts` → symbol-prefix regex Co-authored-by: Sahil Chaturvedi <[email protected]> Co-authored-by: João Bordalo <[email protected]> * Upgrade ts-sdk 0.4.16 - boltz-swap 0.3.17 (#539) * reverse logs in csv export (#546) * reverse logs in csv export * fix * avoid mutating the React state array * Extra tests (#548) * close copy sheet after copy * lint * test swaps in receive * Prevent swap after receival (#547) * Prevent swap after receival * fix * more removals * increase viewport size in tests * more removals * more removals * fixes * fixes * fixes * fix * remove ion-refresher * Merge branch 'master' into remove_ion-button * fix swap page (#549) * fix swap page * lint * add tests * Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551) * fix(receive): add spacing between button row and share button (#552) The vertical gap between the Add amount/Copy buttons and the Share button was too tight. Changed FlexCol gap from 0 to 0.75rem. Co-authored-by: Sahil Chaturvedi <[email protected]> * Revert "Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)" (#553) This reverts commit 1752cc8. * Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551) * Revert "Upgrade ts-sdk 0.4.17 - boltz-swap 0.3.18 (#551)" (#553) This reverts commit 1752cc8. * Upgrade ts-sdk 0.4.18 - boltz-swap 0.3.19 (#554) * add git commit to chatwoot custom attributes (#563) * add lnurl tests (#564) * lint * add lnurl tests Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]> * fix(ui): top-align tx history rows when assets are present (#537) * fix(ui): top-align tx history rows when assets are present When a transaction has assets, the right column (sats + fiat + asset info) is taller than the left column (icon + label + date). The outer FlexRow used alignItems 'center' by default, pushing the sats amount above the label text. Switch to alignItems 'start' so both columns begin at the same vertical position. Also adds explicit 'between' to the outer FlexRow and removes a dead alignItems on the non-flex wrapper div. * show only 2 coins in the rigth side of a tx in txs list (#550) --------- Co-authored-by: João Bordalo <[email protected]> * final removal Co-authored-by: Copilot <[email protected]> * Upgrade ts-sdk 0.4.19 - boltz-swap 0.3.20 (#565) * lint * fix box-sizing Co-authored-by: Copilot <[email protected]> * .label styles Co-authored-by: Copilot <[email protected]> * fix branta lint error (#568) * works on refresher Co-authored-by: Copilot <[email protected]> * fix unit tests Co-authored-by: Copilot <[email protected]> * remove ionic/react * fix label alignment * DRY: AssetCard Co-authored-by: Copilot <[email protected]> * fix inputAmount --------- Co-authored-by: Sahil Chaturvedi <[email protected]> Co-authored-by: Sahil Chaturvedi <[email protected]> Co-authored-by: pietro909 <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Andrew Camilleri <[email protected]>
Preparation for #559
@Kukks please review
Summary by CodeRabbit
Tests
Chores