mirror of
https://github.com/luxfi/bank.git
synced 2026-10-30 17:02:07 +00:00
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]>
52 lines
1.9 KiB
TypeScript
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))
|
|
})
|