113 lines
7.4 KiB
TypeScript
113 lines
7.4 KiB
TypeScript
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<string, string>, query: {} as Record<string, string> } }))
|
||
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<string, string>()
|
||
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)
|
||
})
|
||
})
|