126 lines
4.1 KiB
JavaScript
126 lines
4.1 KiB
JavaScript
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()
|
||
})
|
||
})
|