This commit is contained in:
2026-10-14 11:02:56 +08:00
parent 3f728cd0b2
commit e0f54edcc9
24 changed files with 352 additions and 105 deletions
@@ -32,6 +32,27 @@ beforeEach(() => {
})
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()); vi.useRealTimers(); vi.restoreAllMocks() })
describe('warehouse operating dashboard', () => {
it.each(['intervalOutboundQuantity', 'averageDailyOutboundQuantity', 'physicalQuantity', 'availableQuantity', 'physicalSkuCount', 'noOutboundSkuCount'] as const)('keeps the page usable when the response omits %s and recovers after refresh', async key => {
const invalid = structuredClone(dashboardFixture)
delete (invalid.kpis as Partial<typeof invalid.kpis>)[key]
mocks.dashboard.mockResolvedValue(invalid)
const wrapper = page(); await choose(wrapper, { warehouseCode: 'WH-A' })
expect(wrapper.text()).toContain('经营总览指标返回不完整')
expect(wrapper.find('.dashboard-kpis').exists()).toBe(false)
expect(wrapper.findComponent(query).exists()).toBe(true)
mocks.dashboard.mockResolvedValue(dashboardFixture)
await findButton(wrapper, '整体刷新').trigger('click'); await flushPromises()
expect(wrapper.findAll('.dashboard-kpis section')).toHaveLength(6)
expect(wrapper.text()).not.toContain('经营总览指标返回不完整')
})
it('preserves the last valid result when a refreshed response has a null KPI', async () => {
const wrapper = page(); await choose(wrapper, { warehouseCode: 'WH-A' })
mocks.dashboard.mockResolvedValue({ ...dashboardFixture, kpis: { ...dashboardFixture.kpis, averageDailyOutboundQuantity: null } })
await findButton(wrapper, '整体刷新').trigger('click'); await flushPromises()
expect(wrapper.text()).toContain('经营总览指标返回不完整,请更新服务后重新查询;展示上次查询')
expect(wrapper.findAll('.dashboard-kpis section')).toHaveLength(6)
})
it('offers coverage evidence for both inventory rankings even when they are empty', async () => {
const data = structuredClone(dashboardFixture)
data.longCoverageSkus = { ...data.longCoverageSkus, items: [], eligibleCount: '0', unassessableCount: '2711', dataStatus: 'PARTIAL' }
@@ -151,7 +172,7 @@ describe('warehouse operating dashboard', () => {
expect(findButton(wrapper, '查看Top100').attributes('disabled')).toBeUndefined()
})
it('keeps inventory present without published sales and never derives a daily average from partial sales', async () => {
mocks.dashboard.mockResolvedValue({ ...dashboardFixture, context: { ...dashboardFixture.context, salesRunId: null, salesScopeHash: null }, kpis: { ...dashboardFixture.kpis, averageDailyOutboundQuantity30d: { ...dashboardFixture.kpis.averageDailyOutboundQuantity30d, value: null, knownSubtotal: null, dataStatus: 'UNKNOWN', reasonCodes: ['SALES_UNAVAILABLE'] }, physicalQuantity: { ...dashboardFixture.kpis.physicalQuantity, value: '128', dataStatus: 'COMPLETE' } }, salesTrend: { ...dashboardFixture.salesTrend, points: [] } })
mocks.dashboard.mockResolvedValue({ ...dashboardFixture, context: { ...dashboardFixture.context, salesRunId: null, salesScopeHash: null }, kpis: { ...dashboardFixture.kpis, averageDailyOutboundQuantity: { ...dashboardFixture.kpis.averageDailyOutboundQuantity, value: null, knownSubtotal: null, dataStatus: 'UNKNOWN', reasonCodes: ['SALES_UNAVAILABLE'] }, physicalQuantity: { ...dashboardFixture.kpis.physicalQuantity, value: '128', dataStatus: 'COMPLETE' } }, salesTrend: { ...dashboardFixture.salesTrend, points: [] } })
const wrapper = page(); await choose(wrapper, { warehouseCode: 'WH-A' })
expect(wrapper.findAll('.dashboard-kpis strong')[1]!.text()).toBe('—')
expect(wrapper.findAll('.dashboard-kpis strong')[2]!.text()).toBe('128')
@@ -167,12 +188,15 @@ describe('warehouse operating dashboard', () => {
expect(wrapper.findAll('.dashboard-kpis section')).toHaveLength(6)
expect(wrapper.findAllComponents(DashboardBars)).toHaveLength(4)
expect(wrapper.findAllComponents(DashboardTrend)).toHaveLength(1)
expect(wrapper.text()).toContain('所选时段无出库库存Top10')
expect(wrapper.text()).not.toContain('近30日无出库')
expect(wrapper.findAllComponents(DashboardBars).slice(1).every(chart => chart.text().includes('2026-10-10 至 2026-10-14'))).toBe(true)
expect(wrapper.text()).toContain('区间已采集出库量')
expect(wrapper.text()).toContain('已知小计 128')
expect(wrapper.text()).toContain('另 1 SKU 待核验')
expect(wrapper.text()).not.toContain('lingxing-primary')
})
it('changes only trend range while pinning versions, then explicitly refreshes the whole snapshot', async () => {
it('changes the shared date range while pinning versions, then explicitly refreshes the whole snapshot', async () => {
const wrapper = page(); await choose(wrapper, { warehouseCode: 'WH-A' })
await choose(wrapper, { rangePreset: 'TODAY' }); await findButton(wrapper, '查询').trigger('click'); await flushPromises()
expect(mocks.dashboard.mock.calls.at(-1)?.[0]).toMatchObject({ rangePreset: 'TODAY', salesRunId: '11', inventoryRunId: '12', queryAsOf: dashboardFixture.context.queryAsOf })
@@ -214,7 +238,7 @@ describe('warehouse operating dashboard', () => {
mocks.daily.mockImplementationOnce(() => new Promise(resolve => { finish = resolve }))
const wrapper = page(); await choose(wrapper, { warehouseCode: 'WH-A' })
wrapper.findAllComponents(DashboardBars)[1]!.vm.$emit('select', '1'); await flushPromises()
expect(mocks.daily.mock.calls[0]?.[0]).toMatchObject({ customerCode: 'OWNER-B', sku: 'SKU-X', rangePreset: 'CUSTOM', startDate: '2026-09-13', endDate: '2026-10-12', salesRunId: '11' })
expect(mocks.daily.mock.calls[0]?.[0]).toMatchObject({ customerCode: 'OWNER-B', sku: 'SKU-X', rangePreset: 'CUSTOM', startDate: '2026-10-10', endDate: '2026-10-13', salesRunId: '11' })
expect(wrapper.findAllComponents(dialog).find(item => item.props('title') === 'SKU每日出库趋势')!.props('width')).toBe('1120px')
for (const modal of wrapper.findAllComponents(dialog)) expect(modal.props('appendToBody')).not.toBeUndefined()
wrapper.findAllComponents(dialog).find(item => item.props('title') === 'SKU每日出库趋势')!.vm.$emit('update:modelValue', false); await flushPromises()
@@ -256,7 +280,7 @@ describe('warehouse operating dashboard', () => {
await chart.findAll('tbody button')[0]!.trigger('click')
expect(mocks.push.mock.calls.at(-1)?.[0].query).toMatchObject({ analysis: 'SKU', rangePreset: 'CUSTOM', startDate: '2026-10-10', endDate: '2026-10-10' })
await findButton(wrapper, '查看Top100').trigger('click')
expect(mocks.push.mock.calls.at(-1)?.[0].query).toMatchObject({ analysis: 'HOT', rangePreset: 'CUSTOM', startDate: '2026-09-13', endDate: '2026-10-12' })
expect(mocks.push.mock.calls.at(-1)?.[0].query).toMatchObject({ analysis: 'HOT', rangePreset: 'CUSTOM', startDate: '2026-10-10', endDate: '2026-10-13' })
expect(JSON.parse(mocks.push.mock.calls.at(-1)?.[0].query.context)).toEqual(dashboardFixture.context)
})
it('expires inventory before sales while retaining stock-independent SKU trends', async () => {