import ElementPlus from 'element-plus' import { flushPromises, mount } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' import { createExceptionOrder, listExceptionOrders, resolveExceptionRelation, getExceptionDetail } from '../api/exceptionOrders' import ExceptionOrdersView from './ExceptionOrdersView.vue' import { commonPhase5Stubs } from './phase5TestStubs' const canPermission = vi.hoisted(() => vi.fn((_code: string) => false)) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) })) vi.mock('../api/exceptionOrders', () => ({ listExceptionOrders: vi.fn(), resolveExceptionRelation: vi.fn(), createExceptionOrder: vi.fn(), getExceptionDetail: vi.fn(), commandException: vi.fn(), uploadExceptionEvidence: vi.fn() })) function mountView() { return mount(ExceptionOrdersView, { global: { plugins: [ElementPlus], stubs: commonPhase5Stubs } }) } describe('ExceptionOrdersView permissions and creation', () => { beforeEach(() => { canPermission.mockReset().mockReturnValue(false) vi.mocked(getExceptionDetail).mockReset().mockResolvedValue({ order: { id: '19', exceptionNo: 'EXC-1', customerId: '100', customerName: '示例客户', exceptionType: 'DAMAGED', status: 'PROCESSING', reason: '包装破损', handlerName: '1000001', version: 1, allowedActions: ['COMPLETE_EXCEPTION'], exceptionAt: '', updatedAt: '' }, returnSources: [], returnReceipts: [] }) vi.mocked(listExceptionOrders).mockReset().mockResolvedValue({ records: [], page: 1, size: 10, total: 0, pages: 0 }) vi.mocked(resolveExceptionRelation).mockReset().mockResolvedValue({ inboundOrderId: '9', inboundCode: 'IN-1', lastMileOrderId: '8', lastMileOrderNo: 'LM-1', customerId: '100' }) vi.mocked(createExceptionOrder).mockReset().mockResolvedValue({ id: '19', exceptionNo: 'EXC-1', status: 'PENDING' }) }) it('hides creation without the independent permission', async () => { const wrapper = mountView() await flushPromises() expect(wrapper.find('[data-testid="data-table"]').text()).not.toContain('新增异常订单') wrapper.unmount() }) it('keeps the detail action available and hides numeric operator identifiers', async () => { vi.mocked(listExceptionOrders).mockResolvedValue({ records: [{ id: '19', exceptionNo: 'EXC-1', exceptionType: 'DAMAGED', status: 'PROCESSING', reason: '包装破损', customerId: '100', customerName: '示例客户', overseasWarehouseName: '洛杉矶仓', handlerName: '1000001', exceptionAt: '2026-09-03T09:00:00', updatedAt: '2026-09-03T10:00:00', }], page: 1, size: 10, total: 1, pages: 1, }) const wrapper = mountView() await flushPromises() const table = wrapper.find('[data-testid="data-table"]') expect(table.text()).toContain('查看') await table.find('button').trigger('click') await flushPromises() const detail = wrapper.find('[data-dialog="异常订单详情"]') expect(detail.text()).toContain('示例客户') expect(detail.text()).toContain('包装破损') expect(detail.text()).not.toContain('1000001') wrapper.unmount() }) it('validates the required relation before submitting', async () => { canPermission.mockImplementation(code => code.endsWith(':create')) const wrapper = mountView() await flushPromises() await wrapper.findAll('button').find(button => button.text().includes('新增异常订单'))!.trigger('click') await wrapper.find('[data-dialog="新增异常订单"] .dialog-confirm').trigger('click') await flushPromises() expect(resolveExceptionRelation).not.toHaveBeenCalled() expect(createExceptionOrder).not.toHaveBeenCalled() wrapper.unmount() }) it('resolves and submits trimmed business fields', async () => { canPermission.mockImplementation(code => code.endsWith(':create')) const wrapper = mountView() await flushPromises() await wrapper.findAll('button').find(button => button.text().includes('新增异常订单'))!.trigger('click') const dialog = wrapper.find('[data-dialog="新增异常订单"]') await dialog.find('input[placeholder="请输入入仓码"]').setValue(' IN-1 ') await dialog.find('select').setValue('RETURN') await dialog.find('textarea').setValue(' 包裹破损 ') await dialog.find('.dialog-confirm').trigger('click') await flushPromises() expect(resolveExceptionRelation).toHaveBeenCalledWith({ customerId: undefined, inboundCode: 'IN-1', lastMileOrderNo: undefined }) expect(createExceptionOrder).toHaveBeenCalledWith({ customerId: undefined, inboundCode: 'IN-1', lastMileOrderNo: 'LM-1', exceptionType: 'RETURN', reason: '包裹破损', packageCount: undefined, skuCode: undefined, }) wrapper.unmount() }) })