56 lines
2.9 KiB
JavaScript
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/)
|
|
})
|