20260916231505
This commit is contained in:
1 parent
de05498552
commit
182ed86966
126 files changed
+5900
-766
No files matched your search
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 批量导出弹窗:选列、三种导出方式、保存导出列。
|
||||
*
|
||||
* Modal 用 Teleport 挂到 body,所以断言都从 document 上找节点(与 modal-confirm.spec.js 一致)。
|
||||
* 这里守住的是「弹窗参数拼得对不对」—— 导出本身由后端测试覆盖,前端错在这里最容易漏。
|
||||
*/
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
import FmsExportDialog from '@/components/fms-module-list/FmsExportDialog.vue'
|
||||
import { countExportApi, exportListApi } from '@/services/exportService'
|
||||
|
||||
vi.mock('@/services/exportService', () => ({
|
||||
countExportApi: vi.fn(),
|
||||
exportListApi: vi.fn(),
|
||||
}))
|
||||
|
||||
const columns = [
|
||||
{ field: 'b_name', caption: '名称' },
|
||||
{ field: 'b_bz', caption: '备注' },
|
||||
{ field: 'b_amount', caption: '金额' },
|
||||
]
|
||||
|
||||
const panel = () => document.querySelector('.fms-modal')
|
||||
const checkboxes = () => [...document.querySelectorAll('.export-dialog__field input')]
|
||||
const footerButtons = () => [...document.querySelectorAll('.fms-modal-footer button')]
|
||||
const radios = () => [...document.querySelectorAll('.export-dialog__modes input')]
|
||||
|
||||
function buttonByText(text) {
|
||||
return [...document.querySelectorAll('.fms-modal button')].find(
|
||||
(button) => button.textContent.trim() === text,
|
||||
)
|
||||
}
|
||||
|
||||
async function openDialog(props = {}) {
|
||||
const wrapper = mount(FmsExportDialog, {
|
||||
props: {
|
||||
open: false,
|
||||
moduleId: 'v_test',
|
||||
moduleName: '测试模块',
|
||||
columns,
|
||||
...props,
|
||||
},
|
||||
})
|
||||
await wrapper.setProps({ open: true })
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
return wrapper
|
||||
}
|
||||
|
||||
/** 下载按钮(footer 第二个),点击后返回给 exportListApi 的参数 */
|
||||
async function download() {
|
||||
const button = footerButtons()[1]
|
||||
button.click()
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
describe('FmsExportDialog 批量导出', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks()
|
||||
document.body.innerHTML = ''
|
||||
})
|
||||
|
||||
it('打开后渲染全部候选列且默认全选,并显示后端统计的条数', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 8 } })
|
||||
exportListApi.mockResolvedValue('x.xlsx')
|
||||
await openDialog()
|
||||
|
||||
expect(panel()).not.toBeNull()
|
||||
expect(checkboxes()).toHaveLength(3)
|
||||
expect(checkboxes().every((input) => input.checked)).toBe(true)
|
||||
expect(countExportApi).toHaveBeenCalledWith({
|
||||
moduleId: 'v_test',
|
||||
searchCondition: '',
|
||||
})
|
||||
expect(panel().textContent).toContain('8')
|
||||
})
|
||||
|
||||
it('取消勾选后下载只带剩下的列', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 8 } })
|
||||
exportListApi.mockResolvedValue('x.xlsx')
|
||||
await openDialog()
|
||||
|
||||
const remark = checkboxes()[1]
|
||||
remark.checked = false
|
||||
remark.dispatchEvent(new Event('change'))
|
||||
await nextTick()
|
||||
|
||||
await download()
|
||||
|
||||
expect(exportListApi).toHaveBeenCalledTimes(1)
|
||||
const params = exportListApi.mock.calls[0][0]
|
||||
expect(params.moduleId).toBe('v_test')
|
||||
expect(params.columns).toEqual(['b_name', 'b_amount'])
|
||||
// 默认「全部导出」不带区间与分卷
|
||||
expect(params.rangeStart).toBeUndefined()
|
||||
expect(params.volumeSize).toBeUndefined()
|
||||
})
|
||||
|
||||
it('沿用列表已保存的导出列,且保存的顺序优先(其余列补在后面)', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 8 } })
|
||||
exportListApi.mockResolvedValue('x.xlsx')
|
||||
await openDialog({ savedColumns: ['b_amount', 'b_name'] })
|
||||
|
||||
// 显示顺序 = 已保存顺序 + 未保存的列(未勾选)
|
||||
const names = [...document.querySelectorAll('.export-dialog__field-name')].map((n) => n.textContent)
|
||||
expect(names).toEqual(['金额', '名称', '备注'])
|
||||
expect(checkboxes().filter((input) => input.checked)).toHaveLength(2)
|
||||
|
||||
await download()
|
||||
// 导出列顺序跟随弹窗里的显示顺序
|
||||
expect(exportListApi.mock.calls[0][0].columns).toEqual(['b_amount', 'b_name'])
|
||||
})
|
||||
|
||||
it('按条数区间导出带 rangeStart / rangeEnd', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 800 } })
|
||||
exportListApi.mockResolvedValue('x.xlsx')
|
||||
await openDialog()
|
||||
|
||||
radios()[1].checked = true
|
||||
radios()[1].dispatchEvent(new Event('change'))
|
||||
await nextTick()
|
||||
|
||||
const numbers = [...document.querySelectorAll('.export-dialog__params input')]
|
||||
expect(numbers).toHaveLength(2)
|
||||
numbers[0].value = '101'
|
||||
numbers[0].dispatchEvent(new Event('input'))
|
||||
numbers[1].value = '200'
|
||||
numbers[1].dispatchEvent(new Event('input'))
|
||||
await nextTick()
|
||||
|
||||
await download()
|
||||
const params = exportListApi.mock.calls[0][0]
|
||||
expect(params.rangeStart).toBe(101)
|
||||
expect(params.rangeEnd).toBe(200)
|
||||
expect(params.volumeSize).toBeUndefined()
|
||||
})
|
||||
|
||||
it('分页拆分导出带 volumeSize 且不输出合计', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 800 } })
|
||||
exportListApi.mockResolvedValue('x.zip')
|
||||
await openDialog()
|
||||
|
||||
radios()[2].checked = true
|
||||
radios()[2].dispatchEvent(new Event('change'))
|
||||
await nextTick()
|
||||
|
||||
const sizeInput = document.querySelector('.export-dialog__params input')
|
||||
sizeInput.value = '300'
|
||||
sizeInput.dispatchEvent(new Event('input'))
|
||||
await nextTick()
|
||||
|
||||
await download()
|
||||
const params = exportListApi.mock.calls[0][0]
|
||||
expect(params.volumeSize).toBe(300)
|
||||
expect(params.rangeStart).toBeUndefined()
|
||||
})
|
||||
|
||||
it('区间不合法时不发起请求', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 800 } })
|
||||
await openDialog()
|
||||
|
||||
radios()[1].checked = true
|
||||
radios()[1].dispatchEvent(new Event('change'))
|
||||
await nextTick()
|
||||
|
||||
const numbers = [...document.querySelectorAll('.export-dialog__params input')]
|
||||
numbers[0].value = '500'
|
||||
numbers[0].dispatchEvent(new Event('input'))
|
||||
numbers[1].value = '100'
|
||||
numbers[1].dispatchEvent(new Event('input'))
|
||||
await nextTick()
|
||||
|
||||
await download()
|
||||
expect(exportListApi).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('一列都不选时不发起请求', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 8 } })
|
||||
await openDialog()
|
||||
|
||||
for (const input of checkboxes()) {
|
||||
input.checked = false
|
||||
input.dispatchEvent(new Event('change'))
|
||||
}
|
||||
await nextTick()
|
||||
await download()
|
||||
|
||||
expect(exportListApi).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('导出方式:标题与 Segmented 同行,参数行是它下面的独立一行', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 800 } })
|
||||
await openDialog()
|
||||
|
||||
const mode = document.querySelector('.export-dialog__mode')
|
||||
// 直接子元素顺序:标题、Segmented、(选中区间/分卷时的)参数行
|
||||
const children = [...mode.children].map((node) => node.className)
|
||||
expect(children[0]).toContain('export-dialog__title')
|
||||
expect(children[1]).toContain('export-dialog__modes')
|
||||
|
||||
// 「全部」模式没有参数行
|
||||
expect(mode.querySelector('.export-dialog__params')).toBeNull()
|
||||
|
||||
radios()[1].checked = true
|
||||
radios()[1].dispatchEvent(new Event('change'))
|
||||
await nextTick()
|
||||
|
||||
// 参数行是 mode 的直接子元素(grid 第二行第二列),不是包在另一层容器里
|
||||
const params = document.querySelector('.export-dialog__mode > .export-dialog__params')
|
||||
expect(params).not.toBeNull()
|
||||
expect([...mode.children].indexOf(params)).toBe(2)
|
||||
})
|
||||
|
||||
it('保存配置上抛字段;全选等于回到列表列并清掉个人覆盖', async () => {
|
||||
countExportApi.mockResolvedValue({ data: { total: 8 } })
|
||||
const wrapper = await openDialog({ savedColumns: ['b_name'] })
|
||||
|
||||
buttonByText('保存配置').click()
|
||||
await nextTick()
|
||||
expect(wrapper.emitted('save-columns')?.at(-1)).toEqual([['b_name']])
|
||||
|
||||
buttonByText('全选').click()
|
||||
await nextTick()
|
||||
// 全选 = 回到列表列,顺手清掉个人覆盖(空数组)
|
||||
expect(wrapper.emitted('save-columns')?.at(-1)).toEqual([[]])
|
||||
expect(checkboxes().every((input) => input.checked)).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user