feat: complete phase 5 merchant delivery
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
import ts from 'typescript'
|
||||
|
||||
const apiSource = readFileSync(new URL('./api/deliveryOrders.ts', import.meta.url), 'utf8')
|
||||
const routesSource = readFileSync(new URL('./routes.ts', import.meta.url), 'utf8')
|
||||
const listSource = readFileSync(new URL('./views/DeliveryOrdersView.vue', import.meta.url), 'utf8')
|
||||
const detailSource = readFileSync(new URL('./views/DeliveryOrderDetailView.vue', import.meta.url), 'utf8')
|
||||
const createSource = readFileSync(new URL('./views/DeliveryOrderCreateView.vue', import.meta.url), 'utf8')
|
||||
const permissionSource = readFileSync(new URL('../tenant-management/contracts.ts', import.meta.url), 'utf8')
|
||||
|
||||
async function importTypeScriptModule(source) {
|
||||
const compiled = ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
|
||||
}).outputText
|
||||
return import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`)
|
||||
}
|
||||
|
||||
test('uses only authenticated merchant delivery endpoints without customer scope or mock fallback', () => {
|
||||
assert.match(apiSource, /method: 'GET'[\s\S]*url: '\/merchant\/v1\/last-leg\/delivery-orders'/)
|
||||
assert.match(apiSource, /delivery-orders\/\$\{encodeURIComponent\(deliveryNo\)\}/)
|
||||
assert.match(apiSource, /method: 'POST'[\s\S]*url: '\/merchant\/v1\/last-leg\/delivery-orders'/)
|
||||
assert.match(apiSource, /const \{ dispatchNo, \.\.\.request \} = payload/)
|
||||
assert.match(apiSource, /params: \{ dispatchNo \}/)
|
||||
assert.match(apiSource, /overseasApiRequest<DeliveryOrderDetail>/)
|
||||
assert.doesNotMatch(apiSource, /customerId|tenantId|Record<string, unknown>|mock|fallback/i)
|
||||
})
|
||||
|
||||
test('keeps list, read-only detail and create permissions independent in active routes', () => {
|
||||
assert.match(routesSource, /path: 'orders\/delivery'[\s\S]*permission: 'merchant:warehouse:delivery:list'[\s\S]*navigation:/)
|
||||
assert.match(routesSource, /path: 'orders\/delivery\/create'[\s\S]*DeliveryOrderCreateView\.vue[\s\S]*permission: 'merchant:warehouse:delivery:create'/)
|
||||
assert.match(routesSource, /path: 'orders\/delivery\/:deliveryNo'[\s\S]*DeliveryOrderDetailView\.vue[\s\S]*props: true[\s\S]*permission: 'merchant:warehouse:delivery:list'/)
|
||||
assert.match(routesSource, /orders-delivery-create[\s\S]*keepAlive: false/)
|
||||
assert.match(routesSource, /orders-delivery-detail[\s\S]*keepAlive: false/)
|
||||
})
|
||||
|
||||
test('renders the operation column first and covers loading, empty, error and pagination states', () => {
|
||||
const operationIndex = listSource.indexOf('label="操作"')
|
||||
const deliveryNumberIndex = listSource.indexOf('label="派送单号"')
|
||||
assert.ok(operationIndex >= 0 && operationIndex < deliveryNumberIndex)
|
||||
assert.match(listSource, /v-loading="loading"/)
|
||||
assert.match(listSource, /empty-text="暂无派送单"/)
|
||||
assert.match(listSource, /delivery-state--error/)
|
||||
assert.match(listSource, /@click="loadOrders">重试/)
|
||||
assert.match(listSource, /listDeliveryOrders\(buildQuery\(\)\)/)
|
||||
assert.match(listSource, /<el-pagination/)
|
||||
assert.doesNotMatch(listSource, /row\.id[^a-zA-Z]|deliveryNo \|\| row\.id|customerId|tenantId/)
|
||||
})
|
||||
|
||||
test('shows the create button only with the independent create permission', () => {
|
||||
assert.match(listSource, /auth\.hasPermission\('merchant:warehouse:delivery:create'\)/)
|
||||
assert.match(listSource, /<el-button v-if="canCreate" type="primary" @click="openCreate"/)
|
||||
assert.doesNotMatch(listSource, /hasPermission\('merchant:warehouse:delivery:list'\)[\s\S]{0,120}创建派送单/)
|
||||
})
|
||||
|
||||
test('loads a typed detail route by delivery number and never exposes an internal id fallback', () => {
|
||||
assert.match(detailSource, /defineProps<\{ deliveryNo: string \}>/)
|
||||
assert.match(detailSource, /getDeliveryOrder\(props\.deliveryNo\)/)
|
||||
assert.match(detailSource, /派送单详情加载失败/)
|
||||
for (const heading of ['订单信息', '运输与预约', '货物明细', '费用明细']) assert.match(detailSource, new RegExp(heading))
|
||||
assert.doesNotMatch(detailSource, /detail\?\.id|deliveryNo \|\| detail\?\.id|customerId|tenantId/)
|
||||
})
|
||||
|
||||
test('submits the complete create contract with stable retry idempotency and recoverable failures', () => {
|
||||
for (const model of ['dispatchNo', 'plannedDeliveryAt', 'deliveryMethod', 'pickupMethod', 'loadingType', 'waybillNo', 'carrierName', 'vehicleType', 'vehicleSize', 'driverName', 'driverPhone', 'vehiclePlate', 'appointmentAccount', 'sellerName', 'remark']) {
|
||||
assert.match(createSource, new RegExp(`v-model="form\\.${model}"`))
|
||||
}
|
||||
assert.match(createSource, /if \(!pendingIdempotencyKey\) pendingIdempotencyKey = crypto\.randomUUID\(\)/)
|
||||
assert.match(createSource, /createDeliveryOrder\(requestPayload\(\)\)/)
|
||||
assert.match(createSource, /fees: form\.fees\.map/)
|
||||
assert.match(createSource, /submitError\.value = error instanceof Error/)
|
||||
assert.match(createSource, /useUnsavedWorkRegistration/)
|
||||
assert.doesNotMatch(createSource, /customerId|tenantId|mock|fallback/i)
|
||||
})
|
||||
|
||||
test('maps both merchant delivery permissions independently', async () => {
|
||||
assert.match(permissionSource, /'merchant:warehouse:delivery:list': \['merchant:warehouse:delivery:list'\]/)
|
||||
assert.match(permissionSource, /'merchant:warehouse:delivery:create': \['merchant:warehouse:delivery:create'\]/)
|
||||
const contracts = await importTypeScriptModule(permissionSource)
|
||||
assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:list'])), true)
|
||||
assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:create'])), false)
|
||||
assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:create', new Set(['merchant:warehouse:delivery:create'])), true)
|
||||
assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:create', new Set(['merchant:warehouse:delivery:list'])), false)
|
||||
})
|
||||
Reference in New Issue
Block a user