Files
bank/app/dash/e2e/exchange.spec.ts
zeekayandHanzo Dev d934daa779 feat: correct receiving details, card-rail transactions, and desktop density
Second polish pass over the dash from the visual sweep — the remaining screens
now carry the data a real bank shows and use the space they have.

Backend:
- Receiving coordinates are currency-correct. The "US IBAN" was wrong — the US
  issues no IBANs. receivingFor derives bank name + ABA routing + account
  number + SWIFT + bank address for US accounts, IBAN + BIC for IBAN markets,
  deterministic per account, exposed as account.receiving. account.iban is now
  empty for US. Dropped the redundant stored iban metadata.
- Card purchases are their own transaction type (`card`) — a debit like a
  payment but on the card rail, so they carry no wire fee and don't route to
  forex (both fall through cleanly). The Cards screen lists these specifically.

Dash:
- Accounts + Send render the full receiving coordinates (one Coordinates
  component, shape chosen by currency), each line copyable — enough to actually
  receive a wire.
- Cards: spend-limits panel (month-to-date debits vs the account ceiling) and a
  card-transaction list, filling the desktop dead space; one column on mobile.
- Exchange: the pair is priced on arrival (rate is a property of the pair, not
  the amount) and recent conversions sit beside the converter.
- Activity: a conversion reads as one movement (both legs on the line) instead
  of two adjacent rows; filtering still splits the legs.
- Wallet: copy button on the full deposit address. Landing hero balance now
  equals the demo account, tile for tile. Named nav landmarks; EMV chip detail.

SDK 1.3.4: Account.receiving typed. e2e up to 29 green (added activity + cards
specs, receiving/landing/exchange/wallet/shell coverage). Backend go
test/vet/staticcheck/deadcode clean.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-23 07:14:13 -07:00

61 lines
2.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from '@playwright/test'
import { signIn, holding, holdingAmount, shot } from './demo'
test('the pair is priced before anything is typed', async ({ page }) => {
await signIn(page)
await page.goto('/app/exchange')
// A rate belongs to the pair, not to the amount: the page opens on it.
await expect(page.getByText(/1 USD ≈ [\d.]+ EUR/)).toBeVisible()
// Priced, but nothing to convert yet.
await expect(page.getByRole('button', { name: 'Convert USD → EUR' })).toBeDisabled()
// Changing a leg reprices without an amount either.
await page.getByRole('combobox').last().selectOption('GBP')
await expect(page.getByText(/1 USD ≈ [\d.]+ GBP/)).toBeVisible()
await shot(page, 'exchange-rate-on-arrival')
})
test('past conversions are listed beside the converter', async ({ page }) => {
await signIn(page)
await page.goto('/app/exchange')
await expect(page.getByRole('heading', { name: 'Recent conversions' })).toBeVisible()
const row = page.locator('.card > div').filter({ hasText: 'Converted USD → EUR' }).first()
await expect(row).toBeVisible()
expect(await row.innerText()).toMatch(/−\$[\d,]+\.\d{2}\s*→\s*\+€[\d,]+\.\d{2}/)
})
test('the wallet Buy tile converts USD into LUX', async ({ page }) => {
await signIn(page)
await page.getByRole('link', { name: 'Wallet' }).first().click()
await expect(page.getByRole('heading', { name: 'Crypto wallet' })).toBeVisible()
const before = (await holdingAmount(page, 'LUX')) ?? 0
// Buy / Sell / Convert are links into the exchange, preloaded with the pair.
await page.getByRole('link', { name: 'Buy', exact: true }).click()
await expect(page).toHaveURL(/\/app\/exchange\?from=USD&to=LUX/)
await expect(page.getByRole('heading', { name: 'Exchange' })).toBeVisible()
await page.getByPlaceholder('0.00').fill('10')
// The quote is debounced and priced by bankd; wait for the rate to land.
const convert = page.getByRole('button', { name: 'Convert USD → LUX' })
await expect(page.getByText(/1 USD ≈ [\d.]+ LUX/)).toBeVisible()
await expect(convert).toBeEnabled()
await convert.click()
await expect(page.getByText(/Converted .* to .*LUX\./)).toBeVisible()
await shot(page, 'exchange')
// The conversion settled against the same ledger the wallet reads.
await page.getByRole('link', { name: 'Wallet' }).first().click()
await expect(holding(page, 'LUX')).toBeVisible()
await expect
.poll(() => holdingAmount(page, 'LUX'), { message: 'the LUX holding should grow by the conversion' })
.toBeGreaterThan(before)
})