import ElementPlus from 'element-plus' import { createPinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { listPlatformTenants } from '@/modules/iam' import DataTable from '@/shared/components/DataTable.vue' import { listInboundOrders } from '../api/inboundOrders' import { getInboundPlan } from '../api/inboundPlans' import { listInventoryLedger } from '../api/inventory' import { getLastMileOrder } from '../api/lastMileOrders' import InboundOrderDetailView from './InboundOrderDetailView.vue' import InboundOrdersView from './InboundOrdersView.vue' import InventoryTransactionsView from './InventoryTransactionsView.vue' import LastMileOrderDetailView from './LastMileOrderDetailView.vue' const routing = vi.hoisted(() => ({ push: vi.fn(), route: { params: {} as Record, query: {} as Record } })) vi.mock('vue-router', () => ({ useRoute: () => routing.route, useRouter: () => ({ push: routing.push }) })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() })) vi.mock('../api/inboundOrders', () => ({ listInboundOrders: vi.fn(), createFbaAppointment: vi.fn(), getFbaAppointment: vi.fn(), updateFbaAppointment: vi.fn(), updateInboundPlan: vi.fn(), })) vi.mock('../api/inboundPlans', () => ({ getInboundPlan: vi.fn() })) vi.mock('../api/inventory', () => ({ listInventoryLedger: vi.fn() })) vi.mock('../api/lastMileOrders', () => ({ getLastMileOrder: vi.fn(), createLastMileOrderFee: vi.fn() })) class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} } function memoryStorage(): Storage { const values = new Map(); return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value) } } enableAutoUnmount(afterEach); afterAll(() => vi.unstubAllGlobals()) describe('overseas warehouse mounted real API pages', () => { beforeEach(() => { vi.stubGlobal('ResizeObserver', ResizeObserverStub); vi.stubGlobal('localStorage', memoryStorage()); routing.push.mockReset(); routing.route.params = {}; routing.route.query = {} vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [{ id: '10001', tenantName: '客户甲', status: 'ACTIVE' } as never], page: 1, size: 200, total: 1, pages: 1 }) vi.mocked(listInboundOrders).mockReset().mockResolvedValue({ records: [], page: 1, size: 10, total: 0, pages: 0 }) vi.mocked(getInboundPlan).mockReset().mockResolvedValue({ id: '9007199254740993', planNo: 'IP-1', customerId: '10001', sourceOrderId: '9007199254740995', overseasWarehouseCode: 'US-LAX-01', overseasWarehouseName: '洛杉矶接柜仓', status: 'PENDING_ASSIGNMENT', lineCount: 1, palletCount: 1, totalVolumeCbm: 2, lines: [{ id: '1', orderId: '9007199254740995', inboundCode: 'IN-1', fbaWarehouseCode: 'ABE3', destinationWarehouseCode: 'ABE3', destinationWarehouseName: 'ABE3目的仓', volumeCbm: 2, requiredPackageCount: 10, plannedPalletCount: 1 }], pallets: [], allocations: [] }) vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [{ id: '9007199254740997', customerId: '10001', customerName: '客户甲', destinationWarehouseCode: 'LAX', inboundCode: 'NYEXX2608300001', inboundOrderNo: 'IP-1', businessType: 'STOCK_IN', beforePackages: 0, deltaPackages: 10, afterPackages: 10, occurredAt: '2026-08-20T10:00:00' }], page: 1, size: 20, total: 1, pages: 1 }) vi.mocked(getLastMileOrder).mockReset().mockResolvedValue({ order: { id: '9007199254740999', customerId: '10001', customerName: '华远贸易', orderNo: 'LMO-260827-0001', inboundCode: 'NYEXX2608280033', containerNo: 'TSTU0000061', currentStatus: 'PENDING_ASSIGNMENT' }, cargo: [], statusHistory: [], fees: [], operationHistory: [] }) }) it('keeps receiving warehouse and FBA warehouse independent in the inbound-plan list', async () => { vi.mocked(listInboundOrders).mockResolvedValue({ records: [ { lineId: '1', planId: '11', planNo: 'IP-1', customerId: '10001', orderId: '21', inboundCode: 'IN-1', overseasWarehouseCode: 'US-LAX-01', overseasWarehouseName: '洛杉矶接柜仓', warehouseId: '31', warehouseCode: 'ABE3', warehouseName: 'ABE3目的仓', fbaWarehouseCode: 'ABE3' }, { lineId: '2', planId: '12', planNo: 'IP-2', customerId: '10001', orderId: '22', inboundCode: 'IN-2', overseasWarehouseCode: null, overseasWarehouseName: '不充整接柜仓', warehouseId: '32', warehouseCode: 'ONT8', warehouseName: 'ONT8目的仓', fbaWarehouseCode: 'ONT8' }, ], page: 1, size: 10, total: 2, pages: 1, }) const wrapper = mount(InboundOrdersView, { global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() const rows = wrapper.findComponent(DataTable).props('data') as Array<{ warehouse: string; fbaWarehouse: string; destinationWarehouseName: string }> expect(rows[0]).toMatchObject({ warehouse: 'US-LAX-01 · 洛杉矶接柜仓', fbaWarehouse: 'ABE3', destinationWarehouseName: 'ABE3目的仓' }) expect(rows[1]).toMatchObject({ warehouse: '—', fbaWarehouse: 'ONT8', destinationWarehouseName: 'ONT8目的仓' }) }) it('mounts inbound detail with independent receiving and FBA warehouse references', async () => { routing.route.params = { orderId: 'IP-1' }; routing.route.query = { customerId: '10001' }; const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(getInboundPlan).toHaveBeenCalledWith('IP-1', '10001'); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('IN-1'); expect(wrapper.text()).not.toContain('9007199254740995'); expect(wrapper.text()).toContain('US-LAX-01 · 洛杉矶接柜仓'); expect(wrapper.text()).toContain('ABE3'); expect(wrapper.text()).not.toContain('ABE3目的仓') }) it('shows an empty receiving warehouse when either frozen field is missing without falling back to destination data', async () => { vi.mocked(getInboundPlan).mockResolvedValueOnce({ id: '1', planNo: 'IP-2', customerId: '10001', sourceOrderId: '2', overseasWarehouseCode: null, overseasWarehouseName: '不充整接柜仓', status: 'PENDING_ASSIGNMENT', lineCount: 1, palletCount: 0, totalVolumeCbm: 0, lines: [{ id: '3', orderId: '2', inboundCode: 'IN-2', fbaWarehouseCode: 'ABE3', destinationWarehouseCode: 'ABE3', destinationWarehouseName: 'ABE3目的仓', requiredPackageCount: 1, plannedPalletCount: 0 }], pallets: [], allocations: [] }) routing.route.params = { orderId: 'IP-2' }; routing.route.query = { customerId: '10001' } const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }) await flushPromises() const field = (label: string) => wrapper.findAll('dt').find((item) => item.text() === label)?.element.parentElement?.querySelector('dd')?.textContent expect(field('海外仓')).toBe('—') expect(field('FBA仓库')).toBe('ABE3') expect(wrapper.text()).not.toContain('ABE3目的仓') }) it('mounts inventory ledger as localized read-only business facts without exposing internal ids', async () => { const wrapper = mount(InventoryTransactionsView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(listInventoryLedger).toHaveBeenCalledWith(expect.objectContaining({ page: 1, size: 10 })); expect(listInventoryLedger).not.toHaveBeenCalledWith(expect.objectContaining({ customerId: expect.anything() })); expect(wrapper.text()).toContain('客户甲'); expect(wrapper.text()).toContain('NYEXX2608300001'); expect(wrapper.text()).not.toContain('10001'); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('10'); expect(wrapper.text()).toContain('入库'); expect(wrapper.text()).not.toContain('STOCK_IN'); expect(wrapper.text()).toContain('不提供出库单跳转'); expect(wrapper.text()).not.toContain('当前流水 DTO'); expect(wrapper.text()).not.toContain('操作人名称待补充') }) it('renders complete business references with single-line ellipsis and titles', async () => { routing.route.params = { orderId: '9007199254740999' }; const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(getLastMileOrder).toHaveBeenCalledWith('9007199254740999'); expect(wrapper.text()).toContain('华远贸易'); expect(wrapper.text()).toContain('LMO-260827-0001'); expect(wrapper.get('.order-reference-text').attributes('title')).toBe('LMO-260827-0001'); const businessValues = wrapper.findAll('.detail-field-value--business'); expect(businessValues.map((item) => item.attributes('title'))).toEqual(expect.arrayContaining(['NYEXX2608280033', 'TSTU0000061'])); expect(businessValues.every((item) => item.text().length > 0)).toBe(true) }) })