2026-08-10 15:36:53 +08:00
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { resolve } from 'node:path'
|
|
|
|
|
|
|
|
|
|
const source = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/components/LoadingListsView.vue'), 'utf8')
|
|
|
|
|
const detailSource = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/components/LoadingListDetailDrawer.vue'), 'utf8')
|
2026-08-12 16:31:46 +08:00
|
|
|
const preloadDetailSource = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/pages/PreloadPlanDetailView.vue'), 'utf8')
|
2026-08-10 15:36:53 +08:00
|
|
|
|
|
|
|
|
describe('LoadingListsView', () => {
|
2026-08-14 13:03:11 +08:00
|
|
|
it('filters both list modes by an active name-only customer selector', () => {
|
|
|
|
|
expect(source).toContain("listPlatformTenants({ page: 1, size: 200, status: 'ACTIVE' })")
|
2026-08-14 14:27:03 +08:00
|
|
|
expect(source).toContain("label: customer.tenantName")
|
2026-08-14 13:03:11 +08:00
|
|
|
expect(source).toContain("customerId: String(query.customerId || '').trim() || undefined")
|
|
|
|
|
expect(source).not.toContain('customer.tenantCode')
|
|
|
|
|
})
|
2026-08-10 15:36:53 +08:00
|
|
|
it('keeps the operation column first and fixed left in both lists', () => {
|
|
|
|
|
const actionColumns = [...source.matchAll(/key: 'actions', label: '操作', fixed: 'left'/g)]
|
|
|
|
|
expect(actionColumns).toHaveLength(2)
|
|
|
|
|
expect(source.indexOf("key: 'actions'")).toBeLessThan(source.indexOf("key: 'preloadNo'"))
|
|
|
|
|
expect(source.indexOf("key: 'actions'", source.indexOf("key: 'actions'") + 1)).toBeLessThan(source.indexOf("key: 'loadingListNo'"))
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-10 18:07:33 +08:00
|
|
|
it('queries all warehouses without exposing a warehouse selector', () => {
|
|
|
|
|
expect(source).not.toContain('listAllPlatformWarehouses')
|
|
|
|
|
expect(source).not.toContain("key: 'warehouseId'")
|
|
|
|
|
expect(source).not.toContain('loadingWarehouses')
|
|
|
|
|
expect(source).not.toContain('warehouseId: warehouseId || undefined')
|
|
|
|
|
expect(source).toContain("selectedWarehouseId.value = String(row.warehouseId || '')")
|
2026-08-10 17:42:17 +08:00
|
|
|
expect(source).not.toContain("ElMessage.warning('请先选择仓库')")
|
|
|
|
|
expect(source).not.toContain("'请先选择仓库后查询'")
|
2026-08-11 11:15:44 +08:00
|
|
|
expect(source).toContain('MISSING_WAREHOUSE_CONTEXT')
|
2026-08-10 15:36:53 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-12 16:31:46 +08:00
|
|
|
it('uses the shared query, table, dialog and error components without a detail drawer', () => {
|
2026-08-10 15:36:53 +08:00
|
|
|
expect(source).toContain('QueryToolbar')
|
|
|
|
|
expect(source).toContain('DataTable')
|
|
|
|
|
expect(source).toContain('AppDialog')
|
|
|
|
|
expect(source).toContain('LoadErrorState')
|
|
|
|
|
expect(source).toContain('ApiErrorMeta')
|
2026-08-12 16:31:46 +08:00
|
|
|
expect(detailSource).not.toContain('AppDrawer')
|
|
|
|
|
expect(detailSource).toContain('ContainerFeesPanel')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('opens preload detail as a route and confirmed detail inline on the list page', () => {
|
|
|
|
|
expect(source).toContain("name: 'preload-plan-detail'")
|
|
|
|
|
expect(source).toContain('v-if="!isPreload && (detailLoading || detailError || detail)"')
|
|
|
|
|
expect(source).toContain('class="confirmed-inline-detail surface-panel"')
|
|
|
|
|
expect(preloadDetailSource).toContain('返回列表')
|
|
|
|
|
expect(preloadDetailSource).toContain('router.back()')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps the real transport edit contract in a 620px centered modal', () => {
|
|
|
|
|
expect(source).toContain('class="loading-transport-dialog"')
|
|
|
|
|
expect(source).toContain('width="min(620px, calc(100vw - 28px))"')
|
|
|
|
|
expect(preloadDetailSource).toContain('width="min(620px, calc(100vw - 28px))"')
|
|
|
|
|
expect(source).not.toContain('备注信息(仅自己可见)')
|
|
|
|
|
expect(source).not.toContain('备注信息(客户可见)')
|
|
|
|
|
expect(source).not.toContain('备注信息(仓库可见)')
|
2026-08-10 15:36:53 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('only exposes transport editing for LOADING preload details', () => {
|
2026-08-11 14:23:46 +08:00
|
|
|
expect(detailSource).toContain("mode === 'preload' && canEdit && ['EDITING', 'LOADING'].includes(summary.status)")
|
2026-08-13 11:57:44 +08:00
|
|
|
expect(detailSource).toContain('报关资料')
|
|
|
|
|
expect(detailSource).toContain('完成报关')
|
2026-08-10 15:36:53 +08:00
|
|
|
expect(source).toContain('expectedVersion: detail.value.summary.version')
|
|
|
|
|
})
|
2026-08-11 10:35:39 +08:00
|
|
|
|
2026-08-11 11:52:30 +08:00
|
|
|
it('renders business labels instead of raw loading-plan status codes', () => {
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(source).toContain(':status="loadingListStatusLabel(row.status)"')
|
|
|
|
|
expect(detailSource).toContain(':status="loadingListStatusLabel(summary.status)"')
|
2026-08-11 11:52:30 +08:00
|
|
|
expect(detailSource).not.toContain(':status="summary.status"')
|
|
|
|
|
expect(source).not.toContain('仅装柜中允许修改')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-11 10:35:39 +08:00
|
|
|
it('exposes a row edit action only for loading preload plans', () => {
|
2026-08-11 14:23:46 +08:00
|
|
|
expect(source).toContain("isPreload && canEdit && ['EDITING', 'LOADING'].includes(row.status)")
|
2026-08-11 10:35:39 +08:00
|
|
|
expect(source).toContain('openEditFromRow(row)')
|
|
|
|
|
expect(source).toContain('await loadDetail()')
|
|
|
|
|
})
|
2026-08-11 11:15:44 +08:00
|
|
|
|
|
|
|
|
it('refreshes the detail version on A0409 while preserving transport input', () => {
|
|
|
|
|
expect(source).toContain("details.code === 'A0409'")
|
|
|
|
|
expect(source).toContain('await loadDetail()')
|
|
|
|
|
expect(source).toContain('当前输入已保留,请核对后重新保存')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('opens a validated plan and warehouse deep link without mixing list modes', () => {
|
|
|
|
|
expect(source).toContain('useRoute()')
|
|
|
|
|
expect(source).toContain('route.query.planId')
|
|
|
|
|
expect(source).toContain('route.query.warehouseId')
|
2026-08-11 14:23:46 +08:00
|
|
|
expect(source).toContain("const expectedStatuses = isPreload.value ? ['EDITING', 'LOADING'] : ['COMPLETED']")
|
2026-08-11 11:15:44 +08:00
|
|
|
expect(source).toContain('MODE_STATUS_MISMATCH')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('passes translated status text directly to the badge', () => {
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(source).toContain('<StatusBadge :status="loadingListStatusLabel(row.status)" />')
|
2026-08-11 11:15:44 +08:00
|
|
|
expect(source).not.toContain('<StatusBadge :status="row.status" />')
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(detailSource).toContain('<StatusBadge :status="loadingListStatusLabel(summary.status)" />')
|
2026-08-11 11:15:44 +08:00
|
|
|
})
|
2026-08-12 09:25:55 +08:00
|
|
|
|
2026-08-12 18:00:26 +08:00
|
|
|
it('uses bordered list actions with a stable 320px layout', () => {
|
|
|
|
|
expect(source).toContain('size="small" type="primary" plain')
|
|
|
|
|
expect(source).toContain('size="small" @click="openEditFromRow(row)"')
|
|
|
|
|
expect(source).toContain('编辑运输信息')
|
|
|
|
|
expect(source).not.toContain('<el-button v-if="canView" link')
|
|
|
|
|
expect(source).toContain("key: 'actions', label: '操作', fixed: 'left', width: 230")
|
|
|
|
|
expect(source).toContain('flex-wrap: wrap')
|
|
|
|
|
expect(source).toContain('@media (max-width: 720px)')
|
|
|
|
|
expect(source).toContain('.row-actions { gap: 4px; }')
|
2026-08-12 18:16:44 +08:00
|
|
|
expect(source).toContain(':loading="loading"\n stripe')
|
|
|
|
|
expect(detailSource).toContain(':columns="itemColumns"\n stripe')
|
2026-08-12 18:00:26 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-12 09:25:55 +08:00
|
|
|
it('does not expose unknown history action codes', () => {
|
|
|
|
|
expect(detailSource).toContain("[action] || '状态更新'")
|
|
|
|
|
expect(detailSource).not.toContain('[action] || action')
|
|
|
|
|
})
|
2026-08-12 10:59:49 +08:00
|
|
|
it('uses safe labels when customer or operator names are missing', () => {
|
2026-08-12 12:35:32 +08:00
|
|
|
expect(source).toContain("item.customerName || '客户信息缺失'")
|
|
|
|
|
expect(source).not.toContain('item.customerName || item.accountId')
|
2026-08-12 10:59:49 +08:00
|
|
|
expect(detailSource).toContain("item.customerName || '客户信息缺失'")
|
|
|
|
|
expect(detailSource).toContain("history.operatorName || '操作人信息缺失'")
|
|
|
|
|
expect(detailSource).not.toContain('item.customerName || item.accountId')
|
|
|
|
|
expect(detailSource).not.toContain('history.operatorName || history.operatorId')
|
|
|
|
|
})
|
2026-08-13 11:57:44 +08:00
|
|
|
|
|
|
|
|
it('uploads declaration documents from a file picker and freezes them after completion', () => {
|
|
|
|
|
expect(source).toContain('<el-upload :auto-upload="false" :limit="1"')
|
|
|
|
|
expect(source).toContain('@confirm="uploadCustomsDocument"')
|
|
|
|
|
expect(source).toContain('uploadLoadingDeclaration(selectedPlanId.value, selectedWarehouseId.value, customsFile.value')
|
|
|
|
|
expect(detailSource).toContain("canManageCustoms && !detail.customsDocument?.completedAt")
|
|
|
|
|
expect(source).not.toContain('文件 ID')
|
|
|
|
|
expect(source).not.toContain('fileId')
|
|
|
|
|
expect(source).not.toContain('上传入口待')
|
|
|
|
|
})
|
2026-08-10 15:36:53 +08:00
|
|
|
})
|