Files
client-web/src/modules/finance/views/FinanceAccountView.test.mjs
T

57 lines
3.8 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
const apiSource = readFileSync(new URL('../api/account.ts', import.meta.url), 'utf8')
const viewSource = readFileSync(new URL('./FinanceAccountView.vue', import.meta.url), 'utf8')
const routesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf8')
const permissionsSource = readFileSync(new URL('../../tenant-management/contracts.ts', import.meta.url), 'utf8')
test('uses only authenticated client finance account endpoints without customer scope', () => {
for (const path of ['/client/finance/v1/account', '/client/finance/v1/account/ledger', '/client/finance/v1/recharge-requests', '/client/finance/v1/recharge-channels']) {
assert.match(apiSource, new RegExp(path.replaceAll('/', '\\/')))
}
assert.match(apiSource, /headers: \{ 'Idempotency-Key': idempotencyKey \}/)
assert.match(apiSource, /formData\.append\('amount', amount\)/)
assert.match(apiSource, /formData\.append\('paymentMethod', paymentMethod\)/)
assert.match(apiSource, /formData\.append\('file', file, file\.name\)/)
assert.doesNotMatch(apiSource, /customerId|tenantId|mock|fallback/i)
})
test('activates the account route and maps read and recharge permissions independently', () => {
assert.match(routesSource, /path: 'finance\/account'[\s\S]*FinanceAccountView\.vue[\s\S]*release: 'active'[\s\S]*permission: 'merchant:finance:account:view'/)
assert.match(permissionsSource, /'merchant:finance:account:view': \['merchant:finance:account:view'\]/)
assert.match(permissionsSource, /'merchant:finance:recharge:create': \['merchant:finance:recharge:create'\]/)
assert.match(viewSource, /auth\.hasPermission\('merchant:finance:recharge:create'\)/)
assert.match(viewSource, /<el-button v-if="canRecharge"/)
})
test('renders server balances, ledger and recharge states without internal identifiers', () => {
for (const field of ['actualBalance', 'frozenBalance', 'availableBalance', 'creditLimit', 'creditUsed', 'availableCredit']) assert.match(viewSource, new RegExp(`account\\?\\.${field}`))
for (const field of ['creditUsedAfter', 'availableCreditAfter']) assert.match(viewSource, new RegExp(`row\\.${field}`))
assert.match(viewSource, /实际可用资金与可用授信合计/)
assert.match(viewSource, /CREDIT_LIMIT: '授信调整'/)
assert.match(viewSource, /row-key="businessNo"/)
assert.match(viewSource, /row-key="requestNo"/)
assert.match(viewSource, /empty-text="暂无资金流水"/)
assert.match(viewSource, /empty-text="暂无充值申请"/)
assert.match(viewSource, /account-state--error/)
assert.match(viewSource, /v-loading="pageLoading"/)
assert.match(viewSource, /function formatAccountAmount[\s\S]*value == null \? '—' : formatAmount\(value\)/)
assert.match(viewSource, /formatAccountAmount\(account\?\.actualBalance\)/)
assert.doesNotMatch(viewSource, /requestId|ledgerId|customerId|tenantId/)
})
test('matches the frozen recharge workflow and preserves an idempotency key for failed retries', () => {
assert.match(viewSource, /title="账户充值" width="760px"/)
for (const amount of ['50.00', '100.00', '200.00', '500.00']) assert.match(viewSource, new RegExp(amount.replace('.', '\\.')))
assert.match(viewSource, /\['WECHAT', 'ALIPAY'\]/)
assert.match(viewSource, /selectedChannel\.value\?\.enabled && selectedChannel\.value\.qrCodeUrl/)
assert.match(viewSource, /accept="\.jpg,\.jpeg,\.png,image\/jpeg,image\/png"/)
assert.match(viewSource, /if \(!pendingIdempotencyKey\)[\s\S]*globalThis\.crypto\?\.randomUUID\?\.\(\)/)
assert.match(viewSource, /pendingIdempotencyKey = ''[\s\S]*rechargeVisible\.value = false/)
assert.match(viewSource, /:disabled="!canSubmit"/)
assert.match(viewSource, /await loadAccountData\(\)/)
assert.doesNotMatch(viewSource, /payment SDK|支付成功|模拟成功/)
})