2026-10-19 17:56:31 +08:00
import ElementPlus from 'element-plus'
import { flushPromises , mount } from '@vue/test-utils'
2026-10-03 15:57:53 +08:00
import { beforeEach , expect , it , vi } from 'vitest'
import SmartDispatchDialog from './SmartDispatchDialog.vue'
2026-10-19 17:56:31 +08:00
import AppDialog from '@/shared/components/AppDialog.vue'
import { listAllPlatformWarehouses } from '@/modules/warehouses'
import * as api from '../api/dispatches'
2026-10-20 10:11:31 +08:00
import { candidate , command , detail , sharedPalletCandidates } from '../dispatchTestFixtures'
2026-10-19 17:56:31 +08:00
import type { ConvertDraft , DispatchDraft , Version } from '../dispatchContracts'
const identity = vi . hoisted (() => ({ id : 'smart-operator' }))
const push = vi . hoisted (() => vi . fn ())
vi . mock ( '@/core/auth/auth' , () => ({ useAuthStore : () => ({ user : identity }) }))
vi . mock ( '@/core/access/accessPreview' , () => ({ useAccessPreviewStore : () => ({ can : () => true }) }))
vi . mock ( 'vue-router' , () => ({ useRouter : () => ({ push }) }))
vi . mock ( '@/modules/warehouses' , () => ({ listAllPlatformWarehouses : vi.fn () }))
vi . mock ( './DispatchResourceFields.vue' , () => ({ default : { template : '<div />' } }))
vi . mock ( '../api/dispatches' , () => ({ listDispatchDrafts : vi.fn (), listDispatchCandidates : vi.fn (), createDispatchDraft : vi.fn (), createDispatchDraftContainer : vi.fn (), lockDispatchDraftPallet : vi.fn (), unlockDispatchDraftPallet : vi.fn (), deleteDispatchDraftContainer : vi.fn (), releaseDispatchDraft : vi.fn (), convertDispatchDraft : vi.fn (), createDispatch : vi.fn (), getDispatch : vi.fn (), getDispatchCommand : vi.fn () }))
const source = () => candidate (), draft = () : DispatchDraft => ({ draftToken : 'server-token' , recordVersion : 0 , status : 'ACTIVE' , warehouseId : '321' , containers : [{ draftContainerId : '100000000000000001' , sequenceNo : 1 , capacity : 30 , sourceKeys : [ 'source-1' ] }], sources : [ source ()], expiresAt : '2026-12-01T00:00:00Z' })
2026-10-22 23:10:09 +08:00
it ( 'opens only new dispatch planning and keeps draft cargo selection available' , async () => {
2026-10-21 13:22:42 +08:00
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const dialog = wrapper . getComponent ( AppDialog )
2026-10-22 23:10:09 +08:00
expect ( dialog . find ( '[aria-label="智能排车工作方式"]' ). exists ()). toBe ( false )
expect ( dialog . text ()). not . toContain ( '调整已有排车货物' )
expect ( dialog . findComponent ({ name : 'SmartDispatchCargoBoard' }). exists ()). toBe ( true )
expect ( dialog . props ( 'showConfirm' )). toBe ( true )
expect ( api . listDispatchCandidates ). toHaveBeenCalledOnce ()
2026-10-21 13:22:42 +08:00
expect ( api . createDispatch ). not . toHaveBeenCalled ()
expect ( api . createDispatchDraft ). not . toHaveBeenCalled ()
expect ( api . convertDispatchDraft ). not . toHaveBeenCalled ()
2026-10-22 23:10:09 +08:00
expect ( wrapper . emitted ( 'created' )). toBeUndefined ()
2026-10-21 13:22:42 +08:00
expect ( wrapper . emitted ( 'update:modelValue' )). toBeUndefined ()
wrapper . unmount ()
})
2026-10-22 10:10:32 +08:00
it . each ([ 'ADD_CARGO' , 'REMOVE_CARGO' ] as const )( 'ignores old %s browser requests and opens a fresh workspace' , async action => {
sessionStorage . setItem ( 'dispatch-command:' + identity . id , JSON . stringify ({ dispatchNo : 'PC/1' , action , attempted :true , body : { idempotencyKey : 'old' }}))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
2026-10-22 23:10:09 +08:00
expect ( wrapper . find ( '[aria-label="智能排车工作方式"]' ). exists ()). toBe ( false )
2026-10-22 10:10:32 +08:00
expect ( api . listDispatchDrafts ). toHaveBeenCalledOnce ()
2026-10-22 23:10:09 +08:00
expect ( wrapper . text ()). not . toContain ( '原请求已保留' )
2026-10-22 10:10:32 +08:00
expect ( wrapper . text ()). not . toContain ( '原请求结果尚待核对' ); wrapper . unmount ()
2026-10-21 13:22:42 +08:00
})
2026-10-20 10:11:31 +08:00
it ( 'converts a filtered relabel group with both authoritative sources and locks it through one HTTP command' , async () => {
const [ first , second ] = sharedPalletCandidates (), original = draft ()
original . sources = [ first ! ]; original . containers [ 0 ] ! . sourceKeys = [ first ! . sourceKey ]
2026-10-21 07:19:57 +08:00
vi . mocked ( api . listDispatchCandidates ). mockResolvedValue ({ total : 1 , page : 1 , size : 20 , pages : 1 , candidates : [ first ! ], complete : true , snapshotAt : '' })
2026-10-20 10:11:31 +08:00
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ]); vi . mocked ( api . lockDispatchDraftPallet ). mockResolvedValue ( original )
vi . mocked ( api . convertDispatchDraft ). mockImplementation ( async request => ({ draftToken : request.draftToken , replayed : false , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey ) })) }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; draftAction ( action : string , row : typeof first , container : string ) : Promise < void >; submit () : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . draftAction ( 'LOCK' , first , original . containers [ 0 ] ! . draftContainerId )
expect ( api . lockDispatchDraftPallet ). toHaveBeenCalledExactlyOnceWith ( expect . objectContaining ({ source : first ! . selection }))
await state . submit (); expect ( vi . mocked ( api . convertDispatchDraft ). mock . calls [ 0 ] ! [ 0 ]. groups [ 0 ] ! . dispatch . cargo ). toEqual ([ first ! . selection , second ! . selection ])
wrapper . unmount ()
})
it ( 'allows one atomic removal of a restored group that is no longer selectable' , async () => {
const first = sharedPalletCandidates ()[ 0 ] ! , original = draft ()
first . selectionGroup = { ... first . selectionGroup , complete : false , blockReasons : [ '来源待核齐' ] }
original . sources = [ first ]; original . containers [ 0 ] ! . sourceKeys = [ first . sourceKey ]
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ]); vi . mocked ( api . unlockDispatchDraftPallet ). mockResolvedValue ( original )
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; draftAction ( action : string , row : typeof first , container : string ) : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . draftAction ( 'UNLOCK' , first , original . containers [ 0 ] ! . draftContainerId )
expect ( api . unlockDispatchDraftPallet ). toHaveBeenCalledExactlyOnceWith ( expect . objectContaining ({ source : first.selection })); wrapper . unmount ()
})
2026-10-19 17:56:31 +08:00
const options = { props : { modelValue : true , orders : [] }, global : { plugins : [ ElementPlus ] } }
2026-10-21 07:19:57 +08:00
beforeEach (() => { vi . clearAllMocks (); sessionStorage . clear (); identity . id = crypto . randomUUID (); vi . stubGlobal ( 'ResizeObserver' , class { observe() { } unobserve() { } disconnect() { } }); vi . mocked ( listAllPlatformWarehouses ). mockResolvedValue ([{ id : '321' , warehouseName : '洛杉矶仓' }] as Awaited < ReturnType < typeof listAllPlatformWarehouses >>); vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ draft ()]); vi . mocked ( api . listDispatchCandidates ). mockResolvedValue ({ total : 1 , page : 1 , size : 20 , pages : 1 , candidates : [ source ()], complete : true , snapshotAt : '2026-10-19T00:00:00Z' }); vi . mocked ( api . getDispatch ). mockResolvedValue ( detail ({ recordVersion : 1 })) })
2026-10-22 10:10:32 +08:00
it . each ([ 0 , '0' , '9007199254740993' ])( 'transmits original authoritative batch version %s in conversion and refreshes the batch version on a new click' , async recordVersion => {
2026-10-19 17:56:31 +08:00
const original = draft ()
original . sources [ 0 ] ! . appointmentBatches = [{ batchKey : 'FBA_APPOINTMENT:1' , batchNo : 'AP-1' , customerId : 'C1' , recordVersion , sources : [{ sourceKey : 'ORDER:1' , destinationKey : 'FBA:ONT8' , packageCount : 2 }] }]
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ])
vi . mocked ( api . convertDispatchDraft ). mockRejectedValueOnce ( new Error ( 'unknown' )). mockImplementationOnce ( async request => ({ draftToken : request.draftToken , replayed : true , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey ) })) }))
const wrapper = mount ( SmartDispatchDialog , options )
try {
await flushPromises (); const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; submit () : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . submit ()
const request = JSON . parse ( JSON . stringify ( vi . mocked ( api . convertDispatchDraft ). mock . calls [ 0 ] ! [ 0 ])) as ConvertDraft
expect ( request . groups [ 0 ] ! . dispatch . batchAllocations ). toEqual ([{ planPalletId : '11' , batchKey : 'FBA_APPOINTMENT:1' , customerId : 'C1' , sourceKey : 'ORDER:1' , destinationKey : 'FBA:ONT8' , packageCount : 2 , recordVersion }])
original . sources [ 0 ] ! . appointmentBatches [ 0 ] ! . recordVersion = '9007199254740994'
2026-10-22 10:10:32 +08:00
await state . submit ()
const next = vi . mocked ( api . convertDispatchDraft ). mock . calls [ 1 ] ! [ 0 ]
expect ( next . idempotencyKey ). not . toBe ( request . idempotencyKey )
expect ( next . groups [ 0 ] ! . dispatch . batchAllocations [ 0 ] ! . recordVersion ). toBe ( '9007199254740994' )
2026-10-19 17:56:31 +08:00
} finally { wrapper . unmount () }
2026-10-05 03:30:06 +08:00
})
2026-10-19 17:56:31 +08:00
it . each ([ undefined , null , 'bad' , 9007199254740992 ])( 'requires rereading a restored candidate with missing or invalid batch version %s' , async recordVersion => {
const original = draft ()
original . sources [ 0 ] ! . appointmentBatches = [{ batchKey : 'FBA_APPOINTMENT:1' , batchNo : 'AP-1' , customerId : 'C1' , recordVersion : recordVersion as Version , sources : [] }]
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ])
const wrapper = mount ( SmartDispatchDialog , options )
try {
await flushPromises (); const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; submit () : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . submit ()
2026-10-20 23:34:11 +08:00
expect ( api . convertDispatchDraft ). not . toHaveBeenCalled (); expect ( wrapper . getComponent ({ name : 'ElDialogContent' }). text ()). toContain ( '预约批次版本缺失或无效,请重新读取候选' )
2026-10-19 17:56:31 +08:00
} finally { wrapper . unmount () }
2026-10-05 03:30:06 +08:00
})
2026-10-21 15:10:54 +08:00
it ( 'requires a warehouse when the directory is empty and allows saving a true empty dispatch with no fake cargo' , async () => {
vi . mocked ( listAllPlatformWarehouses ). mockResolvedValue ([])
2026-10-19 17:56:31 +08:00
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const state = wrapper . vm as unknown as { mode : string ; warehouseId : string ; submit : () => Promise < void > }
state . mode = 'empty' ; await wrapper . getComponent ( AppDialog ). vm . $emit ( 'confirm' ); await flushPromises (); expect ( api . createDispatch ). not . toHaveBeenCalled ()
state . warehouseId = '321' ; vi . mocked ( api . createDispatch ). mockImplementation ( async body => command ( 'CREATE' , body . idempotencyKey ))
await state . submit (); await flushPromises ()
expect ( api . createDispatch ). toHaveBeenCalledWith ( expect . objectContaining ({ warehouseId : '321' , cargo : [], vehicle : null , driver : null }))
expect ( wrapper . emitted ( 'created' )). toHaveLength ( 1 ); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-19 17:56:31 +08:00
it ( 'restores an explicit draft and submits complete server source versions in each container' , async () => {
2026-10-20 10:11:31 +08:00
const original = draft (); original . sources . push ( candidate ({ sourceKey : 'source-B' , customerId : 'B' , customerName : '客户乙' , selection : { ... source (). selection , planPalletId : '22' , warehousePalletId : '32' } })); original . containers [ 0 ] ! . sourceKeys . push ( 'source-B' )
2026-10-19 17:56:31 +08:00
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ])
vi . mocked ( api . convertDispatchDraft ). mockImplementation ( async request => ({ draftToken : request.draftToken , replayed : false , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey , 'PC/1' ) })) }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises (); const state = wrapper . vm as unknown as { restoreDraft : ( token : string ) => void ; submit : () => Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . submit (); await flushPromises ()
2026-10-20 10:11:31 +08:00
expect ( api . convertDispatchDraft ). toHaveBeenCalledWith ( expect . objectContaining ({ recordVersion : 0 , draftToken : 'server-token' , groups : [ expect . objectContaining ({ draftContainerId : '100000000000000001' , dispatch : expect.objectContaining ({ cargo : [ source (). selection , original . sources [ 1 ] ! . selection ] }) })] }))
2026-10-19 17:56:31 +08:00
expect ( wrapper . emitted ( 'created' )). toHaveLength ( 1 ); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-22 10:10:32 +08:00
it ( 'releases an unknown conversion and starts a fresh request on the next click' , async () => {
2026-10-19 17:56:31 +08:00
vi . mocked ( api . convertDispatchDraft ). mockRejectedValueOnce ( new Error ( 'network unknown' )). mockImplementationOnce ( async request => ({ draftToken : request.draftToken , replayed : true , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey ) })) }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises (); const state = wrapper . vm as unknown as { restoreDraft : ( token : string ) => void ; submit : () => Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); await state . submit (); await state . submit ()
2026-10-22 10:10:32 +08:00
expect ( api . convertDispatchDraft ). toHaveBeenCalledTimes ( 2 ); expect ( vi . mocked ( api . convertDispatchDraft ). mock . calls [ 1 ] ! [ 0 ]. idempotencyKey ). not . toBe ( vi . mocked ( api . convertDispatchDraft ). mock . calls [ 0 ] ! [ 0 ]. idempotencyKey ); wrapper . unmount ()
2026-10-14 13:53:15 +08:00
})
2026-10-19 17:56:31 +08:00
it ( 'does not discard draft selections when recovery fails and never submits an incomplete source list' , async () => {
vi . mocked ( api . listDispatchDrafts ). mockRejectedValue ( new Error ( 'draft unavailable' ))
2026-10-20 23:34:11 +08:00
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises (); expect ( wrapper . getComponent ({ name : 'ElDialogContent' }). text ()). toContain ( '排车候选或草稿恢复失败' ); expect ( api . convertDispatchDraft ). not . toHaveBeenCalled (); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-21 07:19:57 +08:00
it ( 'accepts a partial candidate page without requiring the filtered whole set' , async () => {
vi . mocked ( api . listDispatchCandidates ). mockResolvedValue ({ total : 21 , page : 1 , size : 20 , pages : 2 , candidates : [ source ()], complete : false , snapshotAt : '' })
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises (); const state = wrapper . vm as unknown as { restoreDraft : ( token : string ) => void }; state . restoreDraft ( 'server-token' ); await flushPromises (); expect ( wrapper . getComponent ({ name : 'ElDialogContent' }). text ()). not . toContain ( '尚未完整返回' ); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-22 10:10:32 +08:00
it ( 'keeps planning remarks independent with unassigned resources and permits resubmission after a failed detail read' , async () => {
2026-10-19 17:56:31 +08:00
const original = draft (); original . containers . push ({ draftContainerId : 'container-B' , sequenceNo : 2 , capacity : 15 , sourceKeys : [ 'source-B' ] }); original . sources . push ( candidate ({ sourceKey : 'source-B' , customerId : 'B' }))
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ])
vi . mocked ( api . convertDispatchDraft ). mockImplementation ( async request => ({ draftToken : request.draftToken , replayed : false , vehicles : request.groups.map (( group , index ) => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey , `PC/ ${ index + 1 } ` ) })) }))
vi . mocked ( api . getDispatch ). mockRejectedValueOnce ( new Error ( 'detail unavailable' )). mockImplementation ( async no => detail ({ dispatchNo : no , recordVersion : 1 }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
2026-10-22 11:56:54 +08:00
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; submit () : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises ()
2026-10-19 17:56:31 +08:00
await state . submit (); expect ( wrapper . emitted ( 'created' )). toBeUndefined ()
2026-10-22 10:10:32 +08:00
await state . submit (); expect ( api . convertDispatchDraft ). toHaveBeenCalledTimes ( 2 )
2026-10-19 17:56:31 +08:00
const groups = vi . mocked ( api . convertDispatchDraft ). mock . calls [ 0 ] ! [ 0 ]. groups
2026-10-22 11:56:54 +08:00
expect ( groups . map ( group => [ group . dispatch . vehicle , group . dispatch . driver , group . dispatch . remark ])). toEqual ([[ null , null , null ], [ null , null , null ]])
2026-10-19 17:56:31 +08:00
expect ( wrapper . emitted ( 'created' )). toHaveLength ( 1 ); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-22 10:10:32 +08:00
it ( 'clears an old departure request without blocking new empty creation' , async () => {
sessionStorage . setItem ( 'dispatch-command:' + identity . id , JSON . stringify ({ dispatchNo : 'PC/1' , action : 'DEPART' , attempted :true , body : { idempotencyKey : 'old' }}))
vi . mocked ( api . createDispatch ). mockImplementation ( async body => command ( 'CREATE' , body . idempotencyKey ))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const state = wrapper . vm as unknown as { mode :string ; submit () : Promise < void >}
state . mode = 'empty' ; await flushPromises (); await state . submit ()
expect ( api . createDispatch ). toHaveBeenCalledOnce ()
2026-10-22 23:10:09 +08:00
expect ( wrapper . text ()). not . toContain ( '原请求已保留' ); wrapper . unmount ()
2026-10-05 03:30:06 +08:00
})
2026-10-19 17:56:31 +08:00
it ( 'rejects a restored source whose batch customer differs from its real cargo owner' , async () => {
const original = draft (); original . sources [ 0 ] ! . appointmentBatches = [{ batchKey : 'batch-1' , batchNo : 'BATCH-1' , recordVersion : 0 , customerId : 'other-customer' , sources : [{ sourceKey : 'source-1' , destinationKey : 'CODE:ONT8' , packageCount : 2 }] }]
vi . mocked ( api . listDispatchDrafts ). mockResolvedValue ([ original ])
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; submit () : Promise < void > }; state . restoreDraft ( 'server-token' ); await flushPromises (); await state . submit ()
2026-10-20 23:34:11 +08:00
expect ( api . convertDispatchDraft ). not . toHaveBeenCalled (); expect ( wrapper . getComponent ({ name : 'ElDialogContent' }). text ()). toContain ( '预约客户不一致' ); wrapper . unmount ()
2026-10-18 16:37:37 +08:00
})
2026-10-21 07:19:57 +08:00
it ( 'keeps server draft cargo across pages, resets filters and sends regional tabs to the server' , async () => {
vi . mocked ( api . listDispatchCandidates ). mockImplementation ( async query => ({ candidates : query.page === 2 ? [] : [ source ()], complete : false , snapshotAt : '' , total : 21 , page : query.page ! , size : query.size ! , pages : Math.ceil ( 21 / query . size ! ) }))
vi . mocked ( api . convertDispatchDraft ). mockImplementation ( async request => ({ draftToken : request.draftToken , replayed : false , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey ) })) }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
try {
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; changePage ( page : number ) : void ; changeTab ( tab : string ) : void ; changeSize ( size : number ) : void ; query : { orderNo : string }; search () : Promise < void >; submit () : Promise < void > }
state . restoreDraft ( 'server-token' ); await flushPromises (); state . changePage ( 2 ); await flushPromises ()
expect ( api . listDispatchCandidates ). toHaveBeenLastCalledWith ( expect . objectContaining ({ page : 2 , size : 20 }))
state . query . orderNo = 'query' ; await state . search ()
expect ( api . listDispatchCandidates ). toHaveBeenLastCalledWith ( expect . objectContaining ({ page : 1 , orderNo : 'query' }))
state . changeTab ( 'NYC' ); await flushPromises ()
2026-10-21 16:40:10 +08:00
expect ( api . listDispatchCandidates ). toHaveBeenLastCalledWith ( expect . objectContaining ({ page : 1 , warehouseId : '321' , warehouseTab : 'ALL' }))
2026-10-21 07:19:57 +08:00
state . changeSize ( 10 ); await flushPromises ()
expect ( api . listDispatchCandidates ). toHaveBeenLastCalledWith ( expect . objectContaining ({ page : 1 , size : 10 }))
state . changePage ( 2 ); await flushPromises (); await state . submit ()
expect ( api . convertDispatchDraft ). toHaveBeenCalledWith ( expect . objectContaining ({ groups : [ expect . objectContaining ({ dispatch : expect.objectContaining ({ cargo : [ source (). selection ] }) })] }))
} finally { wrapper . unmount () }
})
2026-10-21 18:51:10 +08:00
2026-10-22 11:56:54 +08:00
it . each ([ false , true ])( 'defaults publication off and carries publication %s in the atomic draft conversion' , async publish => {
2026-10-21 18:51:10 +08:00
vi . mocked ( api . convertDispatchDraft ). mockImplementation ( async request => ({ draftToken : request.draftToken , replayed : false , vehicles : request.groups.map ( group => ({ draftContainerId : group.draftContainerId , dispatch : command ( 'CREATE' , group . dispatch . idempotencyKey ) })) }))
const wrapper = mount ( SmartDispatchDialog , options ); await flushPromises ()
2026-10-22 11:56:54 +08:00
const state = wrapper . vm as unknown as { restoreDraft ( token : string ) : void ; submit () : Promise < void >; publishLooseboardDemand : boolean }
2026-10-21 18:51:10 +08:00
state . restoreDraft ( 'server-token' ); await flushPromises ()
2026-10-22 11:56:54 +08:00
expect ( state . publishLooseboardDemand ). toBe ( false )
state . publishLooseboardDemand = publish
2026-10-21 18:51:10 +08:00
await state . submit ()
2026-10-22 11:56:54 +08:00
expect ( api . convertDispatchDraft ). toHaveBeenCalledWith ( expect . objectContaining ({ groups : [ expect . objectContaining ({ dispatch : expect.objectContaining ({ publishLooseboardDemand : publish }) })] }))
2026-10-21 18:51:10 +08:00
wrapper . unmount ()
})