import { createPinia } from 'pinia' import { shallowMount, flushPromises } from '@vue/test-utils' import { beforeEach, expect, it, vi } from 'vitest' import TripPreparationDialog from './TripPreparationDialog.vue' import { listTrips, prepareTrip } from '../api/trips' import type { TripDetail, FleetTrip } from '../tripTypes' vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) })) vi.mock('../api/trips', () => ({ listTrips: vi.fn(), prepareTrip: vi.fn() })) vi.mock('element-plus', () => ({ ElMessage: { success: vi.fn(), warning: vi.fn() } })) const trip = { tripNo: 'TR-READY', recordVersion: 4, warehouseId: '9007199254740993', stops: [{ name: '起运仓' }, { name: 'ACY2' }] } as FleetTrip beforeEach(() => { vi.clearAllMocks(); vi.mocked(listTrips).mockResolvedValue({ records: [trip], page: 1, size: 100, total: 1, pages: 1 }) }) function render() { const field = { props: ['modelValue'], emits: ['update:modelValue', 'focus'], template: '
' } return shallowMount(TripPreparationDialog, { props: { modelValue: true, warehouseId: trip.warehouseId, warehouseName: '本仓', dispatchNos: ['DS-ONE', 'DS-TWO'] }, global: { plugins: [createPinia()], stubs: { AppDialog: { name: 'AppDialog', props: ['showConfirm'], emits: ['confirm'], template: '
' }, ElForm: field, ElFormItem: field, ElSelect: { ...field, name: 'ElSelect' }, ElOption: true, ElRadioGroup: { ...field, name: 'ElRadioGroup' }, ElRadio: true, ElDatePicker: field, ElButton: { template: '' }, CapacityOfferSettingsDialog: true, } } }) } it('loads same-warehouse trips on demand and retries one immutable atomic preparation', async () => { const wrapper=render(); await flushPromises() expect(listTrips).not.toHaveBeenCalled() const select=wrapper.findComponent({ name: 'ElSelect' }) select.vm.$emit('focus'); await flushPromises() expect(listTrips).toHaveBeenCalledWith(expect.objectContaining({ warehouseId: '9007199254740993', status: 'PLANNED' })) select.vm.$emit('update:modelValue', trip.tripNo); await flushPromises() vi.mocked(prepareTrip).mockRejectedValueOnce(new Error('network')).mockResolvedValueOnce({ trip, dispatches: [], driverQualification: null, previousDispatches: [], appointmentHistoryUnavailableReason: null, loadingReview: { status: 'PENDING', reason: null, reviewerId: null, reviewedAt: null, recordVersion: null } } as TripDetail) const dialog=wrapper.findComponent({ name: 'AppDialog' }) dialog.vm.$emit('confirm'); await flushPromises() const original=vi.mocked(prepareTrip).mock.calls[0]![0] expect(original).toMatchObject({ tripNo: 'TR-READY', recordVersion: 4, dispatchNos: ['DS-ONE','DS-TWO'], plan: null, vehicleId: null, driverId: null }) expect(wrapper.emitted('saved')).toBeUndefined() dialog.vm.$emit('confirm'); await flushPromises() expect(prepareTrip).toHaveBeenNthCalledWith(2, original) expect(wrapper.emitted('saved')).toHaveLength(1) expect(wrapper.text()).toContain('核对容量并发布余位') expect(dialog.props('showConfirm')).toBe(false) wrapper.unmount() })