Files
client-web/src/modules/cross-border/businessOrders.test.mjs
T

117 lines
6.6 KiB
JavaScript
Raw Normal View History

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { runInNewContext } from 'node:vm'
import ts from 'typescript'
const api = readFileSync(new URL('./api/businessOrders.ts', import.meta.url), 'utf8')
const routes = readFileSync(new URL('./routes.ts', import.meta.url), 'utf8')
const view = readFileSync(new URL('./components/BusinessOrdersView.vue', import.meta.url), 'utf8')
test('renders unknown pallet service and container type as dashes while preserving explicit values', () => {
const helpers = view.slice(view.indexOf('function read('), view.indexOf('function idempotencyKey('))
const compiled = ts.transpileModule(helpers, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText
const { displayCell, yesNo, text } = runInNewContext(`${compiled}\n({ displayCell, yesNo, text })`)
assert.equal(displayCell({ palletRequired: null }, 'palletRequired'), '—')
assert.equal(displayCell({ palletRequired: 'NO' }, 'palletRequired'), '否')
assert.equal(displayCell({ palletRequired: 'YES' }, 'palletRequired'), '是')
assert.equal(displayCell({ containerType: null }, 'containerType'), '—')
assert.equal(displayCell({ containerType: '40HQ' }, 'containerType'), '40HQ')
assert.equal(yesNo(null), '—')
assert.equal(text(null), '—')
assert.match(api, /palletRequired\?: string \| null/)
const create = api.slice(api.indexOf('export interface TransportCreate'), api.indexOf('export interface TransportUpdate'))
assert.doesNotMatch(create, /palletRequired\?: string \| null/)
})
test('shows merchant-owned arrangement facts without a pool request or editable status control', () => {
assert.match(api, /fleetArrangement: import\('\.\.\/fleetArrangement'\)\.FleetArrangement \| null/)
assert.match(view, /aria-label="承运安排"/)
for (const field of ['signedPackages', 'unsignedPackages', 'returnedPackages', 'readOnlyReason']) assert.ok(view.includes(`fleetArrangement.${field}`))
assert.doesNotMatch(api, /capacity-offers|fleet\/trips/)
assert.match(view, /if \(query\.page !== 1\) query\.page = 1\s+else void load\(\)/)
assert.match(view, /if \(reloadDirectly\) void load\(\)/)
})
test('activates all three customer cross-border pages with frozen permissions', () => {
for (const [path, permission] of [
2026-09-17 18:25:08 +08:00
['cross-border/procurement', 'merchant:cross-border:procurement:list'],
['cross-border/domestic-trucking', 'merchant:cross-border:domestic-trucking:list'],
['cross-border/pallet-shipping', 'merchant:cross-border:pallet-shipping:list'],
]) {
assert.match(routes, new RegExp(`path: '${path}'[\\s\\S]*?release: 'active'[\\s\\S]*?permission: '${permission}'`))
}
assert.doesNotMatch(view, /PrototypeFeatureView|mock|customerId|tenantId/i)
2026-09-17 18:25:08 +08:00
assert.match(view, /merchant:cross-border:procurement:create/)
assert.match(view, /merchant:cross-border:domestic-trucking:create/)
assert.match(view, /merchant:cross-border:pallet-shipping:create/)
2026-09-18 16:48:35 +08:00
assert.match(view, /v-if="canCreate"[^>]*type="primary"/)
})
test('uses only merchant overseas endpoints and keeps retry idempotency stable', () => {
assert.match(api, /overseas-procurements/)
assert.match(api, /us-truck-orders/)
assert.match(api, /local-looseboard-orders/)
assert.doesNotMatch(api, /\/platform\/|\/internal\/|customerId|tenantId/)
assert.match(view, /const stableSubmitKey = submitKey\.value \|\| idempotencyKey\(\)/)
assert.match(view, /submitKey\.value = stableSubmitKey/)
assert.match(view, /idempotencyKey: stableSubmitKey/g)
})
test('covers list detail create loading error empty pagination and actions-first contracts', () => {
assert.match(view, /mode\.value = 'detail'/)
assert.match(view, /mode\.value = 'list'/)
assert.match(view, /mode = 'create'/)
assert.match(view, /v-loading="loading"/)
2026-09-18 16:48:35 +08:00
assert.match(view, /class="business-load-error"/)
assert.match(view, /empty-text="暂无订单"/)
2026-09-18 16:48:35 +08:00
assert.match(view, /<QueryToolbar[^>]*layout="grid"/)
assert.match(view, /<DataTable[^>]*:row-actions="getRowActions"/)
assert.match(view, /key: 'actions', label: '操作', width: 90, fixed: 'left'/)
assert.match(view, /class="business-table-card"/)
2026-09-14 10:02:37 +08:00
assert.match(view, /PENDING_OUTBOUND: '待出库'/)
assert.match(view, /PARTIALLY_OUTBOUND: '部分出库'/)
assert.match(view, /OUTBOUNDED: '全部出库'/)
assert.match(view, /\['入库量', read\(summary, 'inboundQuantity'\)\]/)
assert.match(view, /\['审核备注', current\.reviewRemark\]/)
})
2026-09-18 16:48:35 +08:00
test('opens every dashboard cross-border action on a dedicated protected create page', () => {
for (const [path, permission] of [
['cross-border/procurement/create', 'merchant:cross-border:procurement:create'],
['cross-border/domestic-trucking/create', 'merchant:cross-border:domestic-trucking:create'],
['cross-border/pallet-shipping/create', 'merchant:cross-border:pallet-shipping:create'],
]) {
assert.match(routes, new RegExp(`path: '${path}'[\\s\\S]*?props: \\{ createPage: true \\}[\\s\\S]*?permission: '${permission}'`))
}
assert.match(view, /props\.createPage \? 'create' : 'list'/)
assert.match(view, /if \(props\.createPage\) await router\.push/)
})
test('opens all cross-border details on dedicated protected routes', () => {
for (const [path, routeName] of [
['cross-border/procurement/:orderNo/detail', 'cross-border-procurement-detail'],
['cross-border/domestic-trucking/:orderNo/detail', 'cross-border-domestic-trucking-detail'],
['cross-border/pallet-shipping/:orderNo/detail', 'cross-border-pallet-shipping-detail'],
]) {
assert.match(routes, new RegExp(`path: '${path.replaceAll('/', '\\/')}'[\\s\\S]*?name: '${routeName}'`))
}
assert.match(view, /name: detailRouteName\.value, params: \{ orderNo: row\.orderNo \}/)
assert.match(view, /class="business-detail-error"/)
})
2026-09-14 10:02:37 +08:00
test('keeps procurement filters, details and forms readable at PDA width', () => {
assert.match(view, /@media \(max-width: 767px\)/)
2026-09-18 16:48:35 +08:00
assert.match(view, /queryFields/)
2026-09-14 10:02:37 +08:00
assert.match(view, /\.detail-grid \{ grid-template-columns: 1fr; \}/)
assert.match(view, /\.form-card :deep\(\.el-form-item\) \{ display: block; \}/)
assert.match(view, /\.budget-form-item :deep\(\.el-form-item__content\) \{ display: flex; flex-wrap: nowrap; \}/)
})
2026-09-18 16:48:35 +08:00
test('uses the shared dense order-list and detail visual language', () => {
assert.match(view, /class="business-orders client-ui"/)
assert.match(view, /\.business-detail-page \{[^}]*border: 1px solid var\(--client-border\);[^}]*border-radius: var\(--client-radius\);/)
assert.match(view, /\.detail-grid \{[^}]*grid-template-columns: repeat\(4, minmax\(0, 1fr\)\)/)
assert.match(view, /\.detail-grid div \{[^}]*min-height: 34px;[^}]*grid-template-columns: 76px minmax\(0, 1fr\)/)
})