Files
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

46 lines
1.9 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test'
import { signIn, shot } from './demo'
const box = async (page: Page, heading: string) => {
const section = page.locator('section').filter({ has: page.getByRole('heading', { name: heading }) })
return (await section.boundingBox())!
}
test('at desktop width the card sits beside its limits and its spending', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 })
await signIn(page)
await page.goto('/app/cards')
await expect(page.getByRole('heading', { name: 'Spend limits' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Card transactions' })).toBeVisible()
await expect(page.getByText('Spent this month')).toBeVisible()
await expect(page.getByText(/^of \$[\d,]+\.\d{2}$/)).toBeVisible()
await expect(page.getByText('Daily limit')).toBeVisible()
// Payments made on the account show up as the card's spending.
const spend = page.locator('.card > div').filter({ hasText: 'Card — Apple Store' })
await expect(spend.first()).toBeVisible()
// The right-hand column starts to the right of the card face, on the same
// band of the page — that is the dead space being used, not a taller stack.
const face = (await page.locator('.card-face').first().boundingBox())!
const limits = await box(page, 'Spend limits')
expect(limits.x).toBeGreaterThan(face.x + face.width)
expect(limits.y).toBeLessThan(face.y + face.height)
await shot(page, 'cards-desktop')
})
test('on mobile the card and its panels stack in one column', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 })
await signIn(page)
await page.goto('/app/cards')
const face = (await page.locator('.card-face').first().boundingBox())!
const limits = await box(page, 'Spend limits')
expect(limits.y).toBeGreaterThan(face.y + face.height)
expect(limits.x).toBeLessThan(face.x + 8)
await shot(page, 'cards-mobile')
})