49 lines
4.7 KiB
TypeScript
49 lines
4.7 KiB
TypeScript
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<typeof import('@/modules/overseas-warehouse')>(), 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: '<button :disabled="disabled"><slot /></button>' },
|
|
TripPreparationDialog: { name: 'TripPreparationDialog', props: ['modelValue', 'warehouseId', 'dispatchNos'], template: '<div />' },
|
|
LoadErrorState: { props: ['message'], template: '<p role="alert">{{ message }}</p>' }, 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()
|
|
})
|