This commit is contained in:
2026-08-14 14:50:39 +08:00
parent 261a8bf2df
commit 36a048a2bc
34 changed files with 263 additions and 120 deletions
+2 -1
View File
@@ -45,7 +45,8 @@ export async function run(context) {
| 商户资质与下单身份 | `merchant-qualification-fixture.mjs` | `merchant-qualification-fixture-contract.test.mjs` |
| 订单创建、可计价主数据与审核异步收敛 | `orders.mjs` | `orders-pricing-fixture-contract.test.mjs`、`orders-approval-convergence-contract.test.mjs` |
| 入库测量、首次打印冻结箱码与墨家扫码 | `inbound-measurement.mjs` | `inbound-measurement-box-code-contract.test.mjs` |
| 三期柜次、装柜、PDA 与证据/出仓门禁 | `phase3-container-loading.mjs`、`phase3-loading.mjs`、`phase3-pda-convergence.mjs`、`phase3-evidence-gate.mjs`、`phase3-loading-finalize.mjs`、`phase3-outbound-dispatch.mjs` | `phase3-producer-chain-contract.test.mjs`、`manual-state-loading-pda-contract.test.mjs` |
| 三期柜次概览、装柜、PDA 与证据/出仓门禁 | `phase3-container-loading.mjs`、`phase3-container-overview.mjs`、`phase3-loading.mjs`、`phase3-pda-convergence.mjs`、`phase3-evidence-gate.mjs`、`phase3-loading-finalize.mjs`、`phase3-outbound-dispatch.mjs` | `phase3-container-regression-contract.test.mjs`、`phase3-producer-chain-contract.test.mjs`、`manual-state-loading-pda-contract.test.mjs` |
| 分柜与父子入库单数量守恒 | `phase3-order-splitting.mjs` | `phase3-order-splitting-contract.test.mjs` |
| 订舱共享主数据前置与订舱验收 | `ocean-booking-fixture.mjs`、`ocean-bookings.mjs`、`phase3-port-schedules.mjs`、`phase3-carrier-schedule-queries.mjs` | `ocean-booking-fixture-contract.test.mjs`、`phase3-carrier-schedule-queries.test.mjs` |
| 拖车预约 | `truck-appointments.mjs` | `truck-appointments-contract.test.mjs` |
| 消息中心管理 | `notifications.mjs` | `notifications-contract.test.mjs` |
@@ -6,9 +6,7 @@ function id(value) { return value == null ? '' : String(value) }
export function controlledPhotoDownload(downloadPath, response) {
const controlledPath = typeof downloadPath === 'string'
&& downloadPath.startsWith('/api/')
&& !downloadPath.startsWith('//')
&& !downloadPath.includes('://')
&& /^\/api\/platform\/v1\/containers\/[1-9]\d*\/pda\/photos\/[1-9]\d*\/download$/.test(downloadPath)
const contentType = String(response?.contentType || '').toLowerCase()
return controlledPath
&& response?.http === 200
@@ -17,6 +17,7 @@ const truckAttachmentDialog = read(`${adminRoot}trucking/components/TruckAttachm
const truckList = read(`${adminRoot}trucking/pages/TruckAppointmentsView.vue`)
const truckForm = read(`${adminRoot}trucking/components/TruckAppointmentForm.vue`)
const containerList = read(`${adminRoot}container-loading/pages/ContainersView.vue`)
const containerOverviewPanel = read(`${adminRoot}container-loading/components/ContainerOverviewPanel.vue`)
const containerLoadingAcceptance = read('phase3-container-loading.mjs')
const truckAcceptance = read('truck-appointments.mjs')
const matrix = read('../../../docs/三期/货柜管理全链验收矩阵-2026-08-13.md')
@@ -41,23 +42,27 @@ test('overview regression requires workflowStatus and distinguishes 100 percent
test('every overview photo uses its server-provided controlled downloadPath', () => {
assert.match(overviewOpenApi, /\/api\/platform\/v1\/containers\/\{id\}\/pda\/photos\/\{fileId\}\/download/)
assert.match(overviewAcceptance, /context\.requestBinary\('GET', downloadPath\)/)
assert.equal(controlledPhotoDownload('/api/platform/v1/files/1', {
assert.equal(controlledPhotoDownload('/api/platform/v1/containers/7/pda/photos/23/download', {
http: 200, bytes: new Uint8Array([1]), contentType: 'image/png',
}), true)
for (const [path, response] of [
['https://example.test/file', { http: 200, bytes: new Uint8Array([1]), contentType: 'image/png' }],
['/api/platform/v1/files/1', { http: 200, bytes: new Uint8Array([1]), contentType: 'application/json' }],
['/api/platform/v1/files/1', { http: 404, bytes: new Uint8Array(), contentType: 'image/png' }],
['/api/platform/v1/files/loading-evidence/21/22/23', { http: 200, bytes: new Uint8Array([1]), contentType: 'image/png' }],
['/api/platform/v1/containers/7/pda/photos/23/download', { http: 200, bytes: new Uint8Array([1]), contentType: 'application/json' }],
['/api/platform/v1/containers/7/pda/photos/23/download', { http: 404, bytes: new Uint8Array(), contentType: 'image/png' }],
]) assert.equal(controlledPhotoDownload(path, response), false)
})
test('truck attachment API is frozen while Admin add-reference entry remains blocked', () => {
test('truck attachment API and Admin upload-reference-delete flow are frozen', () => {
assert.match(truckApi, /referenceTruckAppointmentAttachment/)
assert.match(truckApi, /uploadTruckAppointmentAttachment/)
assert.match(truckApi, /deleteTruckAppointmentAttachment/)
assert.doesNotMatch(truckAttachmentDialog, /referenceTruckAppointmentAttachment/)
assert.match(truckAttachmentDialog, /uploadTruckAppointmentAttachment/)
assert.match(truckAttachmentDialog, /上传并关联/)
assert.match(truckAttachmentDialog, /confirmButtonText: '确认', cancelButtonText: '取消'/)
assert.match(truckAcceptance, /删除后回查拖车预约附件列表/)
assert.match(truckAcceptance, /!remainingAttachments\.some/)
assert.match(matrix, /拖车附件新增入口缺失[\s\S]*BLOCKED_CONTRACT/)
assert.match(matrix, /拖车附件闭环[\s\S]*`LANDED`/)
})
test('existing container number replacement keeps container plan and PDA identities stable', () => {
@@ -78,6 +83,10 @@ test('customer public display uses tenantName without a legalName fallback', ()
assert.match(matrix, /客户公开名称一致性[\s\S]*已统一 `tenantName`[\s\S]*`LANDED`/)
})
test('Chinese status dialogs and 390px layout remain explicit Admin contract blockers', () => {
assert.match(matrix, /中文状态、弹框与 390px 布局[\s\S]*BLOCKED_CONTRACT/)
test('Chinese status dialogs and 390px layout are landed', () => {
assert.match(releaseView, /确认放舱/)
assert.match(containerOverviewPanel, /AWAITING_REVIEW: '待人工复核'/)
assert.match(containerOverviewPanel, /PHOTO_STEP_EMPTY_CONTAINER: '空柜照片'/)
assert.match(matrix, /中文状态、弹框与 390px 布局[\s\S]*`LANDED`/)
assert.doesNotMatch(matrix, /拖车附件新增入口缺失|中文状态、弹框与 390px 布局[^\n]*BLOCKED_CONTRACT/)
})