import ElementPlus from 'element-plus' import { flushPromises, mount } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' import { listOceanBookingLinkCandidates } from '@/modules/ocean-bookings' import { linkContainerOceanBooking } from '../api/platformContainerLoading' import type { ContainerBookingResource } from '../types' import OceanBookingLinkDialog from './OceanBookingLinkDialog.vue' vi.mock('@/modules/ocean-bookings', () => ({ listOceanBookingLinkCandidates: vi.fn() })) vi.mock('../api/platformContainerLoading', () => ({ linkContainerOceanBooking: vi.fn() })) const container = { id: '9001', cabinetNo: 'CAB-1', bookingNo: 'CB-1', containerNo: null, planId: '7001', planStatus: 'EDITING', sourceType: 'SIMULATION', warehouseId: '8', warehouseName: '义乌仓', channelGroupCode: 'MATSON_CLX', channelCode: null, channelName: null, oceanBookingId: null, billOfLadingNo: null, vesselName: null, voyageNo: null, containerType: '40HQ', shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: null, destinationPortCode: null, destinationPortName: null, destinationWarehouseCode: null, destinationWarehouseName: null, capacityCbm: 68, maxWeightKg: 19500, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null, estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null, estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null, version: 7, } satisfies ContainerBookingResource const candidate = { oceanBookingId: '101', bookingNo: 'OB-101', requirementId: '202', containerType: '40HQ', capacityCbm: 68, maxWeightKg: 19500, requiredQuantity: 2, allocatedQuantity: 1, remainingQuantity: 1, customerName: '测试客户', warehouseName: '义乌仓', channelName: '美森正班', } describe('OceanBookingLinkDialog', () => { beforeEach(() => { vi.mocked(listOceanBookingLinkCandidates).mockReset().mockResolvedValue({ records: [candidate], page: 1, size: 200, total: 1, pages: 1 }) vi.mocked(linkContainerOceanBooking).mockReset().mockResolvedValue({ ...container, oceanBookingId: '101', oceanBookingRequirementId: '202', version: 8 } as never) }) it('loads matching released-booking requirements when opened', async () => { const wrapper = mount(OceanBookingLinkDialog, { props: { modelValue: true, container }, global: { plugins: [ElementPlus] } }) await flushPromises() expect(listOceanBookingLinkCandidates).toHaveBeenCalledExactlyOnceWith({ warehouseId: '8', channelGroupCode: 'MATSON_CLX', containerType: '40HQ', page: 1, size: 200 }) expect((wrapper.vm as unknown as { candidates: Array<{ label: string }> }).candidates[0]?.label).toContain('OB-101 · 40HQ × 1') wrapper.unmount() }) it.each([ [' mscu1234567 ', 'MSCU1234567'], ['', null], ])('normalizes and submits the optional real container number %#', async (input, expected) => { const wrapper = mount(OceanBookingLinkDialog, { props: { modelValue: true, container }, global: { plugins: [ElementPlus] } }) await flushPromises() const vm = wrapper.vm as unknown as { candidateKey: string; containerNo: string; save: () => Promise } vm.candidateKey = '101:202' vm.containerNo = input await vm.save() expect(linkContainerOceanBooking).toHaveBeenCalledExactlyOnceWith('9001', { oceanBookingId: '101', requirementId: '202', containerNo: expected, expectedVersion: 7, }) expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([false]) expect(wrapper.emitted('linked')).toHaveLength(1) wrapper.unmount() }) })