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' import { DataTableStub } from './phase5TestStubs' const sourceQuery = vi.hoisted(() => ({} as Record)) const routerPush = vi.hoisted(() => vi.fn()) const canPermission = vi.hoisted(() => vi.fn(() => false)) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) })) vi.mock('vue-router', () => ({ useRoute: () => ({ params: { dispatchNo: 'PC/1' }, query: sourceQuery }), useRouter: () => ({ push: routerPush }) })) vi.mock('../api/dispatches', () => ({ getDispatch: vi.fn() })) describe('DispatchDetailView', () => { beforeEach(() => { routerPush.mockReset() canPermission.mockReset().mockReturnValue(false) Object.keys(sourceQuery).forEach(key => delete sourceQuery[key]) vi.mocked(getDispatch).mockReset().mockResolvedValue({ id: '1', dispatchNo: 'PC/1', appointmentBatches: [], vehiclePlate: 'CA-100', driverName: '张师傅', status: 'PENDING_DEPARTURE', route: 'LAX → ONT8', items: [{ id: '11', planPalletId: '20', orderNo: 'LM-1', warehousePalletId: '21', fbaWarehouseCode: 'ONT8', grossWeightKg: 88.5, volumeCbm: 1.25 }], }) }) it('loads the business dispatch number and renders summary and related orders', async () => { const wrapper = mount(DispatchDetailView, { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub } } }) await flushPromises() expect(getDispatch).toHaveBeenCalledWith('PC/1') expect(wrapper.text()).toContain('PC/1') expect(wrapper.text()).toContain('CA-100') expect(wrapper.text()).toContain('待发车') const table = wrapper.findComponent(DataTableStub) expect(table.props('data')).toEqual([{ orderNo: 'LM-1', weight: 88.5, palletCount: 1, fbaWarehouse: 'ONT8', volume: 1.25, containerNo: '—' }]) wrapper.unmount() }) it('keeps related order weight unknown when one pallet lacks physical weight', async () => { vi.mocked(getDispatch).mockResolvedValueOnce({ id: '1', dispatchNo: 'PC/1', appointmentBatches: [], status: 'PENDING_DEPARTURE', items: [ { id: '11', planPalletId: '31', orderNo: 'LM-1', warehousePalletId: '21', grossWeightKg: 88.5, volumeCbm: 1 }, { id: '12', planPalletId: '32', orderNo: 'LM-1', warehousePalletId: '22', grossWeightKg: null, volumeWeightKg: 999, volumeCbm: 1 }, ] }) const wrapper = mount(DispatchDetailView, { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub } } }) await flushPromises() expect(wrapper.findComponent(DataTableStub).props('data')).toEqual([expect.objectContaining({ weight: null })]) wrapper.unmount() }) it('shows server blocking reasons and opens only authorized original documents', async () => { const data = await getDispatch('PC/1') data.actions = { canEdit: false, editBlockReason: '请先解除车次关联', canCancel: false, cancelBlockReason: '请先解除车次关联', canRemovePallet: false, removePalletBlockReason: '请先解除车次关联', canTransfer: false, transferBlockReason: '原出库已取消,请取消原排车后重新预排', outboundNo: 'OUT-1', outboundStatus: 'CANCELLED' } data.workflow = { tripNo: 'TR-1', dispatchReview: { status: 'PENDING', reason: null, reviewedAt: null }, loadingReview: { status: 'PENDING', reason: null, reviewedAt: null } } vi.mocked(getDispatch).mockResolvedValue(data) canPermission.mockReturnValue(true) const wrapper = mount(DispatchDetailView, { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub } } }); await flushPromises() expect(wrapper.text()).toContain('原出库已取消') await wrapper.findAll('button').find(button => button.text().includes('处理原车次'))!.trigger('click') expect(routerPush).toHaveBeenLastCalledWith({ name: 'overseas-master-data-trip-detail', params: { tripNo: 'TR-1' }, query: { tab: 'overview' } }) await wrapper.findAll('button').find(button => button.text().includes('查看原出库'))!.trigger('click') expect(routerPush).toHaveBeenLastCalledWith({ name: 'overseas-outbounds', query: { documentNo: 'OUT-1' } }) wrapper.unmount() canPermission.mockReturnValue(false) const readonly = mount(DispatchDetailView, { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub } } }); await flushPromises() expect(readonly.findAll('button').some(button => ['处理原车次', '查看原出库'].some(label => button.text().includes(label)))).toBe(false) expect(readonly.text()).toContain('原出库已取消') readonly.unmount() }) it.each([false, true])('returns to the original inquiry only for a valid source: %s', async fromInquiry => { if (fromInquiry) Object.assign(sourceQuery, { fleetSource: 'inquiry', fleetOfferNo: 'FO-1', fleetBookingNo: 'FB-2' }) const wrapper = mount(DispatchDetailView, { global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub } } }) await flushPromises() const backButton = wrapper.get('button.density-back') expect(backButton.text()).toBe(fromInquiry ? '返回原拼车登记' : '返回列表') await backButton.trigger('click') expect(routerPush).toHaveBeenCalledWith(fromInquiry ? { name: 'overseas-master-data-capacity-offer-detail', params: { offerNo: 'FO-1' }, query: { inquiry: 'FB-2' } } : { name: 'overseas-dispatches' }) wrapper.unmount() }) })