import { flushPromises, mount } from '@vue/test-utils' import { beforeEach, expect, it, vi } from 'vitest' import WorkSharedTripForm from './WorkSharedTripForm.vue' import { getDispatch } from '@/modules/overseas-warehouse' import type { LeaveGuard } from './useWorkDraft' const access = vi.hoisted(() => ({ allowed: true })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => access.allowed }) })) vi.mock('@/modules/overseas-warehouse', async original => ({ ...await original(), getDispatch: vi.fn() })) vi.mock('element-plus', () => ({ ElMessage: { warning: vi.fn() } })) let guard: LeaveGuard function render() { return mount(WorkSharedTripForm, { props: { dispatchNo: 'DS-60', canWrite: true }, global: { provide: { 'appointment-work-leave-guard': (value: LeaveGuard) => { guard = value; return () => undefined } }, stubs: { ElButton: { props: ['disabled'], template: '' }, TripPreparationDialog: { name: 'TripPreparationDialog', props: ['modelValue', 'warehouseId', 'dispatchNos'], template: '
' }, LoadErrorState: { props: ['message'], template: '

{{ message }}

' }, ApiErrorMeta: true, } } }) } beforeEach(() => { vi.clearAllMocks(); access.allowed = true; vi.mocked(getDispatch).mockImplementation(async number => ({ id: `internal-${number}`, dispatchNo: number, appointmentBatches: [], customerId: number === 'DS-60' ? 'customer-b' : 'customer-d', overseasWarehouseId: 'warehouse-real', overseasWarehouseName: '实际起运仓', packageCount: number === 'DS-60' ? 60 : 40, status: 'PENDING_DEPARTURE' })) }) it('checks exact 60 and 40 dispatch business numbers and passes both to the existing Fleet preparation before navigating to its real trip', async () => { const wrapper = render() expect(getDispatch).not.toHaveBeenCalled() await wrapper.get('input').setValue('DS-40'); await wrapper.get('button').trigger('click'); await flushPromises() expect(vi.mocked(getDispatch).mock.calls.map(call => call[0])).toEqual(['DS-60', 'DS-40']) const prepare = wrapper.getComponent({ name: 'TripPreparationDialog' }) expect(prepare.props()).toMatchObject({ modelValue: true, warehouseId: 'warehouse-real', dispatchNos: ['DS-60', 'DS-40'] }) expect(wrapper.text()).toContain('60 箱'); expect(wrapper.text()).toContain('40 箱'); expect(wrapper.text()).not.toContain('internal-') expect(await guard()).toBe(false) prepare.vm.$emit('saved', { trip: { tripNo: 'TR-SHARED' } }); await flushPromises() expect(wrapper.emitted('external')).toEqual([['/overseas-master-data/trips/TR-SHARED']]); expect(wrapper.emitted('updated')).toHaveLength(1) wrapper.unmount() }) it('rejects duplicate, cross-warehouse, already-bound and mismatched dispatches without opening Fleet preparation', async () => { const wrapper = render(); await wrapper.get('input').setValue('DS-60'); await wrapper.get('button').trigger('click'); await flushPromises() expect(getDispatch).not.toHaveBeenCalled() for (const change of [{ overseasWarehouseId: 'another-warehouse' }, { workflow: { tripNo: 'TR-OTHER', dispatchReview: { status: 'PENDING' as const, reason: null, reviewedAt: null }, loadingReview: { status: 'PENDING' as const, reason: null, reviewedAt: null } } }, { status: 'SHIPPED' }, { dispatchNo: 'WRONG' }]) { vi.mocked(getDispatch).mockResolvedValueOnce({ id: 'a', dispatchNo: 'DS-60', appointmentBatches: [], overseasWarehouseId: 'warehouse-real', status: 'PENDING_DEPARTURE' }).mockResolvedValueOnce({ id: 'b', dispatchNo: 'DS-40', appointmentBatches: [], overseasWarehouseId: 'warehouse-real', status: 'PENDING_DEPARTURE', ...change }) await wrapper.get('input').setValue('DS-40'); await wrapper.get('button').trigger('click'); await flushPromises() expect(wrapper.findComponent({ name: 'TripPreparationDialog' }).exists()).toBe(false); expect(wrapper.find('[role="alert"]').exists()).toBe(true) } wrapper.unmount() }) it('keeps a failed lookup retry read-only and respects original permissions', async () => { const wrapper = render(); vi.mocked(getDispatch).mockRejectedValueOnce(new Error('读取失败')) await wrapper.get('input').setValue('DS-40'); await wrapper.get('button').trigger('click'); await flushPromises() expect(wrapper.findComponent({ name: 'TripPreparationDialog' }).exists()).toBe(false) await wrapper.get('button').trigger('click'); await flushPromises() expect(getDispatch).toHaveBeenCalledTimes(4); expect(wrapper.getComponent({ name: 'TripPreparationDialog' }).props('dispatchNos')).toEqual(['DS-60', 'DS-40']) wrapper.unmount(); access.allowed = false const readonly = render(); await readonly.get('button').trigger('click'); expect(getDispatch).toHaveBeenCalledTimes(4); readonly.unmount() })