20260914223013
This commit is contained in:
1 parent
549c8bd2eb
commit
63514476d7
60 files changed
+6478
-2004
No files matched your search
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Modal.confirm 命令式确认对话框:开关时机与 onOk / onCancel 语义。
|
||||
* 覆盖「确定后关闭」「onOk reject 保持打开」「取消」「点遮罩静默关闭(不触发 onCancel)」
|
||||
* 「destroy 立即卸载」几种路径。
|
||||
*/
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises } from '@vue/test-utils'
|
||||
import { nextTick } from 'vue'
|
||||
import { confirm } from '@/components/ui/modal/modal-confirm.js'
|
||||
|
||||
/** Modal 用 Teleport 挂到 body,只能从 document 上找面板 */
|
||||
const panel = () => document.querySelector('.fms-modal')
|
||||
/** footer 按钮顺序:[取消, 确定] */
|
||||
const footerButtons = () => [...document.querySelectorAll('.fms-modal-footer button')]
|
||||
|
||||
/**
|
||||
* 关窗要等 Transition 的两帧 rAF 与 after-leave,时序不确定:
|
||||
* 轮询到条件成立或超时为止。
|
||||
*/
|
||||
async function waitFor(predicate, timeout = 500) {
|
||||
const start = Date.now()
|
||||
while (Date.now() - start < timeout) {
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
if (predicate()) return true
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let active = null
|
||||
|
||||
afterEach(async () => {
|
||||
active?.destroy()
|
||||
active = null
|
||||
await waitFor(() => !panel())
|
||||
})
|
||||
|
||||
describe('Modal.confirm', () => {
|
||||
it('确定:onOk resolve 后对话框关闭', async () => {
|
||||
const onOk = vi.fn()
|
||||
active = confirm({ title: '确认', content: '删除后不可恢复', onOk })
|
||||
|
||||
expect(panel()).toBeTruthy()
|
||||
expect(document.body.textContent).toContain('删除后不可恢复')
|
||||
|
||||
footerButtons().at(-1).click()
|
||||
|
||||
expect(await waitFor(() => !panel())).toBe(true)
|
||||
expect(onOk).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('确定:onOk reject 时保持打开,等待重试或取消', async () => {
|
||||
const onOk = vi.fn(() => Promise.reject(new Error('删除失败')))
|
||||
active = confirm({ title: '确认', content: '内容', onOk })
|
||||
|
||||
footerButtons().at(-1).click()
|
||||
// 等一段确定的时间,确认对话框没有进入关闭流程
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
await flushPromises()
|
||||
|
||||
expect(onOk).toHaveBeenCalledTimes(1)
|
||||
expect(panel()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('取消:onCancel 被调用并关闭', async () => {
|
||||
const onCancel = vi.fn()
|
||||
active = confirm({ title: '确认', content: '内容', onCancel })
|
||||
|
||||
footerButtons()[0].click()
|
||||
|
||||
expect(await waitFor(() => !panel())).toBe(true)
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('点遮罩空白:关闭对话框但不触发 onCancel(静默关闭)', async () => {
|
||||
const onCancel = vi.fn()
|
||||
active = confirm({ title: '确认', content: '内容', onCancel })
|
||||
|
||||
const wrap = document.querySelector('.fms-modal-wrap')
|
||||
expect(wrap).toBeTruthy()
|
||||
// 点击 wrap 自身(面板外空白区域):target === currentTarget 才命中遮罩关闭分支。
|
||||
// 遮罩关闭走 Modal.requestClose → emit('cancel'),但 confirm 未绑 @cancel,
|
||||
// 故只由 v-model:open 关窗,不应触发调用方 onCancel。
|
||||
wrap.click()
|
||||
|
||||
expect(await waitFor(() => !panel())).toBe(true)
|
||||
expect(onCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('okButtonProps.danger 映射为危险态确定按钮', () => {
|
||||
active = confirm({ title: '确认', content: '内容', okButtonProps: { danger: true } })
|
||||
|
||||
expect(footerButtons().at(-1).className).toContain('button-danger')
|
||||
})
|
||||
|
||||
it('destroy 立即卸载容器与遮罩', async () => {
|
||||
active = confirm({ title: '确认', content: '内容' })
|
||||
expect(panel()).toBeTruthy()
|
||||
|
||||
active.destroy()
|
||||
active = null
|
||||
await waitFor(() => !panel())
|
||||
|
||||
expect(panel()).toBeFalsy()
|
||||
expect(document.querySelector('.fms-modal-wrap')).toBeFalsy()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user