写操作后原地重新拉取列表时,status 参数被写死成 '':列表悄悄退回全量(已通过、 已拒绝的单子全冒出来),而页面下拉框仍显示着用户选中的筛选值。用户以为看到的 是筛选结果,实际不是。store 层一直有这个缺陷却没被拦住,因为页面那层没有测试。 修法照 stock.js 已有的约定(stockFilters 配合 stock.test.js 的断言):store 存一份 filters,写操作原地刷新时取出来用。 - approval.js:approve / reject 沿用筛选 - application.js:cancel 沿用筛选 - application.js:create 保持清空筛选并回第 1 页——刚提交的是待审批单,若沿用 「已通过」之类的筛选就看不见它,用户会以为没提交成功 - Application.vue:提交成功后同步把下拉框重置为「全部」,否则 store 清了筛选而 下拉没清,同样是表里不一 测试补 9 条(approval/application 两个 store + Application 页面层)。全部做过变异 验证:把实现改回原样后对应断言确实变红,且只有该变红的那几条变红。
109 lines
3.7 KiB
JavaScript
109 lines
3.7 KiB
JavaScript
import { flushPromises, mount } from '@vue/test-utils'
|
||
import ElementPlus, { ElMessage } from 'element-plus'
|
||
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
||
import Application from './Application.vue'
|
||
|
||
const applicationStore = vi.hoisted(() => ({
|
||
myApplications: [],
|
||
loading: false,
|
||
myPage: { currentPage: 1, pageSize: 20, total: 0, totalPages: 0 },
|
||
fetchMyApplications: vi.fn(),
|
||
createApplication: vi.fn(),
|
||
cancelApplication: vi.fn()
|
||
}))
|
||
|
||
const materialsStore = vi.hoisted(() => ({
|
||
materialOptions: [],
|
||
fetchMaterialOptions: vi.fn()
|
||
}))
|
||
|
||
const stockStore = vi.hoisted(() => ({
|
||
fetchStock: vi.fn(),
|
||
fetchStockByMaterialId: vi.fn()
|
||
}))
|
||
|
||
vi.mock('../stores/application', () => ({ useApplicationStore: () => applicationStore }))
|
||
vi.mock('../stores/materials', () => ({ useMaterialsStore: () => materialsStore }))
|
||
vi.mock('../stores/stock', () => ({ useStockStore: () => stockStore }))
|
||
|
||
// 真实的 ElSelect 在 jsdom 下会因状态双向绑定反复触发自身更新而报
|
||
// "Maximum recursive updates exceeded"。这里用替身直接派发 change,
|
||
// 模拟用户在状态下拉里选中某一项。
|
||
const SelectStub = {
|
||
props: ['modelValue', 'placeholder', 'filterable', 'filterMethod', 'noDataText', 'disabled', 'clearable'],
|
||
emits: ['update:modelValue', 'change'],
|
||
methods: {
|
||
pick(value) {
|
||
this.$emit('update:modelValue', value)
|
||
this.$emit('change', value)
|
||
}
|
||
},
|
||
template: '<div class="select-stub"></div>'
|
||
}
|
||
|
||
const OptionStub = { props: ['label', 'value'], template: '<span />' }
|
||
|
||
function mountApplication() {
|
||
return mount(Application, {
|
||
global: {
|
||
plugins: [ElementPlus],
|
||
stubs: { teleport: true, ElSelect: SelectStub, ElOption: OptionStub }
|
||
}
|
||
})
|
||
}
|
||
|
||
// 页面里有两处 ElSelect:申请表单里的物料选择,和「我的申请」里的状态筛选。
|
||
function statusSelect(wrapper) {
|
||
return wrapper.findAllComponents(SelectStub)
|
||
.find(select => select.props('placeholder') === '全部状态')
|
||
}
|
||
|
||
function setupState(wrapper) {
|
||
return wrapper.vm.$.setupState
|
||
}
|
||
|
||
describe('提交申请后筛选条件与列表保持一致', () => {
|
||
let successSpy
|
||
|
||
beforeEach(() => {
|
||
vi.clearAllMocks()
|
||
applicationStore.createApplication.mockResolvedValue({ data: {} })
|
||
successSpy = vi.spyOn(ElMessage, 'success').mockImplementation(() => {})
|
||
})
|
||
|
||
test('状态下拉选中的值会传给列表查询', async () => {
|
||
const wrapper = mountApplication()
|
||
await flushPromises()
|
||
|
||
statusSelect(wrapper).vm.pick('approved')
|
||
await flushPromises()
|
||
|
||
expect(setupState(wrapper).statusFilter).toBe('approved')
|
||
expect(applicationStore.fetchMyApplications).toHaveBeenLastCalledWith('approved', 1)
|
||
wrapper.unmount()
|
||
})
|
||
|
||
// store 里提交后的那次刷新是刻意清空筛选的(好让刚提交的待审批单出现在列表里),
|
||
// 页面这份状态(下拉框)必须跟着一起回到「全部」:否则会出现下拉写着「已通过」、
|
||
// 列表却是全量的表里不一,这次修复就是补的这一处。
|
||
test('提交成功后下拉框的筛选要一起重置为全部', async () => {
|
||
const wrapper = mountApplication()
|
||
await flushPromises()
|
||
|
||
statusSelect(wrapper).vm.pick('approved')
|
||
await flushPromises()
|
||
expect(setupState(wrapper).statusFilter).toBe('approved')
|
||
|
||
setupState(wrapper).applyForm.purpose = '测试用途'
|
||
setupState(wrapper).applyForm.items = [
|
||
{ material_id: 7, materialSearchText: '', quantity: 1 }
|
||
]
|
||
await setupState(wrapper).handleSubmit()
|
||
await flushPromises()
|
||
|
||
expect(applicationStore.createApplication).toHaveBeenCalled()
|
||
expect(setupState(wrapper).statusFilter).toBe('')
|
||
wrapper.unmount()
|
||
})
|
||
})
|