Files
client-web/src/modules/finance/views/FinanceBillsView.test.mjs
T
2026-08-11 10:46:01 +08:00

56 lines
2.9 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
const routesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf8')
const viewSource = readFileSync(new URL('./FinanceBillsView.vue', import.meta.url), 'utf8')
const apiSource = readFileSync(new URL('../api/finance.ts', import.meta.url), 'utf8')
const adapterSource = readFileSync(new URL('../api/adapters.ts', import.meta.url), 'utf8')
const permissionSource = readFileSync(new URL('../../tenant-management/contracts.ts', import.meta.url), 'utf8')
test('activates the customer finance route with a read-only permission', () => {
assert.match(routesSource, /path: 'finance\/bills'/)
assert.match(routesSource, /FinanceBillsView\.vue/)
assert.match(routesSource, /release: 'active'/)
assert.match(routesSource, /permission: 'finance:bill:view'/)
assert.doesNotMatch(routesSource, /finance\/payables|应付账单/)
})
test('uses client finance read APIs without browser-selected customer scope', () => {
assert.match(apiSource, /method: 'GET'/)
assert.match(apiSource, /\/client\/finance\/v1\/bills/)
assert.match(apiSource, /\/client\/finance\/v1\/receipts/)
assert.doesNotMatch(apiSource, /\/merchant\/v1\/finance/)
assert.doesNotMatch(apiSource, /customerId|tenantId/)
assert.doesNotMatch(apiSource, /method: '(POST|PUT|PATCH|DELETE)'/)
assert.doesNotMatch(apiSource, /params:\s*\{\s*billId/)
})
test('adapts the finance snake-case wire contract and filters receipts locally by bill id', () => {
for (const field of ['bill_no', 'customer_name_snapshot', 'bill_date', 'total_amount', 'outstanding_amount', 'fee_item_name', 'order_id', 'receipt_no', 'bill_id', 'applied_amount']) {
assert.match(adapterSource, new RegExp(field))
}
assert.match(adapterSource, /filter\(\(receipt\).*receipt\.bill_id/s)
assert.match(viewSource, /loadDetail\(row\.id\)/)
assert.doesNotMatch(viewSource, /loadDetail\(row\.billNo\)/)
})
test('keeps actions first and exposes list, balance, detail, receipt, empty and error states', () => {
const actionIndex = viewSource.indexOf('label="操作"')
const billNumberIndex = viewSource.indexOf('label="账单编号"')
assert.ok(actionIndex >= 0 && actionIndex < billNumberIndex)
for (const expected of ['未收余额', '账单详情', '收款记录', '暂无账单', '暂无收款记录', 'state-panel--error']) {
assert.match(viewSource, new RegExp(expected))
}
for (const forbidden of ['确认账单', '发起争议', '新增收款', '核销', '修改金额']) {
assert.doesNotMatch(viewSource, new RegExp(forbidden))
}
})
test('catalogs customer finance as an assignable view-only role permission', () => {
assert.match(permissionSource, /code: 'finance:bill:view'/)
assert.match(permissionSource, /不包含任何财务写操作/)
assert.match(permissionSource, /merchant:finance:statement:list/)
assert.match(permissionSource, /merchant:finance:statement:view/)
})