material_manage_system/frontend/src/views/Stock.test.js
2026-09-21 14:13:30 +08:00

126 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { flushPromises, mount } from '@vue/test-utils'
import ElementPlus, { ElMessage } from 'element-plus'
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import Stock from './Stock.vue'
const stockStore = vi.hoisted(() => ({
stockList: [],
warningList: [],
logs: [],
loading: false,
stockPage: { currentPage: 1, pageSize: 20, total: 0 },
warningPage: { currentPage: 1, pageSize: 20, total: 0 },
logsPage: { currentPage: 1, pageSize: 20, total: 0 },
fetchStock: vi.fn(),
fetchStockLogs: vi.fn(),
fetchStockWarning: vi.fn(),
updateWarningStock: vi.fn()
}))
const materialsStore = vi.hoisted(() => ({
materialOptions: [],
historicalMaterialOptions: [],
fetchMaterialOptions: vi.fn(),
fetchHistoricalMaterialOptions: vi.fn()
}))
vi.mock('../stores/stock', () => ({ useStockStore: () => stockStore }))
vi.mock('../stores/materials', () => ({ useMaterialsStore: () => materialsStore }))
vi.mock('../stores/auth', () => ({ useAuthStore: () => ({ isAdmin: true }) }))
vi.mock('../api/stock', () => ({
stockApi: { getInventoryBalanceDetails: vi.fn().mockResolvedValue({ data: [] }) }
}))
vi.mock('../api/masterData', () => ({
materialCategoriesApi: { list: vi.fn().mockResolvedValue({ data: [] }) }
}))
function findButton(wrapper, text) {
return wrapper.findAll('button').find(button => button.text().includes(text))
}
const TabPaneStub = { template: '<div><slot /></div>' }
function mountStock() {
return mount(Stock, {
global: {
plugins: [ElementPlus],
// WarehouseSettings 及其依赖与本用例无关,直接替身掉;
// ElTabPane 在 jsdom 下会让表格列渲染不出来,用直通替身绕开。
stubs: { teleport: true, WarehouseSettings: true, ElTabPane: TabPaneStub }
}
})
}
describe('库存预警阈值校验', () => {
let warnSpy
beforeEach(() => {
warnSpy = vi.spyOn(ElMessage, 'warning').mockImplementation(() => {})
stockStore.stockList = [{
material_id: 3,
material_code: 'A010203',
name: '测试物料',
total_on_hand: '10.000000',
warning_stock: '5.000000',
warning_enabled: true,
warning_configured: true,
stock_status: 'normal'
}]
stockStore.fetchStock.mockReset()
stockStore.fetchStockLogs.mockReset()
stockStore.fetchStockWarning.mockReset()
stockStore.updateWarningStock.mockReset().mockResolvedValue({ data: {} })
materialsStore.fetchMaterialOptions.mockReset()
materialsStore.fetchHistoricalMaterialOptions.mockReset()
})
afterEach(() => {
// 只恢复 ElMessage 的 spy:restoreAllMocks 会把 vi.mock 工厂里的实现一并清掉,
// 导致下一个用例中 api mock 返回 undefined。
warnSpy.mockRestore()
})
test('阈值被清空时拦下,不提交 null', async () => {
const wrapper = mountStock()
await flushPromises()
// el-table 的行内容在 jsdom 下渲染不出来(列宽依赖真实布局计算),
// 于是跳过表格这一层,直接触发组件的打开逻辑。
wrapper.vm.$.setupState.openWarningDialog(stockStore.stockList[0])
await flushPromises()
// el-input-number 清空后会向 v-model 写 null
const threshold = wrapper.find('.el-input-number input')
await threshold.setValue('')
await threshold.trigger('blur')
await flushPromises()
await findButton(wrapper, '保存').trigger('click')
await flushPromises()
expect(stockStore.updateWarningStock).not.toHaveBeenCalled()
expect(warnSpy).toHaveBeenCalledWith('请填写预警阈值')
wrapper.unmount()
})
test('阈值为 0 是合法配置,可以提交', async () => {
const wrapper = mountStock()
await flushPromises()
wrapper.vm.$.setupState.openWarningDialog(stockStore.stockList[0])
await flushPromises()
const threshold = wrapper.find('.el-input-number input')
await threshold.setValue('0')
await threshold.trigger('blur')
await flushPromises()
await findButton(wrapper, '保存').trigger('click')
await flushPromises()
expect(warnSpy).not.toHaveBeenCalled()
expect(stockStore.updateWarningStock).toHaveBeenCalledTimes(1)
wrapper.unmount()
})
})