Files
admin-web/src/modules/overseas-master-data/components/TripAssignmentPanel.test.ts
T
2026-10-19 10:23:38 +08:00

56 lines
4.9 KiB
TypeScript

import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { flushPromises, shallowMount } from '@vue/test-utils'
import TripAssignmentPanel from './TripAssignmentPanel.vue'
import { listVehicles } from '../api/overseasMasterData'
import { listDrivers } from '../api/drivers'
import { assignTrip, getTripResourceOccupancy } from '../api/trips'
import type { TripDetail } from '../tripTypes'
import type { VehicleDetail } from '../types'
import type { DriverDetail } from '../driverTypes'
vi.mock('element-plus', () => ({ ElMessage: { warning: vi.fn(), success: vi.fn() } }))
vi.mock('../api/overseasMasterData', () => ({ listVehicles: vi.fn() }))
vi.mock('../api/drivers', () => ({ listDrivers: vi.fn() }))
vi.mock('../api/trips', () => ({ assignTrip: vi.fn(), getTripResourceOccupancy: vi.fn() }))
const vehicle: VehicleDetail = { id: '9007199254740999', plateNumber: 'TEST-FREE', model: '26ft', maxPalletSpaces: 12, maxLoadKg: 8000, maxVolumeCbm: 45, fuelConsumptionPer100Km: 20, mileageKm: 0, driverOrDispatchCompany: null, phone: null, region: 'OVERSEAS', ownership: 'OWNED', status: 'IN_SERVICE', remark: null, recordVersion: 0, updatedBy: '1', updatedAt: '' }
const driver: DriverDetail = { id: '9007199254740998', driverNo: 'DR-TEST', details: { name: '验收司机', phone: '000', ownership: 'OWNED', region: 'OVERSEAS', licenseNumber: null, licenseType: null, licenseIssuedOn: null, licenseExpiresOn: '2030-12-31', licenseZone: 'UTC', remark: null, profile: null }, active: true, reviewStatus: 'APPROVED', licenseFileId: '1', recordVersion: 0, updatedBy: '1', updatedAt: '', qualification: { canDispatch: true, expiringSoon: false, reason: 'ELIGIBLE', licenseExpiresOn: '2030-12-31' }, licenseState: 'VALID', warehouseCode: null, warehouseName: null }
const trip: TripDetail = { trip: { tripNo: 'TR-CURRENT', status: 'PLANNED', warehouseId: '1', warehouseCode: 'WH', warehouseName: '仓库', vehicleId: null, vehiclePlate: null, driverId: null, driverName: null, driverPhone: null, maxPalletSpaces: null, maxLoadKg: null, maxVolumeCbm: null, plannedDepartureAt: '2026-10-18T01:00:00Z', expectedDriverEndAt: '2026-10-18T09:00:00Z', departedAt: null, completedAt: null, stops: [], remark: null, recordVersion: 1, updatedAt: '', vehicleRelease: null, driverRelease: null }, driverQualification: null, dispatches: [], loadingReview: { status: 'PENDING', reason: null, reviewerId: null, reviewedAt: null, recordVersion: null }, previousDispatches: [], appointmentHistoryUnavailableReason: null }
function mount() {
return shallowMount(TripAssignmentPanel, { props: { detail: trip }, global: { stubs: {
ElForm: { template: '<form><slot/></form>' }, ElFormItem: { template: '<div><slot/></div>' },
ElSelect: { name: 'ElSelect', props: ['modelValue'], emits: ['focus', 'update:modelValue'], template: '<div><slot/></div>' },
ElOption: { name: 'ElOption', props: ['value', 'label', 'disabled'], template: '<span>{{ label }}</span>' },
ElButton: { template: '<button><slot/></button>' },
} } })
}
beforeEach(() => {
vi.useFakeTimers(); vi.clearAllMocks()
vi.mocked(listVehicles).mockResolvedValue({ records: [vehicle], page: 1, size: 30, total: 1, pages: 1 })
vi.mocked(listDrivers).mockResolvedValue({ records: [driver], page: 1, size: 30, total: 1, pages: 1, summary: { total: 1, owned: 1, dispatched: 0, expiringSoon: 0, expired: 0 } })
})
afterEach(() => vi.useRealTimers())
it('disables resources held by another trip and shows the occupying trip before saving', async () => {
vi.mocked(getTripResourceOccupancy).mockImplementation(async (_, kind, ids) => ids.map(resourceId => ({ resourceId, occupiedTripNo: kind === 'VEHICLE' ? 'TR-OTHER' : 'TR-CURRENT', available: kind === 'DRIVER' })))
const wrapper = mount()
const selects = wrapper.findAllComponents({ name: 'ElSelect' })
selects.forEach(select => select.vm.$emit('focus')); await vi.advanceTimersByTimeAsync(250); await flushPromises()
const options = wrapper.findAllComponents({ name: 'ElOption' })
expect(options[0]!.props()).toMatchObject({ value: vehicle.id, disabled: true })
expect(options[0]!.props('label')).toContain('被车次 TR-OTHER 占用')
expect(options[1]!.props('disabled')).toBe(false)
expect(options[1]!.props('label')).toContain('当前车次')
expect(getTripResourceOccupancy).toHaveBeenCalledTimes(2)
selects[0]!.vm.$emit('update:modelValue', vehicle.id); selects[1]!.vm.$emit('update:modelValue', driver.id); await flushPromises()
await wrapper.find('button').trigger('click'); expect(assignTrip).not.toHaveBeenCalled()
wrapper.unmount()
})
it('does not expose directory candidates as free when occupancy lookup fails', async () => {
vi.mocked(getTripResourceOccupancy).mockRejectedValue(new Error('查询占用失败'))
const wrapper = mount(); wrapper.findComponent({ name: 'ElSelect' }).vm.$emit('focus')
await vi.advanceTimersByTimeAsync(250); await flushPromises()
expect(wrapper.findAllComponents({ name: 'ElOption' })).toHaveLength(0)
wrapper.unmount()
})