144 lines
9.7 KiB
TypeScript
144 lines
9.7 KiB
TypeScript
import ElementPlus from 'element-plus'
|
||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||
import { getLastMileOrder } from '../api/lastMileOrders'
|
||
import LastMileOrderDetailView from './LastMileOrderDetailView.vue'
|
||
import { getWarehouseTransfer } from '../api/warehouseTransfers'
|
||
|
||
const push = vi.hoisted(() => vi.fn())
|
||
vi.mock('vue-router', () => ({ useRoute: () => ({ params: { orderId: 'internal-order-id' } }), useRouter: () => ({ push }) }))
|
||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => false }) }))
|
||
vi.mock('../api/lastMileOrders', () => ({ getLastMileOrder: vi.fn(), createLastMileOrderFee: vi.fn() }))
|
||
vi.mock('../api/warehouseTransfers', () => ({ getWarehouseTransfer: vi.fn() }))
|
||
enableAutoUnmount(afterEach)
|
||
|
||
describe('LastMileOrderDetailView domestic detail alignment', () => {
|
||
it('shows signed box contents and actual quantities on return orders', async () => {
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'tail-return', customerId: 'customer-1', orderNo: 'RETU-1', orderType: 'RETURN_RELABEL', currentStatus: 'INBOUND_COMPLETED' }, cargo: [], fees: [], statusHistory: [], operationHistory: [],
|
||
returnReceipt: { order: { returnNo: 'RT-1', receivedQuantity: 10, expectedQuantity: 10 }, receipts: [{ receiptNo: 'RR-1', unitType: 'PALLET', unitCode: 'PLT-1', receivedQuantity: 10, boxCount: 2, signedAt: '' }],
|
||
cartons: [{ receiptNo: 'RR-1', cartonMark: 'RC#2-1', countStatus: 'CONFIRMED', items: [{ skuCode: 'SKU-A', quantity: 3 }, { skuCode: 'SKU-B', quantity: 7 }] }, { receiptNo: 'RR-2', cartonMark: 'RC-PENDING', countStatus: 'PENDING', items: [] }] } })
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
await wrapper.findAll('[role="tab"]').find(tab => tab.text().includes('退货签收与箱内清点'))!.trigger('click'); await flushPromises()
|
||
const panel = wrapper.get('[aria-label="退货签收与箱内清点"]')
|
||
expect(panel.text()).toContain('实收 10 / 预报 10 件')
|
||
expect(panel.text()).toContain('RC#2-1')
|
||
expect(panel.text()).toContain('SKU-A')
|
||
expect(panel.text()).toContain('实际签收数量')
|
||
expect(panel.text()).toContain('待清点')
|
||
})
|
||
it('loads transfer fulfillment only on the plan tab using sourceOrderNo, never the tail order number', async () => {
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'internal-transfer-tail', customerId: 'customer-1', orderNo: 'WT-TAIL-1', sourceOrderNo: 'WT-SOURCE-1', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'INBOUND_COMPLETED' }, cargo: [], fees: [], statusHistory: [], operationHistory: [] })
|
||
vi.mocked(getWarehouseTransfer).mockResolvedValue({ id: 'internal-transfer', transferNo: 'WT-SOURCE-1', needRelabel: true, status: 'WAIT_RELABEL', relabelNo: 'RL-1', relabelStatus: 'CUSTOMER_CONFIRM_PENDING', packageCount: 2, items: [], cartons: [], pallets: [], relabelBindings: [] })
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
expect(getWarehouseTransfer).not.toHaveBeenCalled()
|
||
await wrapper.findAll('[role="tab"]').find(x => x.text().includes('计划信息'))!.trigger('click'); await flushPromises()
|
||
expect(getWarehouseTransfer).toHaveBeenCalledExactlyOnceWith('customer-1', 'WT-SOURCE-1')
|
||
expect(wrapper.get('[aria-label="本票转运与换标履约"]').text()).toContain('待客户确认')
|
||
expect(wrapper.get('[aria-label="本票转运与换标履约"]').text()).toContain('不得提前出库')
|
||
})
|
||
|
||
beforeEach(() => {
|
||
push.mockReset()
|
||
vi.mocked(getWarehouseTransfer).mockReset()
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({
|
||
order: { id: 'internal-order-id', customerId: 'customer-1', orderNo: 'FL-WH-001', currentStatus: 'PENDING_ASSIGNMENT', customerName: '示例客户', overseasWarehouseName: '洛杉矶仓', packageCount: 0, palletCount: 2, grossWeightKg: 30, netWeightKg: 22, volumeCbm: 6.374, inboundCode: 'IN-001', containerNo: 'CONT-001', customerRemark: '完整保留客户备注', updatedAt: '2026-09-14T10:00:00' },
|
||
cargo: [{ id: 'cargo-1', skuCode: 'SKU-001', productNameZh: '测试货物' }], fees: [], statusHistory: [], operationHistory: [],
|
||
})
|
||
})
|
||
|
||
it('shows one business heading, a semantic status and summary without losing basic fields', async () => {
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||
await flushPromises()
|
||
expect(wrapper.findAll('h1')).toHaveLength(1)
|
||
expect(wrapper.get('h1').text()).toBe('FL-WH-001')
|
||
const header = wrapper.get('.density-detail-header')
|
||
expect(header.text()).not.toContain('尾程订单详情')
|
||
expect(header.text()).not.toContain('尾程单号')
|
||
expect(header.get('.status-badge').classes()).toContain('is-warning')
|
||
expect(header.get('.order-reference-text__copy').classes()).toContain('is-text')
|
||
const summary = wrapper.get('[aria-label="尾程订单概览"]')
|
||
expect(summary.findAll('dd').map(item => item.text())).toEqual(['示例客户', '洛杉矶仓', '0 箱', '2 托', '30 kg', '22 kg', '6.374 m³', expect.stringContaining('2026-09-14')])
|
||
const fields = wrapper.get('dl[aria-label="基本信息"]')
|
||
expect(fields.findAll('dt')).toHaveLength(22)
|
||
expect(fields.text()).toContain('货物毛重(kg)30')
|
||
expect(fields.text()).toContain('货物净重(kg)22')
|
||
expect(fields.text()).toContain('CONT-001')
|
||
expect(fields.text()).toContain('完整保留客户备注')
|
||
expect(fields.findAll('.wide')).toHaveLength(3)
|
||
expect(wrapper.text()).not.toContain('internal-order-id')
|
||
await header.get('.density-back').trigger('click')
|
||
expect(push).toHaveBeenCalledWith({ name: 'overseas-last-mile-orders' })
|
||
})
|
||
|
||
it('keeps all five tab panels reachable after applying the shared tab style', async () => {
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||
await flushPromises()
|
||
const tabs = wrapper.findAll('[role="tab"]')
|
||
expect(tabs.map(tab => tab.text())).toEqual(['📋基本信息', '📦货物信息', '🗓️计划信息', '💰费用明细', '📝操作记录'])
|
||
for (const [index, tab] of tabs.entries()) {
|
||
await tab.trigger('click')
|
||
await flushPromises()
|
||
expect(tab.attributes('aria-selected')).toBe('true')
|
||
const panel = wrapper.findAll('[role="tabpanel"]')[index]!
|
||
expect(panel.isVisible()).toBe(true)
|
||
if (index === 1) expect(panel.text()).toContain('SKU-001')
|
||
}
|
||
})
|
||
|
||
it('shows declared, received and highlighted signed difference only for warehouse transfers', async () => {
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({
|
||
order: { id: 'transfer-id', customerId: 'customer-1', orderNo: 'WT-001', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'WAIT_CUSTOMER_CONFIRMATION', packageCount: 10, receivedPackageCount: 12, packageDifference: 2 },
|
||
cargo: [], fees: [], statusHistory: [], operationHistory: [],
|
||
})
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||
await flushPromises()
|
||
const fields = wrapper.get('dl[aria-label="基本信息"]')
|
||
expect(fields.text()).toContain('客户预报箱数10')
|
||
expect(fields.text()).toContain('实收箱数12')
|
||
expect(fields.text()).toContain('箱数差异+2')
|
||
expect(fields.get('.package-difference--nonzero').text()).toBe('+2')
|
||
expect(wrapper.get('.status-badge').text()).toBe('待客户确认实测')
|
||
const overview = wrapper.get('[aria-label="尾程订单概览"]')
|
||
expect(overview.text()).toContain('实收总箱数12 箱')
|
||
expect(overview.text()).not.toContain('客户预报总箱数')
|
||
})
|
||
|
||
it('shows the declared total before a warehouse transfer has a received count', async () => {
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({
|
||
order: { id: 'transfer-id', customerId: 'customer-1', orderNo: 'WT-002', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'WAIT_PDA_RECEIVING', packageCount: 10, receivedPackageCount: null, packageDifference: null },
|
||
cargo: [], fees: [], statusHistory: [], operationHistory: [],
|
||
})
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||
await flushPromises()
|
||
const overview = wrapper.get('[aria-label="尾程订单概览"]')
|
||
expect(overview.text()).toContain('客户预报总箱数10 箱')
|
||
expect(overview.text()).not.toContain('实收总箱数')
|
||
})
|
||
|
||
it('renders both return relabel documents with independent states and quantities', async () => {
|
||
vi.mocked(getLastMileOrder).mockResolvedValue({
|
||
order: {
|
||
id: 'return-id', customerId: 'customer-1', orderNo: 'RT-001', orderType: 'RETURN_RELABEL', currentStatus: 'INBOUND_COMPLETED',
|
||
relabelOrderNo: 'RL-SECOND', relabelStatus: 'PROCESSING',
|
||
relabelApplications: [
|
||
{ relabelNo: 'RL-FIRST', status: 'COMPLETED', quantity: 10, createdAt: '2026-09-21T10:00:00' },
|
||
{ relabelNo: 'RL-SECOND', status: 'PROCESSING', quantity: 5, createdAt: '2026-09-21T11:00:00' },
|
||
],
|
||
},
|
||
cargo: [], fees: [], statusHistory: [], operationHistory: [],
|
||
})
|
||
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||
await flushPromises()
|
||
await wrapper.get('#tab-plan').trigger('click')
|
||
await flushPromises()
|
||
const rows = wrapper.findAll('.related-panel .el-table__body tbody tr')
|
||
expect(rows).toHaveLength(2)
|
||
expect(rows[0].text()).toContain('RL-FIRST')
|
||
expect(rows[0].text()).toContain('已完成')
|
||
expect(rows[0].text()).toContain('10')
|
||
expect(rows[1].text()).toContain('RL-SECOND')
|
||
expect(rows[1].text()).toContain('处理中')
|
||
expect(rows[1].text()).toContain('5')
|
||
})
|
||
})
|