import { flushPromises, mount } from '@vue/test-utils' import ElementPlus from 'element-plus' import { beforeEach, expect, it, vi } from 'vitest' import { getDeliveryOrder, type DeliveryOrderDetail } from '../api/deliveryOrders' import type { DeliveryReference } from '../dispatchContracts' import DispatchVehicleMaintenanceFees from './DispatchVehicleMaintenanceFees.vue' const can = vi.hoisted(() => vi.fn(() => true)) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) })) vi.mock('../api/deliveryOrders', () => ({ getDeliveryOrder: vi.fn() })) const reference: DeliveryReference = { groupKey: 'group-a', customerId: 'C1', customerName: '客户甲', outboundNo: 'OUT-1', deliveryNo: 'DL-1', status: 'CREATED', packageCount: 2, palletCount: 1, destination: { key: 'WAREHOUSE:ONT8', type: 'FBA', warehouseId: null, warehouseCode: 'ONT8', warehouseName: null, address: null, postalCode: null, contactName: null, contactPhone: null } } function result(): DeliveryOrderDetail { return { order: { id: '11', version: 0, allowedActions: [], customerId: 'C1', deliveryNo: 'DL-1', outboundNo: 'OUT-1', dispatchNo: 'PC/1', status: 'CREATED', palletCount: 1, packageCount: 2, signedPackages: null, createdAt: '2026-10-21T10:00:00Z' }, skus: [], operations: [], fees: [{ id: '21', feeDirection: 'RECEIVABLE', feeType: 'DELIVERY_FEE', currency: 'USD', amount: 28, voidStatus: 'CONFIRMED' }] } } function render(deliveries = [reference]) { return mount(DispatchVehicleMaintenanceFees, { props: { dispatchNo: 'PC/1', deliveries }, global: { plugins: [ElementPlus] } }) } beforeEach(() => { vi.resetAllMocks(); can.mockReturnValue(true); vi.mocked(getDeliveryOrder).mockResolvedValue(result()) }) it('reads official fees once per customer and delivery and preserves their currency and status', async () => { const wrapper = render([reference, reference]); await flushPromises() expect(getDeliveryOrder).toHaveBeenCalledExactlyOnceWith('DL-1', 'C1') expect(wrapper.text()).toContain('派送费用'); expect(wrapper.text()).toContain('USD'); expect(wrapper.text()).toContain('28'); expect(wrapper.text()).toContain('已作废') expect(wrapper.find('input').exists()).toBe(false); wrapper.unmount() }) it('does not request or invent fees before a formal delivery exists', async () => { const wrapper = render([]); await flushPromises() expect(getDeliveryOrder).not.toHaveBeenCalled(); expect(wrapper.text()).toContain('尚未生成派送单费用'); wrapper.unmount() }) it('preserves the existing delivery-list read permission', async () => { can.mockReturnValue(false) const wrapper = render(); await flushPromises() expect(getDeliveryOrder).not.toHaveBeenCalled(); expect(wrapper.text()).toContain('暂无派送单费用查看权限'); wrapper.unmount() }) it('rejects a fee response belonging to another dispatch and keeps failures separate from empty records', async () => { const wrong = result(); wrong.order.dispatchNo = 'PC/OTHER' vi.mocked(getDeliveryOrder).mockResolvedValueOnce(wrong) const wrapper = render(); await flushPromises() expect(wrapper.text()).toContain('费用读取失败'); expect(wrapper.text()).not.toContain('暂无正式费用记录'); expect(wrapper.text()).not.toContain('USD') wrapper.unmount() }) it('ignores a late response from the previous dispatch', async () => { let resolve!: (value: DeliveryOrderDetail) => void vi.mocked(getDeliveryOrder).mockImplementationOnce(() => new Promise(done => { resolve = done })) const wrapper = render(); await flushPromises() await wrapper.setProps({ dispatchNo: 'PC/2', deliveries: [] }) resolve(result()); await flushPromises() expect(wrapper.text()).toContain('尚未生成派送单费用'); expect(wrapper.text()).not.toContain('USD'); wrapper.unmount() })