Files
bank/app/dash/e2e/landing.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

52 lines
1.9 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { shot } from './demo'
const TIERS = [
{ name: 'Silver', price: '$29' },
{ name: 'Gold', price: '$99' },
{ name: 'Black', price: '$299' },
{ name: 'Sovereign', price: '$999' },
]
test('landing renders the membership ladder from live plans', async ({ page }) => {
await page.goto('/')
const membership = page.locator('#plans')
await expect(membership.getByRole('heading', { name: 'Membership' })).toBeVisible()
for (const tier of TIERS) {
const card = membership.locator('.card').filter({ hasText: tier.name })
await expect(card).toHaveCount(1)
await expect(card.getByRole('heading', { name: tier.name })).toBeVisible()
await expect(card.getByText(tier.price, { exact: true })).toBeVisible()
}
await shot(page, 'landing-plans')
})
// The hero preview is the account you get. Its headline is the sum of the
// balances under it — a total that contradicts its own parts (or the app it
// leads into) reads as two different companies.
test('the hero total is the sum of the balances it shows', async ({ page }) => {
await page.goto('/')
const dollars = (s: string) => parseFloat(s.replace(/[^0-9.]/g, ''))
const total = dollars(await page.getByText(/^\$[\d,]+\.\d{2}$/).first().innerText())
const tiles = await page.locator('.card-2 p:last-child').allInnerTexts()
const parts = tiles.filter((t) => /^\$[\d,]+\.\d{2}$/.test(t)).map(dollars)
expect(parts.length).toBe(5)
expect(parts.reduce((a, b) => a + b, 0)).toBeCloseTo(total, 2)
await shot(page, 'landing-hero')
})
test('plans come from the bank API, not hardcoded markup', async ({ page }) => {
const plans = page.waitForResponse(
(r) => r.url().includes('/v1/bank/plans') && r.status() === 200,
)
await page.goto('/')
const body = await (await plans).json()
expect(body.map((p: { name: string }) => p.name)).toEqual(TIERS.map((t) => t.name))
})