2026-08-20 18:02:52 +08:00
import ElementPlus from 'element-plus'
2026-08-27 15:56:13 +08:00
import { createPinia } from 'pinia'
2026-08-20 18:02:52 +08:00
import { enableAutoUnmount , flushPromises , mount } from '@vue/test-utils'
import { afterAll , afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
import { listPlatformTenants } from '@/modules/iam'
2026-09-08 14:15:28 +08:00
import DataTable from '@/shared/components/DataTable.vue'
import { listInboundOrders } from '../api/inboundOrders'
2026-08-20 18:02:52 +08:00
import { getInboundPlan } from '../api/inboundPlans'
import { listInventoryLedger } from '../api/inventory'
2026-08-27 15:56:13 +08:00
import { getLastMileOrder } from '../api/lastMileOrders'
2026-08-20 18:02:52 +08:00
import InboundOrderDetailView from './InboundOrderDetailView.vue'
2026-09-08 14:15:28 +08:00
import InboundOrdersView from './InboundOrdersView.vue'
2026-08-20 18:02:52 +08:00
import InventoryTransactionsView from './InventoryTransactionsView.vue'
2026-08-27 15:56:13 +08:00
import LastMileOrderDetailView from './LastMileOrderDetailView.vue'
2026-08-20 18:02:52 +08:00
const routing = vi . hoisted (() => ({ push : vi.fn (), route : { params : {} as Record < string , string >, query : {} as Record < string , string > } }))
vi . mock ( 'vue-router' , () => ({ useRoute : () => routing . route , useRouter : () => ({ push : routing.push }) }))
vi . mock ( '@/modules/iam' , () => ({ listPlatformTenants : vi.fn () }))
2026-09-08 14:15:28 +08:00
vi . mock ( '../api/inboundOrders' , () => ({
listInboundOrders : vi.fn (), createFbaAppointment : vi.fn (), getFbaAppointment : vi.fn (),
updateFbaAppointment : vi.fn (), updateInboundPlan : vi.fn (),
}))
2026-08-20 18:02:52 +08:00
vi . mock ( '../api/inboundPlans' , () => ({ getInboundPlan : vi.fn () }))
vi . mock ( '../api/inventory' , () => ({ listInventoryLedger : vi.fn () }))
2026-08-27 15:56:13 +08:00
vi . mock ( '../api/lastMileOrders' , () => ({ getLastMileOrder : vi.fn (), createLastMileOrderFee : vi.fn () }))
2026-08-20 18:02:52 +08:00
class ResizeObserverStub implements ResizeObserver { observe() { } unobserve() { } disconnect() { } }
function memoryStorage () : Storage { const values = new Map < string , string >(); 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 })
2026-09-08 14:15:28 +08:00
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 : [] })
2026-08-30 17:43:03 +08:00
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 })
2026-09-07 21:06:39 +08:00
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 : [] })
2026-08-20 18:02:52 +08:00
})
2026-09-08 14:15:28 +08:00
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目的仓' )
})
2026-09-04 17:46:15 +08:00
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 ( '操作人名称待补充' ) })
2026-09-07 21:06:39 +08:00
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 ) })
2026-08-20 18:02:52 +08:00
})