265 lines
24 KiB
TypeScript
265 lines
24 KiB
TypeScript
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import ElementPlus from 'element-plus'
|
|
import { defineComponent, h } from 'vue'
|
|
import AppDialog from '@/shared/components/AppDialog.vue'
|
|
import DataTable from '@/shared/components/DataTable.vue'
|
|
import { ApiResponseError } from '@/shared/api/http'
|
|
import WarehouseTransferFulfillmentPanel from './WarehouseTransferFulfillmentPanel.vue'
|
|
import { allocateWarehouseTransfer, getWarehouseTransfer, type WarehouseTransferDetailDto } from '../api/warehouseTransfers'
|
|
import { listWarehouseTransferSources, type WarehouseTransferSourceCandidate } from '../api/warehouseTransferSources'
|
|
|
|
const { push, can } = vi.hoisted(() => ({ push: vi.fn(), can: vi.fn() }))
|
|
vi.mock('vue-router', () => ({ useRouter: () => ({ push }) }))
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) }))
|
|
vi.mock('../api/warehouseTransfers', () => ({ getWarehouseTransfer: vi.fn(), allocateWarehouseTransfer: vi.fn() }))
|
|
vi.mock('../api/warehouseTransferSources', () => ({ listWarehouseTransferSources: vi.fn() }))
|
|
const dialogStub = defineComponent({ props: { modelValue: Boolean, confirmDisabled: Boolean, confirmText: String }, emits: ['confirm', 'update:modelValue'], setup(props, { slots, emit }) { return () => props.modelValue ? h('section', { class: 'allocation-dialog' }, [slots.default?.(), h('button', { class: 'confirm-allocation', disabled: props.confirmDisabled, onClick: () => emit('confirm') }, props.confirmText)]) : null } })
|
|
enableAutoUnmount(afterEach)
|
|
function fixture(transferNo = 'WT-1', values: Partial<WarehouseTransferDetailDto> = {}): WarehouseTransferDetailDto {
|
|
return { id: 'internal-transfer-id', transferNo, packageCount: 2, needRelabel: true, status: 'APPROVED', items: [], cartons: [], pallets: [], relabelBindings: [], ...values }
|
|
}
|
|
function render(customerId = 'customer-1', transferNo = 'WT-1') {
|
|
return mount(WarehouseTransferFulfillmentPanel, { props: { customerId, transferNo }, global: { plugins: [ElementPlus], stubs: { AppDialog: dialogStub } } })
|
|
}
|
|
const sourceCandidate: WarehouseTransferSourceCandidate = { inventoryPositionId: '9223372036854775001', palletId: '9223372036854775011', inboundCode: 'IN-1', skuCode: 'SKU-A', productNameZh: '测试货物', palletNo: 'DP-1', warehouseLocationCode: 'LOC-1', availablePackages: 3, canSelect: true, blockReason: '' }
|
|
async function openNativeAllocation() {
|
|
const wrapper = mount(WarehouseTransferFulfillmentPanel, { attachTo: document.body, props: { customerId: 'customer-1', transferNo: 'WT-1' }, global: { plugins: [ElementPlus] } })
|
|
await flushPromises()
|
|
await wrapper.findAll('button').find(button => button.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
return { wrapper, dialog: wrapper.getComponent(AppDialog) }
|
|
}
|
|
describe('WarehouseTransferFulfillmentPanel', () => {
|
|
beforeEach(() => { vi.clearAllMocks(); can.mockReturnValue(true); vi.mocked(getWarehouseTransfer).mockReset().mockResolvedValue(fixture()); vi.mocked(allocateWarehouseTransfer).mockReset(); vi.mocked(listWarehouseTransferSources).mockReset() })
|
|
it('requires manage and inventory read permissions before offering allocation', async () => {
|
|
can.mockImplementation(code => code !== 'lastleg:warehouse-transfer:manage')
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.text()).not.toContain('选择库存来源 / 分配')
|
|
expect(listWarehouseTransferSources).not.toHaveBeenCalled()
|
|
})
|
|
it('confirms exact quantities and preserves original body/key through unknown failure, close and reopen', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { packageCount: 1 }))
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValue({ issues: [], candidates: [{ inventoryPositionId: 'POSITION', palletId: 'PALLET', inboundCode: 'IN-1', skuCode: 'OLD', productNameZh: '货物', palletNo: 'DP-1', warehouseLocationCode: 'LOC-1', availablePackages: 3, canSelect: true, blockReason: '' }, { inventoryPositionId: 'HOLD', palletId: 'PALLET-HOLD', inboundCode: 'IN-1', skuCode: 'OLD', productNameZh: '货物', palletNo: 'DP-HOLD', warehouseLocationCode: 'LOC-1', availablePackages: 0, canSelect: false, blockReason: '当前无可分配箱数' }] })
|
|
vi.mocked(allocateWarehouseTransfer).mockRejectedValueOnce(new Error('response lost')).mockResolvedValueOnce(fixture('WT-1', { status: 'WAIT_RELABEL', relabelNo: 'RL-NEW', relabelStatus: 'PENDING_REVIEW' }))
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeDefined()
|
|
const available = wrapper.find('.allocation-dialog').get('label[aria-label="选择 DP-1 OLD"] input')
|
|
const held = wrapper.find('.allocation-dialog').get('label[aria-label="选择 DP-HOLD OLD"] input')
|
|
expect(held.attributes('disabled')).toBeDefined()
|
|
await available.setValue(true); await flushPromises()
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeUndefined()
|
|
const quantity = wrapper.get('input[aria-label="DP-1 分配箱数"]')
|
|
expect(quantity.attributes('max')).toBe('3')
|
|
await quantity.setValue('2'); await flushPromises()
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeDefined()
|
|
await quantity.setValue('1'); await flushPromises()
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeUndefined()
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
const [originalCustomer, originalTransferNo, originalPayload] = vi.mocked(allocateWarehouseTransfer).mock.calls[0]!
|
|
const original = [originalCustomer, originalTransferNo, { idempotencyKey: originalPayload.idempotencyKey, allocations: originalPayload.allocations.map(row => ({ ...row })) }]
|
|
expect(original).toEqual(['customer-1', 'WT-1', { idempotencyKey: expect.any(String), allocations: [{ inventoryPositionId: 'POSITION', palletId: 'PALLET', packageCount: 1 }] }])
|
|
expect(wrapper.text()).toContain('已保留本次选择,重试使用相同内容')
|
|
expect(wrapper.text()).not.toContain('幂等键')
|
|
wrapper.getComponent(AppDialog).vm.$emit('update:modelValue', false); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '重试本次库存分配')!.trigger('click'); await flushPromises()
|
|
expect(listWarehouseTransferSources).toHaveBeenCalledTimes(1)
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
expect(vi.mocked(allocateWarehouseTransfer).mock.calls[1]).toEqual(original)
|
|
expect(wrapper.text()).toContain('待换标'); expect(wrapper.text()).toContain('RL-NEW')
|
|
})
|
|
it('keeps ambiguous sources and source query errors explicit and cannot submit them', async () => {
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValueOnce({ candidates: [], issues: ['原票库存代表不匹配,待核查'] }).mockRejectedValueOnce(new Error('query lost'))
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
expect(wrapper.text()).toContain('待核查'); expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeDefined()
|
|
wrapper.getComponent(AppDialog).vm.$emit('update:modelValue', false); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
expect(wrapper.text()).toContain('来源库存加载失败,请重试')
|
|
expect(allocateWarehouseTransfer).not.toHaveBeenCalled()
|
|
})
|
|
it('recovers a definite inventory conflict through an explicit fresh choice and submits a new command', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { packageCount: 1 }))
|
|
const row = { inventoryPositionId: 'OLD-POSITION', palletId: 'OLD-PALLET', inboundCode: 'IN-1', skuCode: 'OLD', productNameZh: '货物', palletNo: 'DP-OLD', warehouseLocationCode: 'LOC-1', availablePackages: 3, canSelect: true, blockReason: '' }
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValueOnce({ issues: [], candidates: [row] }).mockResolvedValueOnce({ issues: [], candidates: [{ ...row, inventoryPositionId: 'NEW-POSITION', palletId: 'NEW-PALLET', palletNo: 'DP-NEW', availablePackages: 1 }] })
|
|
vi.mocked(allocateWarehouseTransfer).mockRejectedValueOnce(new ApiResponseError('库存可用箱数已变化', 'A0409', 200, undefined, 'conflict-request', 'conflict-trace')).mockResolvedValueOnce(fixture('WT-1', { status: 'WAIT_RELABEL', relabelNo: 'RL-RECOVERED' }))
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
await wrapper.get('label[aria-label="选择 DP-OLD OLD"] input').setValue(true); await flushPromises()
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
const oldPayload = vi.mocked(allocateWarehouseTransfer).mock.calls[0]![2]
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeDefined()
|
|
expect(wrapper.text()).toContain('重新选择来源')
|
|
wrapper.getComponent(AppDialog).vm.$emit('update:modelValue', false); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '查看分配问题')!.trigger('click'); await flushPromises()
|
|
expect(listWarehouseTransferSources).toHaveBeenCalledTimes(1)
|
|
await wrapper.findAll('button').find(x => x.text() === '重新选择来源')!.trigger('click'); await flushPromises()
|
|
expect(getWarehouseTransfer).toHaveBeenCalledTimes(2)
|
|
expect(listWarehouseTransferSources).toHaveBeenCalledTimes(2)
|
|
expect(wrapper.get('[aria-label="上次分配诊断"]').text()).toContain('库存可用箱数已变化')
|
|
expect(wrapper.get('[aria-label="上次分配诊断"]').text()).toContain('conflict-request')
|
|
expect(wrapper.find('label[aria-label="选择 DP-OLD OLD"]').exists()).toBe(false)
|
|
expect(wrapper.get('.confirm-allocation').attributes('disabled')).toBeDefined()
|
|
await wrapper.get('label[aria-label="选择 DP-NEW OLD"] input').setValue(true); await flushPromises()
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
const newPayload = vi.mocked(allocateWarehouseTransfer).mock.calls[1]![2]
|
|
expect(newPayload.idempotencyKey).not.toBe(oldPayload.idempotencyKey)
|
|
expect(newPayload.allocations).toEqual([{ inventoryPositionId: 'NEW-POSITION', palletId: 'NEW-PALLET', packageCount: 1 }])
|
|
expect(oldPayload.allocations).toEqual([{ inventoryPositionId: 'OLD-POSITION', palletId: 'OLD-PALLET', packageCount: 1 }])
|
|
expect(wrapper.text()).toContain('RL-RECOVERED')
|
|
expect(wrapper.text()).not.toContain('幂等键')
|
|
})
|
|
it.each([
|
|
new ApiResponseError('服务响应异常', 'A0409', 503),
|
|
new ApiResponseError('身份响应异常', 'A0409', 401),
|
|
new ApiResponseError('分配响应身份不一致', 'TRANSFER_IDENTITY_MISMATCH', 200),
|
|
])('keeps uncertain %s outcomes locked to the original command', async cause => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { packageCount: 1 }))
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValue({ issues: [], candidates: [{ inventoryPositionId: 'POSITION', palletId: 'PALLET', inboundCode: 'IN-1', skuCode: 'OLD', productNameZh: '货物', palletNo: 'DP-1', warehouseLocationCode: 'LOC-1', availablePackages: 3, canSelect: true, blockReason: '' }] })
|
|
vi.mocked(allocateWarehouseTransfer).mockRejectedValueOnce(cause).mockResolvedValueOnce(fixture('WT-1', { status: 'WAIT_RELABEL', relabelNo: 'RL-RETRIED' }))
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(x => x.text() === '选择库存来源 / 分配')!.trigger('click'); await flushPromises()
|
|
await wrapper.get('label[aria-label="选择 DP-1 OLD"] input').setValue(true); await flushPromises()
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
const original = vi.mocked(allocateWarehouseTransfer).mock.calls[0]![2]
|
|
expect(wrapper.text()).not.toContain('重新选择来源')
|
|
expect(wrapper.get('label[aria-label="选择 DP-1 OLD"] input').attributes('disabled')).toBeDefined()
|
|
await wrapper.get('.confirm-allocation').trigger('click'); await flushPromises()
|
|
expect(vi.mocked(allocateWarehouseTransfer).mock.calls[1]![2]).toEqual(original)
|
|
expect(listWarehouseTransferSources).toHaveBeenCalledTimes(1)
|
|
})
|
|
it('reads only the exact source once and blocks missing relabel prerequisites', async () => {
|
|
const wrapper = render(); await flushPromises()
|
|
expect(getWarehouseTransfer).toHaveBeenCalledExactlyOnceWith('customer-1', 'WT-1')
|
|
expect(wrapper.text()).toContain('尚未关联换标单')
|
|
expect(wrapper.text()).toContain('不得提前出库')
|
|
expect(wrapper.text()).not.toContain('internal-transfer-id')
|
|
expect(wrapper.findAll('button').some(x => /出库|跳过|确认换标完成|分配库存/.test(x.text()))).toBe(false)
|
|
})
|
|
it('links the business relabel number and keeps customer confirmation pending', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { status: 'WAIT_RELABEL', relabelApplicationId: 'internal-relabel-id', relabelNo: 'RL-1', relabelStatus: 'CUSTOMER_CONFIRM_PENDING' }))
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.text()).toContain('待客户确认'); expect(wrapper.text()).toContain('不得提前出库')
|
|
expect(wrapper.text()).not.toContain('internal-relabel-id')
|
|
await wrapper.findAll('button').find(x => x.text() === '查看本票换标单')!.trigger('click')
|
|
expect(push).toHaveBeenCalledWith({ name: 'overseas-relabel-order-detail', params: { orderId: 'RL-1' } })
|
|
})
|
|
it('requires relabel read permission and preserves later inventory gates', async () => {
|
|
can.mockReturnValue(false)
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { relabelNo: 'RL-1', relabelStatus: 'READY_FOR_OUTBOUND' }))
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.text()).toContain('后续出库仍须'); expect(wrapper.text()).not.toContain('查看本票换标单')
|
|
})
|
|
it('never displays MIXED or internal allocation identities as actual cargo', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { allocations: [{ inventoryPositionId: 'internal-position', inboundOrderId: 'internal-inbound', inboundCode: 'IN-1', palletId: 'internal-pallet', palletNo: 'PALLET-1', packageCount: 2, status: 'RESERVED', skuCode: 'MIXED' }] }))
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.text()).toContain('IN-1'); expect(wrapper.text()).toContain('PALLET-1')
|
|
for (const value of ['MIXED', 'internal-position', 'internal-inbound', 'internal-pallet']) expect(wrapper.text()).not.toContain(value)
|
|
})
|
|
it('does not fall back from missing source number to an internal or tail order ID', async () => {
|
|
const wrapper = render('customer-1', ''); await flushPromises()
|
|
expect(getWarehouseTransfer).not.toHaveBeenCalled(); expect(wrapper.text()).toContain('缺少客户或来源转运单号')
|
|
})
|
|
it('shows a real empty allocation state for transfers that do not need relabel', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { needRelabel: false, status: 'ALLOCATED', allocations: [] }))
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.text()).toContain('尚未确认本票库存来源分配')
|
|
expect(wrapper.text()).not.toContain('不得提前出库')
|
|
expect(wrapper.text()).not.toContain('查看本票换标单')
|
|
})
|
|
it('does not query across customer scope when the customer is missing', async () => {
|
|
const wrapper = render('', 'WT-1'); await flushPromises()
|
|
expect(getWarehouseTransfer).not.toHaveBeenCalled()
|
|
expect(wrapper.text()).toContain('缺少客户或来源转运单号')
|
|
})
|
|
it('does not display a response that arrives after the page is closed', async () => {
|
|
let resolve!: (value: WarehouseTransferDetailDto) => void
|
|
vi.mocked(getWarehouseTransfer).mockImplementationOnce(() => new Promise(done => { resolve = done }))
|
|
const wrapper = render(); wrapper.unmount()
|
|
resolve(fixture('WT-1', { relabelNo: 'RL-LATE' })); await flushPromises()
|
|
expect(push).not.toHaveBeenCalled()
|
|
})
|
|
it('ignores old source responses and rejects a mismatched transfer identity', async () => {
|
|
let resolveFirst!: (value: WarehouseTransferDetailDto) => void
|
|
vi.mocked(getWarehouseTransfer).mockImplementationOnce(() => new Promise(resolve => { resolveFirst = resolve })).mockResolvedValueOnce(fixture('WT-2'))
|
|
const wrapper = render(); await wrapper.setProps({ transferNo: 'WT-2' }); await flushPromises()
|
|
resolveFirst(fixture('WT-1', { relabelNo: 'RL-OLD' })); await flushPromises()
|
|
expect(wrapper.text()).toContain('WT-2'); expect(wrapper.text()).not.toContain('RL-OLD')
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValueOnce(fixture('WT-WRONG'))
|
|
await wrapper.findAll('button').find(x => x.text() === '刷新转运状态')!.trigger('click'); await flushPromises()
|
|
expect(wrapper.text()).toContain('返回的转运单与当前票不一致'); expect(wrapper.text()).not.toContain('WT-WRONG')
|
|
})
|
|
it('clears stale data on read failure and retries explicitly', async () => {
|
|
const wrapper = render(); await flushPromises()
|
|
vi.mocked(getWarehouseTransfer).mockRejectedValueOnce(new Error('测试读取失败'))
|
|
await wrapper.findAll('button').find(x => x.text() === '刷新转运状态')!.trigger('click'); await flushPromises()
|
|
expect(wrapper.text()).toContain('转运履约加载失败,请重试'); expect(wrapper.text()).not.toContain('尚未关联换标单')
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValueOnce(fixture('WT-1', { relabelNo: 'RL-RETRY' }))
|
|
await wrapper.findAll('button').find(x => x.text().includes('重新加载'))!.trigger('click'); await flushPromises()
|
|
expect(wrapper.text()).toContain('RL-RETRY')
|
|
})
|
|
it('retains partial and complete selections through native fullscreen and submits every exact source', async () => {
|
|
const second = { ...sourceCandidate, inventoryPositionId: '9223372036854775002', palletId: '9223372036854775012', palletNo: 'DP-2', skuCode: 'SKU-B' }
|
|
const blocked = { ...sourceCandidate, inventoryPositionId: 'BLOCKED', palletNo: 'DP-BLOCKED', canSelect: false, availablePackages: 0, blockReason: '当前无可分配箱数' }
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValue({ candidates: [sourceCandidate, second, blocked], issues: [] })
|
|
vi.mocked(allocateWarehouseTransfer).mockResolvedValue(fixture('WT-1', { status: 'ALLOCATED' }))
|
|
const { dialog } = await openNativeAllocation()
|
|
expect(dialog.props('title')).toBe('选择本票库存来源 · WT-1')
|
|
expect(dialog.get('label[aria-label="选择 DP-BLOCKED SKU-A"] input').attributes('disabled')).toBeDefined()
|
|
await dialog.get('label[aria-label="选择 DP-1 SKU-A"] input').setValue(true); await flushPromises()
|
|
expect(dialog.props('confirmDisabled')).toBe(true)
|
|
expect(allocateWarehouseTransfer).not.toHaveBeenCalled()
|
|
await dialog.get('label[aria-label="选择 DP-2 SKU-B"] input').setValue(true); await flushPromises()
|
|
await dialog.get('button[aria-label="展开全屏"]').trigger('click'); await flushPromises()
|
|
expect(dialog.findComponent({ name: 'ElDialog' }).props('fullscreen')).toBe(true)
|
|
expect(dialog.get('[aria-label="库存分配核对"]').text()).toContain('已选择2 箱')
|
|
expect(dialog.props('confirmDisabled')).toBe(false)
|
|
await dialog.findAll('button').find(button => button.text() === '确认库存分配')!.trigger('click'); await flushPromises()
|
|
expect(allocateWarehouseTransfer).toHaveBeenCalledExactlyOnceWith('customer-1', 'WT-1', {
|
|
idempotencyKey: expect.any(String), allocations: [
|
|
{ inventoryPositionId: sourceCandidate.inventoryPositionId, palletId: sourceCandidate.palletId, packageCount: 1 },
|
|
{ inventoryPositionId: second.inventoryPositionId, palletId: second.palletId, packageCount: 1 },
|
|
],
|
|
})
|
|
expect(dialog.props('modelValue')).toBe(false)
|
|
})
|
|
it('locks the native frame while saving and retains the identical command after an uncertain failure', async () => {
|
|
vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture('WT-1', { packageCount: 1 }))
|
|
vi.mocked(listWarehouseTransferSources).mockResolvedValue({ candidates: [sourceCandidate], issues: [] })
|
|
let reject!: (reason: unknown) => void
|
|
vi.mocked(allocateWarehouseTransfer).mockReturnValueOnce(new Promise((_, no) => { reject = no })).mockResolvedValueOnce(fixture('WT-1', { status: 'ALLOCATED' }))
|
|
const { wrapper, dialog } = await openNativeAllocation()
|
|
await dialog.get('label[aria-label="选择 DP-1 SKU-A"] input').setValue(true); await flushPromises()
|
|
dialog.vm.$emit('confirm'); dialog.vm.$emit('confirm'); await flushPromises()
|
|
expect(allocateWarehouseTransfer).toHaveBeenCalledTimes(1)
|
|
const original = vi.mocked(allocateWarehouseTransfer).mock.calls[0]!
|
|
expect(dialog.findComponent({ name: 'ElDialog' }).props()).toMatchObject({ showClose: false, closeOnPressEscape: false })
|
|
expect(dialog.get('input[aria-label="DP-1 分配箱数"]').attributes('disabled')).toBeDefined()
|
|
reject(new ApiResponseError('库存分配响应未确认', 'A0503', 503, null, 'allocation-frame-request')); await flushPromises()
|
|
expect(dialog.props('modelValue')).toBe(true)
|
|
expect(dialog.text()).toContain('allocation-frame-request')
|
|
expect(dialog.get('input[aria-label="DP-1 分配箱数"]').attributes('disabled')).toBeDefined()
|
|
await dialog.findAll('button').find(button => button.text() === '取消')!.trigger('click'); await flushPromises()
|
|
await wrapper.findAll('button').find(button => button.text() === '重试本次库存分配')!.trigger('click'); await flushPromises()
|
|
await dialog.findAll('button').find(button => button.text() === '重试本次分配')!.trigger('click'); await flushPromises()
|
|
expect(vi.mocked(allocateWarehouseTransfer).mock.calls[1]).toEqual(original)
|
|
expect(listWarehouseTransferSources).toHaveBeenCalledTimes(1)
|
|
})
|
|
it('retries source errors in the native frame and keeps a long candidate table in the main body', async () => {
|
|
const candidates = Array.from({ length: 25 }, (_, index) => ({ ...sourceCandidate, inventoryPositionId: `POSITION-${index}`, palletId: `PALLET-${index}`, palletNo: `DP-${index}` }))
|
|
vi.mocked(listWarehouseTransferSources).mockRejectedValueOnce(new ApiResponseError('来源读取失败', 'A0503', 503, null, 'source-frame-request')).mockResolvedValueOnce({ candidates, issues: ['绑定资料待核查'] })
|
|
const { dialog } = await openNativeAllocation()
|
|
expect(dialog.text()).toContain('source-frame-request')
|
|
expect(dialog.props('confirmDisabled')).toBe(true)
|
|
await dialog.findAll('button').find(button => button.text() === '重新加载')!.trigger('click'); await flushPromises()
|
|
const table = dialog.get('[aria-label="本票来源库存"]')
|
|
expect(table.findAll('.el-table__body-wrapper .el-table__row')).toHaveLength(25)
|
|
expect(table.text()).toContain('DP-24')
|
|
expect(dialog.findComponent(DataTable).props('maxHeight')).toBe('')
|
|
expect((dialog.get('.el-table__body-wrapper .el-scrollbar__wrap').element as HTMLElement).style.maxHeight).toBe('')
|
|
expect(dialog.text()).toContain('绑定资料待核查')
|
|
expect(dialog.props('confirmDisabled')).toBe(true)
|
|
expect(allocateWarehouseTransfer).not.toHaveBeenCalled()
|
|
})
|
|
})
|