import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import { describe, expect, it } from 'vitest' import { unifiedOrderStatusOptions } from '../api/unifiedOrders' const source = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/pages/UnifiedOrdersView.vue'), 'utf8').replace(/\r\n/g, '\n') const routeSource = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/routes.ts'), 'utf8').replace(/\r\n/g, '\n') const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8') describe('UnifiedOrdersView contract', () => { it('imports old-system workbooks only for the fixed test customer', () => { expect(source).toContain("can('platform:order:import')") expect(source).toContain('导入旧系统订单') expect(source).toContain('固定归属客户') expect(source).toContain('IT测试公司') expect(source).toContain('accept=".xlsx"') expect(source).toContain('legacyImportResult.items.filter((item) => !item.success)') }) it('exposes desktop pallet printing only for one-click dropship orders', () => { expect(source).toContain("detail.value?.summary.fulfillmentType === 'ONE_CLICK_DROPSHIP'") expect(source).toContain("{ key: 'dropship-print', label: '托盘打印', icon: '🖨️' }") expect(source).toContain("activeDetailTab === 'dropship-print'") expect(source).toContain(' { const openDialogSource = source.slice(source.indexOf('async function openFeeConfirmDialog()'), source.indexOf('async function executeConfirmAndSendFee()')) const sendFeeSource = source.slice(source.indexOf('async function executeConfirmAndSendFee()'), source.indexOf('function handleOrderSelectionChange')) expect(openDialogSource).toContain('getLatestInboundFee') expect(sendFeeSource).not.toContain('getLatestInboundFee') expect(sendFeeSource).toContain('adjustPublishedInboundFee') expect(source).toContain('价格表参考单价') expect(source).toContain('本次对客单价') expect(openDialogSource).toContain("feeConfirmPriceMissing.value = true") expect(sendFeeSource).toContain('createManualInboundQuote') expect(source).toContain("feeConfirmPriceMissing ? '未命中'") expect(source).toContain("feeConfirmPriceMissing ? '人工定价原因'") expect(source).not.toContain('
固定附加费
') expect(sendFeeSource).toContain("ElMessage.error(getApiErrorDetails(error, '确认费用发送失败').message)") }) it('locks measurement changes as soon as the customer confirms the fee', () => { expect(source).toContain('&& !measurementDetail.value.customerFeeConfirmed') expect(source).toContain('费用已由客户确认,测量数据已锁定') }) it('prevents duplicate confirm-and-send chains and reuses one attempt idempotency key set', () => { const sendFeeSource = source.slice(source.indexOf('async function executeConfirmAndSendFee()'), source.indexOf('function handleOrderSelectionChange')) expect(sendFeeSource).toContain('if (confirmFeeSending.value) return') expect(sendFeeSource).toContain('const attempt = currentFeeConfirmAttempt(selectedWeightKg, selectedVolumeCbm, selectedUnitPrice)') expect(sendFeeSource).toContain('attempt.calculateKey') expect(sendFeeSource).toContain('attempt.adjustKey') expect(sendFeeSource).toContain('attempt.confirmKey') expect(sendFeeSource).toContain('attempt.sendKey') expect(sendFeeSource).toContain("if (snapshot.feeStatus === 'SENT')") expect(source).toContain('feeConfirmAttempt = null\n feeConfirmOpen.value = true') }) it('exposes the product main statuses including dropship palletizing', () => { expect(unifiedOrderStatusOptions.map((item) => item.label)).toEqual([ '待审核', '待测量数据', '待一件代发组托', '待分柜', '装柜中', '装柜完成', '派送完成', '已完成', ]) for (const legacyLabel of ['待客户确认', '差异处理中', '派送中', '进入分柜']) { expect(source).not.toContain(legacyLabel) } }) it('renders accessible current-status tabs with keyboard navigation', () => { expect(source).toContain('role="tablist"') expect(source).toContain('role="tab"') expect(source).toContain(':aria-selected="selectedStatus === tab.value"') expect(source).toContain("event.key === 'ArrowRight'") expect(source).toContain("event.key === 'ArrowLeft'") expect(source).toContain("event.key === 'Home'") expect(source).toContain("event.key === 'End'") }) it('provides labeled prototype-aligned advanced filters backed by real query fields', () => { for (const key of [ 'receivingWarehouseId', 'destinationWarehouseId', 'inboundCode', 'expectedArrivalDate', 'feeReviewStatus', 'containerNo', 'cabinetNo', 'channelName', 'sailingDate', 'loaded', 'fullContainer', 'measurementReviewStatus', 'fbaCode', 'createdRange', ]) expect(source).toContain(`key: '${key}'`) for (const label of ['定位仓库', '交货仓', '费用审核状态', '是否已配载', '是否整柜', '测量数据审核状态', '商品 SKU 编码']) { expect(source).toContain(`label: '${label}'`) } expect(source).toContain('listAllPlatformWarehouses') expect(source).toContain("{ status: 'ACTIVE' }") expect(source).toContain("warehouse.warehouseType === 'DOMESTIC'") expect(source).toContain("loaded: optionalBoolean(activeFilters.loaded)") expect(source).toContain("fullContainer: optionalBoolean(activeFilters.fullContainer)") expect(source).toContain("{ value: 'UNRECORDED', label: '未录入' }") expect(source).not.toContain("{ value: 'DRAFT', label: '草稿' }") expect(source).not.toContain("{ value: 'CANCELLED', label: '已取消' }") expect(source.match(/key: 'inboundCode', type: 'input'/g)).toHaveLength(1) expect(source).not.toContain("key: 'orderNo', type:") expect(source).not.toContain('internalOrderStatus: filters.value.internalOrderStatus') expect(source).not.toContain("key: 'keyword'") const prototypeFieldOrder = [ 'receivingWarehouseId', 'destinationWarehouseId', 'inboundCode', 'expectedArrivalDate', 'feeReviewStatus', 'tenantId', 'containerNo', 'cabinetNo', 'channelName', 'sailingDate', 'loaded', 'fullContainer', 'measurementReviewStatus', 'fbaCode', 'createdRange', ] const fieldPositions = prototypeFieldOrder.map((key) => source.indexOf(`key: '${key}'`)) expect(fieldPositions).toEqual([...fieldPositions].sort((left, right) => left - right)) expect(source).toContain('preference-key="unified-orders-query-v2"') }) it('keeps unified filters aligned with the formal query contract and does not expose hold filters', () => { const filterSource = source.slice(source.indexOf('const primaryQueryFields'), source.indexOf('const columns:')) for (const key of ['expectedArrivalDate', 'feeReviewStatus', 'measurementReviewStatus', 'fbaCode', 'loaded', 'fullContainer']) { expect(filterSource).toContain(`key: '${key}'`) } expect(filterSource).not.toContain("key: 'holdCargo'") expect(filterSource).not.toContain("key: 'holdReason'") expect(filterSource).toContain("key: 'fbaCode', type: 'input', width: 220, label: '商品 SKU 编码'") expect(filterSource).not.toContain("label: '商品 FBA 编码'") expect(source).toContain('expectedArrivalDate: activeFilters.expectedArrivalDate || undefined') expect(source).toContain('feeReviewStatus: activeFilters.feeReviewStatus || undefined') expect(source).toContain('measurementReviewStatus: activeFilters.measurementReviewStatus || undefined') expect(source).toContain('fbaCode: trimmed(activeFilters.fbaCode)') }) it('keeps hold state in the other tab and consumes only frozen server capabilities', () => { for (const marker of [ 'summary.holdFlag', 'summary.currentHoldReason', 'getPlatformInboundHold', 'holdDetail.value?.canHold', 'holdDetail.value?.canRelease', "can('platform:firstleg:inbound:hold')", '扣货历史', '下载凭证', '订单尚未生成入库单,扣货状态仅可查看', 'uploadPlatformInboundHoldEvidence', 'downloadPlatformInboundHoldEvidence', '仅支持 TXT、PDF、JPG、PNG,单文件不超过 10MB', ]) expect(source).toContain(marker) expect(source).toContain("await Promise.all([loadDetail(true), loadOrders()])") expect(source).not.toContain('detail.summary.holdCargo') expect(source).not.toContain('detail.summary.holdReason') expect(source).toContain('evidenceFileId: holdUploadedEvidenceFileId.value') }) it('uses tabs as the only order-status filter and keeps advanced controls inside QueryToolbar', () => { expect(source).toContain('mainStatus: selectedStatus.value || undefined') expect(source).toContain('