84 lines
9.7 KiB
TypeScript
84 lines
9.7 KiB
TypeScript
import ElementPlus from 'element-plus'
|
||
import { flushPromises, mount } from '@vue/test-utils'
|
||
import { beforeEach, expect, it, vi } from 'vitest'
|
||
import DispatchResourceFields from './DispatchResourceFields.vue'
|
||
import { resources } from '../dispatchTestFixtures'
|
||
import { listDrivers, listVehicles, type DriverDetail, type VehicleDetail } from '@/modules/overseas-master-data'
|
||
const can = vi.hoisted(() => vi.fn(() => true))
|
||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) }))
|
||
vi.mock('@/modules/overseas-master-data', () => ({ listDrivers: vi.fn(), listVehicles: vi.fn(), overseasMasterDataPermissions: { vehicleList: 'vehicle:list', driverList: 'driver:list' } }))
|
||
beforeEach(() => { vi.clearAllMocks(); can.mockReturnValue(true); vi.mocked(listVehicles).mockResolvedValue({ records: [], page: 1, size: 100, total: 0, pages: 0 }) })
|
||
it('carries the selected master license for preview without carrying another driver’s trip photos', async () => {
|
||
const candidate: DriverDetail = { id: '22', driverNo: 'DR22', details: { name: '司机乙', phone: '555-0022', ownership: 'OWNED', region: 'OVERSEAS', licenseNumber: 'LICENSE22', licenseType: null, licenseIssuedOn: null, licenseExpiresOn: '2028-01-01', licenseZone: 'UTC', remark: null, profile: null }, active: true, reviewStatus: 'APPROVED', licenseFileId: '33', recordVersion: 1, updatedBy: 'operator', updatedAt: '2026-10-23T00:00:00Z', qualification: { canDispatch: true, expiringSoon: false, reason: 'ELIGIBLE', licenseExpiresOn: '2028-01-01' }, licenseState: 'VALID', warehouseCode: null, warehouseName: null }
|
||
vi.mocked(listDrivers).mockResolvedValue({ records: [candidate], page: 1, size: 100, total: 1, pages: 1, summary: { total: 1, owned: 1, dispatched: 0, expiringSoon: 0, expired: 0 } })
|
||
const value = resources()
|
||
const wrapper = mount(DispatchResourceFields, { props: { ...value, disabled: false, dispatchNo: 'PC-OWN' }, global: { plugins: [ElementPlus] } })
|
||
const state = wrapper.vm as unknown as { searchDrivers(): Promise<void>; selectDriver(id: string): void }
|
||
await state.searchDrivers(); expect(listDrivers).toHaveBeenCalledWith(expect.objectContaining({ dispatchNo: 'PC-OWN' })); state.selectDriver('22'); await flushPromises()
|
||
expect(wrapper.emitted('update:driver')!.at(-1)![0]).toMatchObject({ driverId: '22', evidence: { fileIds: [], masterLicenseFileId: '33' } })
|
||
wrapper.unmount()
|
||
})
|
||
it.each([['本趟承运公司', '555-0123'], [null, null]])('shows continuous master snapshot fields read-only and uses a dash for missing facts (%s)', async (carrierName, phone) => {
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = 'vehicle-1'; value.vehicle.carrierName = carrierName; value.driver.mode = 'MASTER'; value.driver.driverId = 'driver-1'; value.driver.phone = phone
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous: true }, global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
const field = (label: string) => wrapper.findAllComponents({ name: 'ElFormItem' }).find(item => item.props('label') === label)!.getComponent({ name: 'ElInput' })
|
||
expect(field('拖车公司名称').props('modelValue')).toBe(carrierName || '—'); expect(field('拖车公司名称').props('readonly')).toBe(true)
|
||
expect(field('联系电话').props('modelValue')).toBe(phone || '—'); expect(field('联系电话').props('readonly')).toBe(true)
|
||
expect(listVehicles).toHaveBeenCalled(); expect(listDrivers).not.toHaveBeenCalled()
|
||
expect(wrapper.emitted('update:vehicle')).toBeUndefined(); expect(wrapper.emitted('update:driver')).toBeUndefined(); wrapper.unmount()
|
||
})
|
||
it('keeps master company and phone fields out of the existing non-continuous creation form', async () => {
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.driver.mode = 'MASTER'
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false }, global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
const labels = wrapper.findAllComponents({ name: 'ElFormItem' }).map(item => item.props('label'))
|
||
expect(labels).not.toContain('拖车公司名称'); expect(labels).not.toContain('联系电话'); wrapper.unmount()
|
||
})
|
||
it('shows the newly selected authorized vehicle company without copying it into the master command', async () => {
|
||
const candidate: VehicleDetail = { id: 'vehicle-2', plateNumber: 'CA-200', model: '厢式货车', driverOrDispatchCompany: '新承运公司', phone: null, maxPalletSpaces: 20, maxLoadKg: 18000, maxVolumeCbm: 60, fuelConsumptionPer100Km: 0, mileageKm: 0, region: 'OVERSEAS', ownership: 'OWNED', status: 'IN_SERVICE', remark: null, recordVersion: 1, updatedBy: 'operator', updatedAt: '2026-10-21T10:00:00Z' }
|
||
vi.mocked(listVehicles).mockResolvedValue({ records: [candidate], page: 1, size: 100, total: 1, pages: 1 })
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = 'vehicle-1'
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous: true }, global: { plugins: [ElementPlus] } })
|
||
const state = wrapper.vm as unknown as { searchVehicles(): Promise<void>; selectVehicle(id: string): void }
|
||
await state.searchVehicles(); state.selectVehicle('vehicle-2')
|
||
const selected = wrapper.emitted('update:vehicle')!.at(-1)![0] as typeof value.vehicle
|
||
expect(selected).toMatchObject({ vehicleId: 'vehicle-2', carrierName: null })
|
||
await wrapper.setProps({ vehicle: selected })
|
||
const company = wrapper.findAllComponents({ name: 'ElFormItem' }).find(item => item.props('label') === '拖车公司名称')!.getComponent({ name: 'ElInput' })
|
||
expect(company.props('modelValue')).toBe('新承运公司'); expect(company.props('readonly')).toBe(true); wrapper.unmount()
|
||
})
|
||
it.each([false, true])('renders known business labels without internal IDs (continuous=%s)', async continuous => {
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = '900719925474099301'; value.driver.mode = 'MASTER'; value.driver.driverId = '900719925474099302'
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous }, global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
expect(wrapper.text()).toContain('CA-100'); expect(wrapper.text()).toContain('司机甲'); expect(wrapper.text()).not.toContain('90071992547409930'); wrapper.unmount()
|
||
})
|
||
it.each([false, true])('resets only the switched resource and keeps choices independent (continuous=%s)', async continuous => {
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = 'vehicle-1'
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous }, global: { plugins: [ElementPlus] } })
|
||
const state = wrapper.vm as unknown as { changeDriver(): void }; state.changeDriver(); await flushPromises()
|
||
expect(wrapper.emitted('update:driver')![0]![0]).toMatchObject({ mode: 'EXTERNAL', driverId: null, name: null, evidence: { fileIds: [], licenseNo: null } })
|
||
expect(wrapper.emitted('update:vehicle')).toBeUndefined(); expect(value.vehicle.vehicleId).toBe('vehicle-1'); wrapper.unmount()
|
||
})
|
||
it.each([false, true])('requires each directory permission before reading candidates (continuous=%s)', async continuous => {
|
||
can.mockReturnValue(false); const value = resources()
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous }, global: { plugins: [ElementPlus] } })
|
||
const state = wrapper.vm as unknown as { searchVehicles(): Promise<void>; searchDrivers(): Promise<void> }; await state.searchVehicles(); await state.searchDrivers()
|
||
expect(listVehicles).not.toHaveBeenCalled(); expect(listDrivers).not.toHaveBeenCalled(); expect(wrapper.text()).toContain('缺少司机档案读取权限'); wrapper.unmount()
|
||
})
|
||
|
||
it('filters real unavailable statuses, sends dispatch context, and lets an invalid selection be cleared', async () => {
|
||
const base: VehicleDetail = { id: 'good', plateNumber: 'GOOD', model: '厢式货车', driverOrDispatchCompany: null, phone: null, maxPalletSpaces: 20, maxLoadKg: 18000, maxVolumeCbm: 60, fuelConsumptionPer100Km: 0, mileageKm: 0, region: 'OVERSEAS', ownership: 'OWNED', status: 'IN_SERVICE', remark: null, recordVersion: 1, updatedBy: 'operator', updatedAt: '2026-10-21T10:00:00Z' }
|
||
vi.mocked(listVehicles).mockResolvedValue({ records: [base, { ...base, id: 'bad', plateNumber: 'BAD', status: 'MAINTENANCE' }], page: 1, size: 100, total: 2, pages: 1 })
|
||
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = 'bad'
|
||
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous: true, dispatchNo: 'PC-REAL' }, global: { plugins: [ElementPlus] } }); await flushPromises()
|
||
expect(wrapper.text()).toContain('当前车辆已不可用')
|
||
const state = wrapper.vm as unknown as { searchVehicles(): Promise<void>; selectVehicle(id: string | null): void }
|
||
await state.searchVehicles(); await flushPromises()
|
||
expect(listVehicles).toHaveBeenLastCalledWith(expect.objectContaining({ status: 'IN_SERVICE', region: 'OVERSEAS', dispatchNo: 'PC-REAL' }))
|
||
const options = wrapper.findAllComponents({ name: 'ElOption' })
|
||
expect(options.find(item => item.props('value') === 'good')?.props('disabled')).toBe(false)
|
||
expect(options.find(item => item.props('value') === 'bad')).toBeUndefined()
|
||
state.selectVehicle('bad'); expect(wrapper.emitted('update:vehicle')).toBeUndefined()
|
||
state.selectVehicle(null); expect(wrapper.emitted('update:vehicle')!.at(-1)![0]).toMatchObject({ vehicleId: null })
|
||
wrapper.unmount()
|
||
})
|