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]>
61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
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)
|
||
})
|