import ElementPlus from 'element-plus' import { flushPromises, mount } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' import { getOutbound } from '../api/outbounds' import OutboundDetailView from './OutboundDetailView.vue' const canPermission = vi.hoisted(() => vi.fn((_code: string) => false)) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) })) vi.mock('vue-router', () => ({ useRoute: () => ({ params: { outboundNo: 'OUT/1' } }), useRouter: () => ({ push: vi.fn() }) })) vi.mock('../api/outbounds', () => ({ getOutbound: vi.fn(), cancelOutbound: vi.fn() })) describe('OutboundDetailView', () => { beforeEach(() => { canPermission.mockReset().mockReturnValue(false) vi.mocked(getOutbound).mockReset().mockResolvedValue({ outbound: { id: '1', outboundNo: 'OUT/1', dispatchNo: 'PC-1', status: 'PICKED', palletCount: 2, packageCount: 20, createdAt: '2026-09-01T10:00:00' }, pallets: [{ palletNo: 'IP-ACY2-20260907-597057-P001', inboundCode: 'NYEXX2609060063', destinationWarehouseCode: 'ONT8', destinationWarehouseName: 'Amazon ONT8', skuCode: 'SKU-001', packageCount: 20 }], delivery: { id: '2', customerId: 'customer-1', deliveryNo: 'DEL-1', outboundNo: 'OUT/1', dispatchNo: 'PC-1', status: 'WAIT_DELIVERY', palletCount: 2, packageCount: 20, createdAt: '2026-09-01T10:00:00' }, }) }) it('loads by encoded business outbound number and remains read-only without cancel permission', async () => { const wrapper = mount(OutboundDetailView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(getOutbound).toHaveBeenCalledWith('OUT/1') expect(wrapper.text()).toContain('OUT/1') expect(wrapper.text()).toContain('PC-1') expect(wrapper.text()).toContain('DEL-1') expect(wrapper.text()).toContain('出库托盘') expect(wrapper.text()).toContain('IP-ACY2-20260907-597057-P001') expect(wrapper.text()).toContain('NYEXX2609060063') expect(wrapper.text()).toContain('Amazon ONT8') expect(wrapper.text()).toContain('SKU-001') expect(wrapper.findAll('.outbound-pallet-table .data-table__single-line-cell').length).toBeGreaterThanOrEqual(6) expect(wrapper.text()).not.toContain('取消出库单') wrapper.unmount() }) it('renders pallet missing values consistently and keeps the empty state in the table', async () => { vi.mocked(getOutbound).mockResolvedValueOnce({ outbound: { id: '1', outboundNo: 'OUT/1', status: 'OUTBOUNDED' }, pallets: [{ palletNo: null, inboundCode: null, destinationWarehouseCode: null, destinationWarehouseName: null, skuCode: null, packageCount: null }], delivery: null, }) const wrapper = mount(OutboundDetailView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.get('.outbound-pallet-table').text()).toContain('—') expect(wrapper.text()).toContain('暂无关联派送单') wrapper.unmount() vi.mocked(getOutbound).mockResolvedValueOnce({ outbound: { id: '1', outboundNo: 'OUT/1', status: 'OUTBOUNDED' }, pallets: [], delivery: null, }) const emptyWrapper = mount(OutboundDetailView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(emptyWrapper.text()).toContain('暂无出库托盘') emptyWrapper.unmount() }) it('shows cancellation only for PICKED with the independent permission', async () => { canPermission.mockImplementation((code: string) => code === 'platform:overseas-warehouse:outbound:cancel') const wrapper = mount(OutboundDetailView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('取消出库单') wrapper.unmount() }) })