import ElementPlus from 'element-plus' import { createPinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { getInventoryPieceProjection, getInventorySummary, listInventoryBalances, listInventoryLedger, listInventoryPositions } from '../api/inventory' import InventoryDetailView from './InventoryDetailView.vue' import InventoryView from './InventoryView.vue' const routing = vi.hoisted(() => ({ push: vi.fn(), route: { params: {} as Record, query: {} as Record } })) vi.mock('vue-router', () => ({ useRoute: () => routing.route, useRouter: () => ({ push: routing.push }) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => false }) })) vi.mock('../api/pallets', () => ({ listReceivingOverseasWarehouses: vi.fn() })) vi.mock('../api/inventory', () => ({ listInventoryBalances: vi.fn(), getInventorySummary: vi.fn(), listInventoryPositions: vi.fn(), listInventoryLedger: vi.fn(), getInventoryPieceProjection: vi.fn(), })) class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} } function memoryStorage(): Storage { const values = new Map() return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value), } } const customerId = '2092618717861048321' const balance = { id: '2092873463568596994', customerId, customerName: '华远贸易', overseasWarehouseId: '200', overseasWarehouseName: '洛杉矶仓', inboundOrderId: '300', inboundOrderNo: 'LIP-1', inboundCode: 'IN-1', destinationWarehouseName: 'ONT8目的仓', skuCode: 'SKU-1', productNameZh: '验收商品', totalPackages: 10, availablePackages: 10, holdPackages: 0, allocatedPackages: 0, pickedPackages: 0, inTransitPackages: 0, receivingPackages: 0, procurementPackages: 0, updatedAt: '2026-08-28T10:00:00', } enableAutoUnmount(afterEach) afterAll(() => vi.unstubAllGlobals()) describe('inventory customer-name snapshots', () => { beforeEach(() => { vi.stubGlobal('ResizeObserver', ResizeObserverStub) vi.stubGlobal('localStorage', memoryStorage()) routing.push.mockReset(); routing.route.params = {}; routing.route.query = {} vi.mocked(listInventoryBalances).mockReset().mockResolvedValue({ records: [balance], page: 1, size: 10, total: 1, pages: 1 }) vi.mocked(getInventorySummary).mockReset().mockResolvedValue({ inboundOrderCount: 1, palletCount: 1, totalPackages: 10, availablePackages: 10, holdPackages: 0, allocatedPackages: 0, pickedPackages: 0, inTransitPackages: 0, receivingPackages: 0, procurementPackages: 0 }) vi.mocked(listInventoryPositions).mockReset().mockResolvedValue([]) vi.mocked(getInventoryPieceProjection).mockReset().mockResolvedValue({ availablePieceCount: null, knownAvailablePieceCount: 0, unknownCartonCount: 10, availableCartonCount: 10, availablePalletCount: 1, groups: [] }) vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }) }) it('renders the formal server customerName in the inventory list', async () => { const wrapper = mount(InventoryView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('华远贸易') expect(wrapper.text()).toContain('洛杉矶仓') expect(wrapper.text()).not.toContain('ONT8目的仓') expect(wrapper.text()).not.toContain(customerId) }) it('renders the same server snapshot in inventory detail', async () => { routing.route.params = { inventoryId: balance.id } routing.route.query = { overseasWarehouseId: balance.overseasWarehouseId, inboundOrderNo: balance.inboundOrderNo, inboundCode: balance.inboundCode, skuCode: balance.skuCode } const wrapper = mount(InventoryDetailView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('华远贸易') expect(wrapper.text()).toContain('洛杉矶仓') expect(wrapper.find('.density-detail-title').text()).toBe(`${balance.inboundOrderNo} / ${balance.inboundCode} / ${balance.skuCode}`) expect(wrapper.text()).toContain('美本土散板') expect(wrapper.text()).not.toContain('美本土散版') expect(wrapper.text()).not.toContain('ONT8目的仓') expect(wrapper.text()).not.toContain(customerId) expect(wrapper.text()).not.toContain(balance.id) }) it('keeps the formal customer and carton list when piece projection fails', async () => { vi.mocked(getInventoryPieceProjection).mockRejectedValueOnce(new Error('projection unavailable')) const wrapper = mount(InventoryView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('华远贸易') expect(wrapper.text()).toContain('可用件数及箱内SKU加载失败') expect(wrapper.text()).not.toContain('库存数据加载失败') }) it('renders every mixed-carton SKU and real piece count alongside carton totals', async () => { vi.mocked(getInventoryPieceProjection).mockResolvedValueOnce({ availablePieceCount: '12', knownAvailablePieceCount: '12', unknownCartonCount: '0', availableCartonCount: '2', availablePalletCount: '1', groups: [{ customerId, warehouseId: '200', destinationCode: null, anchorSkuCode: 'SKU-1', availablePieceCount: '12', knownAvailablePieceCount: '12', unknownCartonCount: '0', availableCartonCount: '2', availablePalletCount: '1', skuContents: [{ skuCode: 'SKU-1', quantity: '5' }, { skuCode: 'SKU-SECOND', quantity: '7' }], cartons: [] }] }) const wrapper = mount(InventoryView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('华远贸易') expect(wrapper.text()).toContain('SKU-1 × 5 件;SKU-SECOND × 7 件') expect(wrapper.text()).toContain('可用 12 件 / 2 箱 / 1 托') }) it('combines the available references and then shows an explicit empty business reference without exposing the inventory id', async () => { vi.mocked(listInventoryBalances).mockResolvedValueOnce({ records: [{ ...balance, inboundOrderNo: '', skuCode: '' }], page: 1, size: 10, total: 1, pages: 1 }) routing.route.params = { inventoryId: balance.id } routing.route.query = { inboundCode: balance.inboundCode } const inboundCodeWrapper = mount(InventoryDetailView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(inboundCodeWrapper.find('.density-detail-title').text()).toBe(`${balance.inboundCode}`) expect(inboundCodeWrapper.text()).not.toContain(balance.id) inboundCodeWrapper.unmount() vi.mocked(listInventoryBalances).mockResolvedValueOnce({ records: [{ ...balance, inboundOrderNo: '', inboundCode: '', skuCode: '' }], page: 1, size: 10, total: 1, pages: 1 }) routing.route.query = {} const emptyWrapper = mount(InventoryDetailView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } }) await flushPromises() expect(emptyWrapper.find('.density-detail-title').text()).toBe('—') expect(emptyWrapper.text()).not.toContain(balance.id) }) })