优化
This commit is contained in:
@@ -2,6 +2,7 @@ import ElementPlus from 'element-plus'
|
||||
import { enableAutoUnmount, flushPromises, mount, type VueWrapper } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import SmartDispatchDialog from './SmartDispatchDialog.vue'
|
||||
import SmartDispatchCargoBoard from './SmartDispatchCargoBoard.vue'
|
||||
import DispatchResourceFields from './DispatchResourceFields.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
@@ -107,6 +108,34 @@ describe('new dispatch native layout', () => {
|
||||
expect(content.get('[aria-label="第 1 辆车"]').text()).toContain('15 托位')
|
||||
})
|
||||
|
||||
it('does not show a recovery warning or blank the board during a successful slow addition', async () => {
|
||||
const row = candidate()
|
||||
const empty = { ...draft(), sources: [], containers: [{ draftContainerId: 'container-1', sequenceNo: 1, capacity: 30, sourceKeys: [] }] }
|
||||
vi.mocked(api.listDispatchDrafts).mockResolvedValue([empty])
|
||||
const { dialog } = await render(); await restore(dialog); await selectFirstOrder(dialog)
|
||||
let finishLock!: (value: DispatchDraft) => void
|
||||
let finishRefresh!: (value: Awaited<ReturnType<typeof api.listDispatchCandidates>>) => void
|
||||
vi.mocked(api.lockDispatchDraftPallet).mockReturnValueOnce(new Promise(resolve => { finishLock = resolve }))
|
||||
vi.mocked(api.listDispatchCandidates).mockReturnValueOnce(new Promise(resolve => { finishRefresh = resolve }))
|
||||
const content = view(dialog)
|
||||
await content.get('button[aria-label^="将托盘 "]').trigger('click'); await flushPromises()
|
||||
expect(content.text()).not.toContain('原请求结果尚待核对')
|
||||
expect(dialog.props('confirmText')).toBe('生成排车单')
|
||||
expect(dialog.props('confirmLoading')).toBe(true)
|
||||
finishLock({ ...empty, recordVersion: 1, sources: [row], containers: [{ ...empty.containers[0]!, sourceKeys: [row.sourceKey] }] })
|
||||
await flushPromises()
|
||||
expect(dialog.props('loading')).toBe(false)
|
||||
expect(content.get('[aria-label="待排订单"]').text()).toContain(row.orderNo)
|
||||
expect(content.get('[aria-label="托盘明细"]').text()).not.toContain('请选择左侧订单')
|
||||
expect(content.get('[aria-label="智能排车三栏选货"]').attributes('aria-busy')).toBe('true')
|
||||
expect(content.text()).not.toContain('原请求结果尚待核对')
|
||||
finishRefresh({ total: 1, page: 1, size: 20, pages: 1, candidates: [row], complete: true, snapshotAt: '' })
|
||||
await flushPromises()
|
||||
expect(content.get('[aria-label="智能排车三栏选货"]').attributes('aria-busy')).toBe('false')
|
||||
expect(content.get('[aria-label="第 1 辆车"]').text()).toContain('托数 1/30')
|
||||
expect(api.lockDispatchDraftPallet).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('adds cargo directly, preparing the first vehicle internally and enabling submission after assignment', async () => {
|
||||
const empty: DispatchDraft = { ...draft(), containers: [], sources: [] }
|
||||
const grouped: DispatchDraft = { ...empty, recordVersion: 1, containers: [{ draftContainerId: 'first-group', sequenceNo: 1, capacity: 30, sourceKeys: [] }] }
|
||||
@@ -124,7 +153,7 @@ describe('new dispatch native layout', () => {
|
||||
const candidateCalls = vi.mocked(api.listDispatchCandidates).mock.calls.length
|
||||
expect(dialog.props('confirmDisabled')).toBe(true)
|
||||
await selectFirstOrder(dialog)
|
||||
await view(dialog).findAll('button').find(button => button.text() === '加入这辆车')!.trigger('click'); await flushPromises()
|
||||
await view(dialog).get('button[aria-label^="将托盘 "]').trigger('click'); await flushPromises()
|
||||
expect(api.createDispatchDraft).toHaveBeenCalledExactlyOnceWith({ warehouseId: '321', idempotencyKey: expect.any(String) })
|
||||
expect(api.createDispatchDraftContainer).toHaveBeenCalledExactlyOnceWith({ draftToken: 'server-token', recordVersion: 0, capacity: 30, idempotencyKey: expect.any(String) })
|
||||
expect(api.listDispatchCandidates).toHaveBeenCalledTimes(candidateCalls + 1)
|
||||
@@ -132,7 +161,7 @@ describe('new dispatch native layout', () => {
|
||||
expect(api.lockDispatchDraftPallet).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ draftContainerId: 'first-group', source: planned.selection, recordVersion: 1 }))
|
||||
expect(dialog.props('confirmDisabled')).toBe(false)
|
||||
expect(view(dialog).get('[aria-label="第 1 辆车"]').text()).toContain('托数 1/30')
|
||||
expect(view(dialog).findAll('button').find(button => button.text() === '已加入第 1 辆车')!.attributes('disabled')).toBeDefined()
|
||||
expect(view(dialog).find('[aria-label="托盘明细"] tr[draggable]').exists()).toBe(false)
|
||||
})
|
||||
it('retains the exact first-group command after an unknown result without creating a second draft', async () => {
|
||||
const empty: DispatchDraft = { ...draft(), containers: [], sources: [] }
|
||||
@@ -140,7 +169,7 @@ describe('new dispatch native layout', () => {
|
||||
vi.mocked(api.createDispatchDraftContainer).mockRejectedValueOnce(new Error('network unknown')).mockResolvedValueOnce({ ...empty, recordVersion: 1, containers: [{ draftContainerId: 'first-group', sequenceNo: 1, capacity: 30, sourceKeys: [] }] })
|
||||
const { dialog } = await render(); await selectWarehouse(dialog)
|
||||
await selectFirstOrder(dialog)
|
||||
await view(dialog).findAll('button').find(button => button.text() === '加入这辆车')!.trigger('click'); await flushPromises()
|
||||
await view(dialog).get('button[aria-label^="将托盘 "]').trigger('click'); await flushPromises()
|
||||
const original = vi.mocked(api.createDispatchDraftContainer).mock.calls[0]![0]
|
||||
expect(dialog.props('confirmDisabled')).toBe(false)
|
||||
dialog.vm.$emit('confirm'); await flushPromises()
|
||||
@@ -213,11 +242,15 @@ describe('new dispatch native layout', () => {
|
||||
const { wrapper, dialog } = await render(); await restore(dialog)
|
||||
dialog.vm.$emit('confirm'); dialog.vm.$emit('confirm'); await flushPromises()
|
||||
expect(api.convertDispatchDraft).toHaveBeenCalledTimes(1)
|
||||
expect(view(dialog).text()).not.toContain('原请求结果尚待核对')
|
||||
expect(dialog.props('confirmText')).toBe('生成排车单')
|
||||
const original = JSON.parse(JSON.stringify(vi.mocked(api.convertDispatchDraft).mock.calls[0]![0])) as ConvertDraft
|
||||
expect(dialog.findComponent({ name: 'ElDialog' }).props()).toMatchObject({ showClose: false, closeOnPressEscape: false })
|
||||
expect(field(dialog, '备注').get('input').attributes('disabled')).toBeDefined()
|
||||
reject(new ApiResponseError('转换结果尚未确认', 'A0503', 503, null, 'new-dispatch-request')); await flushPromises()
|
||||
expect(view(dialog).text()).toContain('new-dispatch-request')
|
||||
expect(view(dialog).text()).toContain('原请求结果尚待核对')
|
||||
expect(dialog.props('confirmText')).toBe('重试原请求')
|
||||
expect(dialog.findComponent(DispatchResourceFields).exists()).toBe(false)
|
||||
await wrapper.setProps({ modelValue: false }); await wrapper.setProps({ modelValue: true }); await flushPromises()
|
||||
dialog.vm.$emit('confirm'); await flushPromises()
|
||||
@@ -260,18 +293,63 @@ describe('new dispatch native layout', () => {
|
||||
expect(api.listDispatchDrafts).toHaveBeenCalledTimes(2)
|
||||
expect(api.createDispatch).not.toHaveBeenCalled(); expect(api.createDispatchDraft).not.toHaveBeenCalled()
|
||||
})
|
||||
it('requires the explicit recheck action after a definite rejection without automatically resubmitting', async () => {
|
||||
it('unfreezes after a definite rejection without automatically resubmitting', async () => {
|
||||
vi.mocked(api.createDispatchDraft).mockRejectedValue(new ApiResponseError('草稿条件已变化', 'A0409', 409, null, 'draft-rejected-request'))
|
||||
const { dialog } = await render(); await selectWarehouse(dialog)
|
||||
await selectFirstOrder(dialog)
|
||||
await view(dialog).findAll('button').find(button => button.text() === '加入这辆车')!.trigger('click'); await flushPromises()
|
||||
await view(dialog).get('button[aria-label^="将托盘 "]').trigger('click'); await flushPromises()
|
||||
expect(view(dialog).text()).toContain('draft-rejected-request')
|
||||
expect(dialog.findComponent(DispatchResourceFields).exists()).toBe(false)
|
||||
await view(dialog).findAll('button').find(button => button.text() === '按明确驳回结果重新核对')!.trigger('click'); await flushPromises()
|
||||
expect(view(dialog).text()).not.toContain('原请求结果尚待核对')
|
||||
expect(sessionStorage.getItem(`dispatch-draft-command:${identity.id}`)).toBeNull()
|
||||
expect(api.createDispatchDraft).toHaveBeenCalledTimes(1)
|
||||
expect(field(dialog, '建单方式').getComponent({ name: 'ElRadioGroup' }).props('disabled')).toBe(false)
|
||||
expect(dialog.findComponent(DispatchResourceFields).exists()).toBe(false)
|
||||
expect(api.listDispatchDrafts).toHaveBeenCalledTimes(2)
|
||||
expect(api.listDispatchDrafts).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it('rejects removal of a populated car before sending a command and keeps the draft editable', async () => {
|
||||
const { dialog } = await render(); await restore(dialog)
|
||||
dialog.getComponent(SmartDispatchCargoBoard).vm.$emit('removeContainer', 'container-1'); await flushPromises()
|
||||
expect(api.deleteDispatchDraftContainer).not.toHaveBeenCalled()
|
||||
expect(view(dialog).text()).toContain('请先移回车内托盘')
|
||||
expect(view(dialog).text()).not.toContain('原请求结果尚待核对')
|
||||
expect(dialog.getComponent(SmartDispatchCargoBoard).props('disabled')).toBe(false)
|
||||
})
|
||||
it.each(['rejection', 'unknown'] as const)('handles an empty-car deletion %s without confusing rejected and unknown results', async failure => {
|
||||
const empty = { ...draft(), sources: [], containers: [{ ...draft().containers[0]!, sourceKeys: [] }] }
|
||||
vi.mocked(api.listDispatchDrafts).mockResolvedValue([empty])
|
||||
vi.mocked(api.deleteDispatchDraftContainer).mockRejectedValueOnce(failure === 'rejection'
|
||||
? new ApiResponseError('仅空容器可以删除', 'A0409', 409, null, 'delete-request') : new Error('network unknown'))
|
||||
const { dialog } = await render(); await restore(dialog)
|
||||
await view(dialog).findAll('button').find(button => button.text() === '删除车辆')!.trigger('click'); await flushPromises()
|
||||
expect(api.deleteDispatchDraftContainer).toHaveBeenCalledTimes(1)
|
||||
expect(view(dialog).find('[aria-label="第 1 辆车"]').exists()).toBe(true)
|
||||
expect(dialog.getComponent(SmartDispatchCargoBoard).props('disabled')).toBe(failure === 'unknown')
|
||||
if (failure === 'rejection') {
|
||||
expect(view(dialog).text()).toContain('仅空容器可以删除')
|
||||
expect(view(dialog).text()).not.toContain('原请求结果尚待核对')
|
||||
expect(sessionStorage.getItem(`dispatch-draft-command:${identity.id}`)).toBeNull()
|
||||
} else {
|
||||
const original = vi.mocked(api.deleteDispatchDraftContainer).mock.calls[0]!
|
||||
vi.mocked(api.deleteDispatchDraftContainer).mockRejectedValueOnce(new ApiResponseError('仅空容器可以删除', 'A0409', 409))
|
||||
dialog.vm.$emit('confirm'); await flushPromises()
|
||||
expect(api.deleteDispatchDraftContainer).toHaveBeenNthCalledWith(2, ...original)
|
||||
expect(view(dialog).text()).toContain('原请求结果尚待核对')
|
||||
expect(sessionStorage.getItem(`dispatch-draft-command:${identity.id}`)).not.toBeNull()
|
||||
vi.mocked(api.deleteDispatchDraftContainer).mockResolvedValueOnce({ ...empty, recordVersion: 1, containers: [] })
|
||||
dialog.vm.$emit('confirm'); await flushPromises()
|
||||
expect(api.deleteDispatchDraftContainer).toHaveBeenNthCalledWith(3, ...original)
|
||||
expect(sessionStorage.getItem(`dispatch-draft-command:${identity.id}`)).toBeNull()
|
||||
}
|
||||
})
|
||||
it('clears a saved definite rejection on reopen while restoring the original draft', async () => {
|
||||
sessionStorage.setItem(`dispatch-draft-command:${identity.id}`, JSON.stringify({ action: 'DELETE', attempted: true, rejected: true, containerId: 'container-1', body: { draftToken: 'server-token', recordVersion: 0, idempotencyKey: 'rejected-key' } }))
|
||||
const { dialog } = await render()
|
||||
expect(view(dialog).text()).not.toContain('原请求结果尚待核对')
|
||||
expect(sessionStorage.getItem(`dispatch-draft-command:${identity.id}`)).toBeNull()
|
||||
expect(api.deleteDispatchDraftContainer).not.toHaveBeenCalled()
|
||||
expect(view(dialog).get('[aria-label="第 1 辆车"]').text()).toContain('PALLET-1')
|
||||
expect(dialog.getComponent(SmartDispatchCargoBoard).props('disabled')).toBe(false)
|
||||
})
|
||||
it('keeps creation unavailable without its write permission', async () => {
|
||||
can.mockReturnValue(false)
|
||||
|
||||
Reference in New Issue
Block a user