fix(dispatch): release failed request state and return rejected reviews
This commit is contained in:
@@ -8,9 +8,8 @@ import DataTable from '@/shared/components/DataTable.vue'
|
||||
import RowActions from '@/shared/components/RowActions.vue'
|
||||
import { getDispatch, getDispatchBolPdf, listDispatches } from '../api/dispatches'
|
||||
import { cancelTransport, getTransport } from '../api/transportTasks'
|
||||
import { command, detail, listRow, page, reviews } from '../dispatchTestFixtures'
|
||||
import { detail, listRow, page, reviews } from '../dispatchTestFixtures'
|
||||
import type { DispatchPage } from '../dispatchContracts'
|
||||
import { useDispatchCommandRecovery } from '../dispatchCommandRecovery'
|
||||
const identity = vi.hoisted(() => ({ id: '' }))
|
||||
const push = vi.hoisted(() => vi.fn())
|
||||
const can = vi.hoisted(() => vi.fn(() => true))
|
||||
@@ -104,13 +103,13 @@ it('keeps return-to-review on the loading list with its service gate and permiss
|
||||
expect(denied.getComponent({ name: 'DispatchWorkflowDialog' }).props('modelValue')).toBe(false)
|
||||
denied.unmount()
|
||||
})
|
||||
it('offers recovery of the original pending command independently of the filtered list rows', async () => {
|
||||
const pending = useDispatchCommandRecovery(() => identity.id); pending.begin('PC-OTHER', 'LOADING_COMPLETE', { recordVersion: 0, idempotencyKey: 'original-key' })
|
||||
const wrapper = mount(DispatchesView, options); await flushPromises()
|
||||
expect(wrapper.text()).toContain('PC-OTHER 的确认装车完成结果尚待核对')
|
||||
await wrapper.findAll('button').find(button => button.text() === '核对原请求')!.trigger('click')
|
||||
const dialog = wrapper.get('dispatch-workflow-dialog-stub')
|
||||
expect(dialog.attributes('dispatchno')).toBe('PC-OTHER'); expect(dialog.attributes('modelvalue')).toBe('true'); wrapper.unmount()
|
||||
it('does not let an old pending command hide or block filtered list operations',async()=>{
|
||||
sessionStorage.setItem('dispatch-command:'+identity.id,JSON.stringify({dispatchNo:'PC-OTHER',action:'LOADING_COMPLETE',attempted:true,body:{idempotencyKey:'old'}}))
|
||||
const wrapper=mount(DispatchesView,options);await flushPromises()
|
||||
expect(wrapper.text()).not.toContain('结果尚待核对')
|
||||
expect(wrapper.findAll('button').some(button=>button.text()==='核对原请求')).toBe(false)
|
||||
expect(wrapper.getComponent({name:'DispatchWorkflowDialog'}).props('modelValue')).toBe(false)
|
||||
expect(wrapper.text()).toContain('PC/1');wrapper.unmount()
|
||||
})
|
||||
it('centralizes cargo changes in smart dispatch and never exposes row add/remove actions', async () => {
|
||||
vi.mocked(listDispatches).mockResolvedValueOnce(page([listRow({ actions: { ADD_CARGO: { allowed: true, blockCode: null, blockReason: null }, REMOVE_CARGO: { allowed: true, blockCode: null, blockReason: null } } })]))
|
||||
@@ -156,13 +155,13 @@ it.each([['DISPATCH_REVIEW', 1, '调度审核'], ['LOADING_REVIEW', 2, '理货
|
||||
expect(wrapper.getComponent({ name: 'DispatchWorkflowDialog' }).props()).toMatchObject({ modelValue: true, action, dispatchNo: 'PC/1' })
|
||||
wrapper.unmount()
|
||||
})
|
||||
it.each(['ADD_CARGO', 'REMOVE_CARGO'] as const)('recovers %s only inside smart dispatch', async action => {
|
||||
useDispatchCommandRecovery(() => identity.id).begin('PC-OTHER', action, { recordVersion: 0, idempotencyKey: 'cargo-key' }, action === 'REMOVE_CARGO' ? { itemId: '17' } : undefined)
|
||||
const wrapper = mount(DispatchesView, options); await flushPromises()
|
||||
await wrapper.findAll('button').find(button => button.text() === '核对原请求')!.trigger('click')
|
||||
expect(wrapper.getComponent({ name: 'SmartDispatchDialog' }).props('modelValue')).toBe(true)
|
||||
expect(wrapper.getComponent({ name: 'DispatchWorkflowDialog' }).props('modelValue')).toBe(false)
|
||||
wrapper.unmount()
|
||||
it.each(['ADD_CARGO','REMOVE_CARGO'] as const)('ignores old %s requests while allowing smart dispatch to open normally',async action=>{
|
||||
sessionStorage.setItem('dispatch-command:'+identity.id,JSON.stringify({dispatchNo:'PC-OTHER',action,attempted:true,body:{idempotencyKey:'old'}}))
|
||||
const wrapper=mount(DispatchesView,options);await flushPromises()
|
||||
await wrapper.findAll('button').find(button=>button.text()==='智能排车')!.trigger('click')
|
||||
expect(wrapper.getComponent({name:'SmartDispatchDialog'}).props('modelValue')).toBe(true)
|
||||
expect(wrapper.getComponent({name:'DispatchWorkflowDialog'}).props('modelValue')).toBe(false)
|
||||
expect(wrapper.text()).not.toContain('结果尚待核对');wrapper.unmount()
|
||||
})
|
||||
it.each([1, 2])('keeps only details in audit tab %s without its review permission', async index => {
|
||||
can.mockReturnValue(false)
|
||||
@@ -397,16 +396,12 @@ it.each(['saved', 'rejected'] as const)('refreshes list actions after workflow %
|
||||
expect(wrapper.text()).not.toContain('结果尚待核对')
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('describes a proven command awaiting its GET as completed instead of unknown', async () => {
|
||||
const recovery = useDispatchCommandRecovery(() => identity.id)
|
||||
recovery.begin('PC/1', 'SUBMIT_REVIEW', { recordVersion: 0, idempotencyKey: 'known-key' })
|
||||
recovery.pending.value!.attempted = true
|
||||
recovery.pending.value!.result = command('SUBMIT_REVIEW', 'known-key')
|
||||
const wrapper = mount(DispatchesView, options); await flushPromises()
|
||||
expect(wrapper.text()).toContain('操作已完成,页面资料待刷新')
|
||||
it('does not restore stored failed commands or expose replay controls in the list',async()=>{
|
||||
sessionStorage.setItem('dispatch-command:'+identity.id,JSON.stringify({dispatchNo:'PC/1',action:'SUBMIT_REVIEW',attempted:true,result:{recordVersion:1},body:{idempotencyKey:'old'}}))
|
||||
const wrapper=mount(DispatchesView,options);await flushPromises()
|
||||
expect(wrapper.text()).not.toContain('结果尚待核对')
|
||||
await wrapper.findAll('button').find(button => button.text() === '刷新页面资料')!.trigger('click')
|
||||
expect(wrapper.getComponent({ name: 'DispatchWorkflowDialog' }).props()).toMatchObject({ modelValue: true, action: 'SUBMIT_REVIEW', dispatchNo: 'PC/1' })
|
||||
expect(wrapper.text()).not.toContain('刷新页面资料')
|
||||
expect(wrapper.getComponent({name:'DispatchWorkflowDialog'}).props('modelValue')).toBe(false)
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('clears an earlier input rejection after the corrected workflow succeeds', async () => {
|
||||
@@ -433,3 +428,17 @@ it('opens the exception form from the dispatching row and hides it without permi
|
||||
expect((deniedRow.props('actions') as Array<{ key: string }>).map(item => item.key)).not.toContain('MARK_EXCEPTION')
|
||||
denied.unmount()
|
||||
})
|
||||
|
||||
it.each([['DISPATCH_REVIEW',1,'DISPATCH_REJECTED'],['LOADING_REVIEW',2,'LOADING_REJECTED']] as const)('returns a rejected %s to vehicle information',async(action,index,reviewState)=>{
|
||||
const allowed={allowed:true,blockCode:null,blockReason:null}
|
||||
vi.mocked(listDispatches).mockResolvedValue(page([listRow({actions:{[action]:allowed}})]))
|
||||
const wrapper=mount(DispatchesView,options);await flushPromises()
|
||||
await wrapper.get('[aria-label="装车前审核分区"]').findAll('button')[index]!.trigger('click');await flushPromises()
|
||||
const row=wrapper.findAllComponents(RowActions).find(entry=>entry.props('moreLabel')==='更多操作 · PC/1')!
|
||||
row.vm.$emit('action',action);await flushPromises()
|
||||
vi.mocked(getDispatch).mockResolvedValueOnce(detail({reviewState,stage:'BEFORE_LOADING'}))
|
||||
wrapper.getComponent({name:'DispatchWorkflowDialog'}).vm.$emit('saved');await flushPromises()
|
||||
expect(listDispatches).toHaveBeenLastCalledWith(expect.objectContaining({page:1,stage:'BEFORE_LOADING',beforeLoadingSection:'VEHICLE_INFO'}))
|
||||
expect(wrapper.get('[aria-label="装车前审核分区"]').findAll('button')[0]!.attributes('aria-selected')).toBe('true')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user