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

186 lines
6.5 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 MasterDataSettings from './MasterDataSettings.vue'
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../../api/masterData'
vi.mock('../../api/masterData', () => ({
materialCategoriesApi: { list: vi.fn(), create: vi.fn(), update: vi.fn() },
unitsApi: { list: vi.fn(), create: vi.fn(), update: vi.fn() },
codeRulesApi: { list: vi.fn(), create: vi.fn(), update: vi.fn() }
}))
const STANDARD_PART_DESCRIPTION =
'1.核心定义:国家/行业通用标准化零件,型号固定,无需绘图、无需任何二次加工。 2.包含说明:设备通用紧固、锁紧、防松类标准配件,采用全行业通用规格,无非标改造。 3.现场举例:内六角螺钉、沉头螺钉、组合螺钉。 4.边界区分:国标通用紧固配件统一归此类;非标定制紧固结构件归入 A04。'
// 真实的 ElSelect 在 jsdom 下会因状态双向绑定反复触发自身更新而报
// "Maximum recursive updates exceeded"。这里只关心表单校验,用轻量替身绕开。
const SelectStub = {
props: ['modelValue', 'placeholder'],
emits: ['update:modelValue', 'change'],
template: '<div data-test="select"><slot /></div>'
}
const OptionStub = {
props: ['label', 'value'],
template: '<span class="option">{{ label }}</span>'
}
function mountSettings() {
return mount(MasterDataSettings, {
global: {
plugins: [ElementPlus],
stubs: { teleport: true, ElSelect: SelectStub, ElOption: OptionStub }
}
})
}
function detailButtons(wrapper) {
return wrapper.findAll('button').filter(button => button.text() === '描述详情')
}
describe('物料分类描述详情', () => {
beforeEach(() => {
materialCategoriesApi.list.mockResolvedValue({
data: [
{ id: 1, code: 'A', name: '机械结构类', description: '纯机械传动件纳入本类管理。', parent_id: null, status: 'active' },
{ id: 2, code: 'A01', name: '标准件', description: STANDARD_PART_DESCRIPTION, parent_id: 1, status: 'active' },
{ id: 3, code: 'A02', name: '成品件', description: '', parent_id: 1, status: 'active' }
]
})
unitsApi.list.mockResolvedValue({ data: [] })
codeRulesApi.list.mockResolvedValue({ data: [] })
})
test('每一行都提供描述详情按钮,描述正文不直接铺在表格里', async () => {
const wrapper = mountSettings()
await flushPromises()
expect(detailButtons(wrapper)).toHaveLength(3)
expect(wrapper.text()).not.toContain('国家/行业通用标准化零件')
wrapper.unmount()
})
test('点击描述详情后弹窗展示该行的完整描述', async () => {
const wrapper = mountSettings()
await flushPromises()
await detailButtons(wrapper)[1].trigger('click')
await flushPromises()
expect(wrapper.text()).toContain('A01 标准件')
expect(wrapper.text()).toContain(STANDARD_PART_DESCRIPTION)
expect(wrapper.text()).not.toContain('纯机械传动件纳入本类管理。')
wrapper.unmount()
})
test('描述为空时弹窗显示占位文案', async () => {
const wrapper = mountSettings()
await flushPromises()
await detailButtons(wrapper)[2].trigger('click')
await flushPromises()
expect(wrapper.text()).toContain('A02 成品件')
expect(wrapper.text()).toContain('暂无描述')
wrapper.unmount()
})
})
describe('基础配置表单必填校验', () => {
let warnSpy
// 表单项没有 placeholder,按 label 文本定位输入框。
function inputByLabel(wrapper, label) {
return wrapper.findAll('.el-form-item')
.find(node => node.find('.el-form-item__label').text() === label)
.find('input')
}
function findButton(wrapper, text) {
return wrapper.findAll('button').find(button => button.text().includes(text))
}
async function openCreateDialog(wrapper) {
await findButton(wrapper, '新增').trigger('click')
await flushPromises()
}
async function clickSave(wrapper) {
await findButton(wrapper, '保存').trigger('click')
await flushPromises()
}
async function switchTab(wrapper, label) {
await wrapper.findAll('.el-tabs__item').find(node => node.text() === label).trigger('click')
await flushPromises()
}
beforeEach(() => {
warnSpy = vi.spyOn(ElMessage, 'warning').mockImplementation(() => {})
materialCategoriesApi.list.mockResolvedValue({ data: [] })
materialCategoriesApi.create.mockReset().mockResolvedValue({ data: {} })
materialCategoriesApi.update.mockReset().mockResolvedValue({ data: {} })
unitsApi.list.mockResolvedValue({ data: [] })
unitsApi.create.mockReset().mockResolvedValue({ data: {} })
codeRulesApi.list.mockResolvedValue({ data: [] })
codeRulesApi.create.mockReset().mockResolvedValue({ data: {} })
})
afterEach(() => {
vi.restoreAllMocks()
})
test('物料分类 tab:编码留空被拦下', async () => {
const wrapper = mountSettings()
await flushPromises()
await openCreateDialog(wrapper)
await clickSave(wrapper)
expect(materialCategoriesApi.create).not.toHaveBeenCalled()
expect(warnSpy).toHaveBeenCalledWith('请填写编码')
wrapper.unmount()
})
test('物料分类 tab:只填了编码时提示名称', async () => {
const wrapper = mountSettings()
await flushPromises()
await openCreateDialog(wrapper)
await inputByLabel(wrapper, '编码').setValue('A03')
await clickSave(wrapper)
expect(materialCategoriesApi.create).not.toHaveBeenCalled()
expect(warnSpy).toHaveBeenCalledWith('请填写名称')
wrapper.unmount()
})
test('编码规则 tab:分类未选时提示物料分类', async () => {
const wrapper = mountSettings()
await flushPromises()
await switchTab(wrapper, '编码规则')
await openCreateDialog(wrapper)
await clickSave(wrapper)
expect(codeRulesApi.create).not.toHaveBeenCalled()
expect(warnSpy).toHaveBeenCalledWith('请选择物料分类')
wrapper.unmount()
})
test('编码规则 tab 不校验编码与名称', async () => {
const wrapper = mountSettings()
await flushPromises()
await switchTab(wrapper, '编码规则')
await openCreateDialog(wrapper)
// 分类和前缀都为空,应当只提示分类,不应冒出"请填写编码"
await clickSave(wrapper)
expect(warnSpy).not.toHaveBeenCalledWith('请填写编码')
expect(warnSpy).not.toHaveBeenCalledWith('请填写名称')
wrapper.unmount()
})
})