2026-09-01 19:57:36 +08:00
|
|
|
import ElementPlus from 'element-plus'
|
|
|
|
|
import { flushPromises, mount } from '@vue/test-utils'
|
|
|
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
|
import { getDispatch } from '../api/dispatches'
|
|
|
|
|
import DispatchDetailView from './DispatchDetailView.vue'
|
2026-10-19 17:56:31 +08:00
|
|
|
import { detail, destination } from '../dispatchTestFixtures'
|
2026-09-01 19:57:36 +08:00
|
|
|
import { DataTableStub } from './phase5TestStubs'
|
2026-10-19 17:56:31 +08:00
|
|
|
const query = vi.hoisted(() => ({} as Record<string, string>))
|
|
|
|
|
const push = vi.hoisted(() => vi.fn())
|
|
|
|
|
const can = vi.hoisted(() => vi.fn(() => false))
|
|
|
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) }))
|
|
|
|
|
vi.mock('vue-router', () => ({ useRoute: () => ({ params: { dispatchNo: 'PC/1' }, query }), useRouter: () => ({ push }) }))
|
2026-09-01 19:57:36 +08:00
|
|
|
vi.mock('../api/dispatches', () => ({ getDispatch: vi.fn() }))
|
2026-10-19 17:56:31 +08:00
|
|
|
vi.mock('@/modules/overseas-master-data', () => ({ inquiryReturnTarget: (value: Record<string, string>) => value.fleetOfferNo ? { name: 'overseas-master-data-capacity-offer-detail', params: { offerNo: value.fleetOfferNo }, query: { inquiry: value.fleetBookingNo } } : null }))
|
|
|
|
|
const options = { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub, DispatchWorkflowDialog: true } } }
|
|
|
|
|
beforeEach(() => { vi.mocked(getDispatch).mockResolvedValue(detail()); can.mockReturnValue(false); push.mockReset(); Object.keys(query).forEach(key => delete query[key]) })
|
|
|
|
|
describe('whole-dispatch four-tab detail', () => {
|
|
|
|
|
it('uses only four accessible colored tabs and switches without another network read', async () => {
|
2026-10-20 10:11:31 +08:00
|
|
|
const value = detail(); value.dispatchInfo.fbaAppointments = [{ batchKey: 'FBA_APPOINTMENT:1', batchNo: 'BATCH-1', appointmentNo: 'FBA-AP-1', destinationKey: 'FBA:ONT8', destinationWarehouseCode: 'ONT8', status: 'CONFIRMED', confirmedAt: null, blockReason: null }]
|
|
|
|
|
vi.mocked(getDispatch).mockResolvedValueOnce(value)
|
2026-10-19 17:56:31 +08:00
|
|
|
const wrapper = mount(DispatchDetailView, options); await flushPromises()
|
2026-09-01 19:57:36 +08:00
|
|
|
expect(getDispatch).toHaveBeenCalledWith('PC/1')
|
2026-10-19 17:56:31 +08:00
|
|
|
const tabs = wrapper.findAll('[role="tab"]')
|
|
|
|
|
expect(tabs.map(tab => tab.text())).toEqual(['🚚车辆信息', '📋排车信息', '📝单据信息', '📦关联订单明细'])
|
|
|
|
|
expect(wrapper.findAll('.density-tab-icon')).toHaveLength(4)
|
|
|
|
|
await tabs[1]!.trigger('click'); await flushPromises()
|
|
|
|
|
expect(tabs[1]!.attributes('aria-selected')).toBe('true')
|
2026-10-20 10:11:31 +08:00
|
|
|
const dispatchInfo = wrapper.get(`#${tabs[1]!.attributes('aria-controls')}`)
|
|
|
|
|
expect(dispatchInfo.text()).toContain('LAX → ONT8'); expect(dispatchInfo.text()).toContain('FBA预约'); expect(dispatchInfo.text()).toContain('ONT8 · FBA-AP-1 · CONFIRMED')
|
|
|
|
|
expect(dispatchInfo.findAll('dt').map(field => field.text())).toEqual(['路线', '发车时间', '预计到达时间', 'FBA预约'])
|
2026-10-19 17:56:31 +08:00
|
|
|
expect(getDispatch).toHaveBeenCalledTimes(1); expect(wrapper.text()).toContain('装车前'); wrapper.unmount()
|
2026-09-01 19:57:36 +08:00
|
|
|
})
|
2026-10-19 17:56:31 +08:00
|
|
|
it('uses exact server order attribution including unknown measurements and real container numbers', async () => {
|
|
|
|
|
const value = detail(); value.orderDetails.push({ ...value.orderDetails[0]!, customerId: 'C2', customerName: '客户乙', sourceKey: 'source-2', orderNo: 'LM-2', grossWeightKg: 12.25 })
|
2026-10-20 10:11:31 +08:00
|
|
|
value.orderDetails.forEach((row, index) => { row.sourceBusinessType = 'RELABEL_ORDER'; row.sourceBusinessNo = `RL-${index + 1}` })
|
2026-10-19 17:56:31 +08:00
|
|
|
vi.mocked(getDispatch).mockResolvedValueOnce(value)
|
|
|
|
|
const wrapper = mount(DispatchDetailView, options); await flushPromises()
|
|
|
|
|
expect(wrapper.getComponent(DataTableStub).props('data')).toEqual(value.orderDetails)
|
2026-10-20 10:11:31 +08:00
|
|
|
expect(wrapper.getComponent(DataTableStub).vm.$attrs.columns).toContainEqual(expect.objectContaining({ prop: 'sourceBusinessNo', label: '来源业务号' }))
|
2026-10-19 17:56:31 +08:00
|
|
|
expect(value.orderDetails[0]!.grossWeightKg).toBeNull(); wrapper.unmount()
|
2026-10-03 04:35:55 +08:00
|
|
|
})
|
2026-10-19 17:56:31 +08:00
|
|
|
it('renders all customer delivery references and real update time/remark without leaking internal IDs', async () => {
|
|
|
|
|
const value = detail(); value.documents.deliveries = ['DEL-A', 'DEL-B'].map((deliveryNo, index) => ({ deliveryNo, customerId: `C${index}`, customerName: `客户${index}`, outboundNo: `OUT-${index}`, groupKey: `group-${index}`, destination: destination(), status: 'WAIT_DELIVERY', packageCount: 1, palletCount: 1 }))
|
|
|
|
|
vi.mocked(getDispatch).mockResolvedValueOnce(value)
|
|
|
|
|
const wrapper = mount(DispatchDetailView, options); await flushPromises(); await wrapper.findAll('[role="tab"]')[2]!.trigger('click')
|
|
|
|
|
expect(wrapper.text()).toContain('DEL-A'); expect(wrapper.text()).toContain('DEL-B'); expect(wrapper.text()).toContain('真实备注')
|
|
|
|
|
expect(wrapper.findAll('button').some(button => button.text() === 'DEL-A')).toBe(false); wrapper.unmount()
|
2026-10-18 16:37:37 +08:00
|
|
|
})
|
2026-10-19 17:56:31 +08:00
|
|
|
it('requires a literal service allowance and permission for a write action', async () => {
|
|
|
|
|
can.mockReturnValue(true); vi.mocked(getDispatch).mockResolvedValueOnce(detail({ actions: { DEPART: { allowed: false, blockCode: 'NOT_ARRANGED', blockReason: '尚未安排派送' } } }))
|
|
|
|
|
const wrapper = mount(DispatchDetailView, options); await flushPromises()
|
|
|
|
|
expect(wrapper.text()).toContain('尚未安排派送'); expect(wrapper.findAll('button').some(button => button.text() === '确认发车')).toBe(false); wrapper.unmount()
|
|
|
|
|
})
|
|
|
|
|
it('retains a real API failure and exposes retry rather than rendering a successful empty detail', async () => {
|
|
|
|
|
vi.mocked(getDispatch).mockRejectedValueOnce(new Error('permission denied'))
|
|
|
|
|
const wrapper = mount(DispatchDetailView, options); await flushPromises(); expect(wrapper.text()).toContain('排车详情加载失败,请重试'); expect(wrapper.findAll('[role="tab"]')).toHaveLength(0); wrapper.unmount()
|
|
|
|
|
})
|
|
|
|
|
it('keeps the original inquiry return target', async () => {
|
|
|
|
|
Object.assign(query, { fleetOfferNo: 'FO-1', fleetBookingNo: 'FB-2' }); const wrapper = mount(DispatchDetailView, options); await flushPromises(); await wrapper.get('button.density-back').trigger('click')
|
|
|
|
|
expect(push).toHaveBeenCalledWith({ name: 'overseas-master-data-capacity-offer-detail', params: { offerNo: 'FO-1' }, query: { inquiry: 'FB-2' } }); wrapper.unmount()
|
2026-10-11 15:33:09 +08:00
|
|
|
})
|
2026-09-01 19:57:36 +08:00
|
|
|
})
|