import { flushPromises, mount } from '@vue/test-utils' import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { ApiResponseError } from '@/shared/api/http' import WorkBatchContinuation from './WorkBatchContinuation.vue' import { resolveBatchWork } from './workApi' import { workDetail, workItem } from './work.test-support' vi.mock('./workApi', async original => ({ ...await original(), resolveBatchWork: vi.fn() })) function render() { return mount(WorkBatchContinuation, { props: { batchKey: 'FBA_APPOINTMENT:40', labels: [{ customerId: 'customer-a', label: 'IN-A' }] }, global: { stubs: { ElButton: { props: ['disabled'], template: '' }, LoadErrorState: { props: ['message'], template: '
{{ message }}
' }, ApiErrorMeta: true } } }) } beforeEach(() => vi.clearAllMocks()) afterEach(() => vi.useRealTimers()) it('shows synchronization after a successful save and continues by reading without resubmitting', async () => { vi.useFakeTimers() vi.mocked(resolveBatchWork).mockRejectedValueOnce(new ApiResponseError('预约批次或运输关联已变化,服务端正在协调完整工作清单', 'A0409', 409)).mockResolvedValue({ batchKey: 'FBA_APPOINTMENT:40', customerId: null, detail: workDetail({ item: workItem({ workItemKey: 'actual-key' }) }), candidates: [] }) const wrapper = render(); await flushPromises() expect(wrapper.text()).toContain('同步中') expect(wrapper.text()).toContain('无需重复提交') expect(wrapper.find('[role="alert"]').exists()).toBe(false) await vi.advanceTimersByTimeAsync(2000); await flushPromises() expect(wrapper.emitted('open')).toEqual([['actual-key']]) wrapper.unmount() }) it('requires a customer selection for multiple real work candidates and uses the selected resolver result', async () => { vi.mocked(resolveBatchWork).mockResolvedValueOnce({ batchKey: 'FBA_APPOINTMENT:40', customerId: null, detail: null, candidates: [{ customerId: 'customer-a', workItemKey: 'real-work-a', batchNo: 'AP-40' }, { customerId: 'customer-b', workItemKey: 'real-work-b', batchNo: 'AP-40' }] }).mockResolvedValue({ batchKey: 'FBA_APPOINTMENT:40', customerId: 'customer-a', detail: workDetail({ item: workItem({ workItemKey: 'current-work-a' }) }), candidates: [] }) const wrapper = render(); await flushPromises() expect(wrapper.emitted('open')).toBeUndefined(); expect(wrapper.text()).toContain('IN-A'); expect(wrapper.text()).not.toContain('customer-a') await wrapper.findAll('button')[0]!.trigger('click'); await flushPromises() expect(resolveBatchWork).toHaveBeenLastCalledWith('FBA_APPOINTMENT:40', 'customer-a', expect.any(AbortSignal)) expect(wrapper.emitted('open')).toEqual([['current-work-a']]); wrapper.unmount() }) it('keeps saved results on resolution failure and only retries the read', async () => { vi.mocked(resolveBatchWork).mockRejectedValueOnce(new Error('同步未完成')).mockResolvedValue({ batchKey: 'FBA_APPOINTMENT:40', customerId: null, detail: workDetail({ item: workItem({ workItemKey: 'actual-key' }) }), candidates: [] }) const wrapper = render(); await flushPromises() expect(wrapper.text()).toContain('无需重复提交'); expect(wrapper.emitted('open')).toBeUndefined() await wrapper.get('[role="alert"] button').trigger('click'); await flushPromises() expect(resolveBatchWork).toHaveBeenCalledTimes(2); expect(wrapper.emitted('open')).toEqual([['actual-key']]); wrapper.unmount() })