Files
workspace/code/fms/fms-vue/tests/components/popover.spec.js
T
oneao 05d0c7f304 chore(fms-vue): WIP 基线快照——收编工作区未提交的前端改动
不含 fms-api Java 侧改动与后续导航状态化阶段提交。主要内容:
- ui 组件库:date/dropdown/form/input/popover/select/tooltip 调整,
  新增 Segmented 组件;fms-table 可编辑单元格渲染器与 FileCard 更新
- 模块管理:列表配置加载自动补全、查询缺配徽标与一键补充、
  结构编辑内核 useStructureEditing/useBandLayout、子表表格编辑方式、
  分组标题多语言、I18nKeyCell 等(含配套 spec)
- 布局/主题/仪表盘/演示页小幅更新
2026-08-30 20:06:19 +08:00

161 lines
5.6 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 { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { nextTick, ref } from 'vue'
import Popover from '@/components/ui/popover/popover.vue'
import Button from '@/components/ui/button/button.vue'
/** 查询 Teleport 到 body 的浮层(Transition + v-if 渲染) */
function bodyPopover() {
return document.body.querySelector('.popover')
}
async function flush() {
await nextTick()
}
describe('Popover 触发器包裹', () => {
it('默认(unwrapped=false)渲染 span.popover-trigger 包裹层', () => {
const wrapper = mount(Popover, {
props: { trigger: 'click' },
slots: {
trigger: '<button class="t-btn">触发</button>',
content: '<div class="t-content">内容</div>',
},
})
expect(wrapper.find('span.popover-trigger').exists()).toBe(true)
expect(wrapper.find('button.t-btn').exists()).toBe(true)
wrapper.unmount()
})
it('unwrapped 单根元素:不渲染包裹层 span,仅保留用户触发器', () => {
const wrapper = mount(Popover, {
props: { trigger: 'click', unwrapped: true },
slots: {
trigger: '<button class="t-btn">触发</button>',
content: '<div class="t-content">内容</div>',
},
})
expect(wrapper.find('span.popover-trigger').exists()).toBe(false)
expect(wrapper.find('button.t-btn').exists()).toBe(true)
wrapper.unmount()
})
it('unwrapped 多根 fragment:自动回退到 span.popover-trigger 包裹层', () => {
const wrapper = mount(Popover, {
props: { trigger: 'click', unwrapped: true },
slots: {
trigger: '<button>A</button><button>B</button>',
content: '<div class="t-content">内容</div>',
},
})
expect(wrapper.find('span.popover-trigger').exists()).toBe(true)
expect(wrapper.findAll('span.popover-trigger > button').length).toBe(2)
wrapper.unmount()
})
it('unwrapped 文本节点:回退到包裹层', () => {
const wrapper = mount(Popover, {
props: { trigger: 'click', unwrapped: true },
slots: {
trigger: '纯文本触发器',
content: '<div class="t-content">内容</div>',
},
})
expect(wrapper.find('span.popover-trigger').exists()).toBe(true)
wrapper.unmount()
})
})
describe('Popover 零包裹交互', () => {
it('unwrapped 点击触发器可开合浮层(事件注入到用户 vnode)', async () => {
const wrapper = mount(Popover, {
props: { trigger: 'click', unwrapped: true },
slots: {
trigger: '<button class="t-btn">触发</button>',
content: '<div class="t-content">内容</div>',
},
})
expect(bodyPopover()).toBeNull()
await wrapper.find('button.t-btn').trigger('click')
await flush()
expect(bodyPopover()).not.toBeNull()
await wrapper.find('button.t-btn').trigger('click')
await flush()
expect(bodyPopover()).toBeNull()
wrapper.unmount()
})
it('unwrapped 浮层有 left/top 定位(证明锚点指令真正挂载:vnode.dirs 字段名)', async () => {
// 回归测试:曾因把指令注入到 vnode.directives 字段(应为 vnode.dirs),
// 导致锚点指令静默失效、triggerEl 永远 null、updatePosition 提前 return、
// 浮层无定位在 body 末尾不可见。jsdom 矩形=0,updatePosition 期望写出 0px。
const wrapper = mount(Popover, {
props: { trigger: 'click', unwrapped: true },
slots: {
trigger: '<button class="t-btn">触发</button>',
content: '<div class="t-content">内容</div>',
},
})
await wrapper.find('button.t-btn').trigger('click')
await flush()
const float = bodyPopover()
expect(float).not.toBeNull()
// 锚点指令必须已挂载并同步 triggerEl,updatePosition 才能写 left/top;
// 若 vnode.dirs 拼成 directives(被静默忽略),style.left 保持空串。
// jsdom 矩形=0,具体值取决于 computePosition 的 shift 防溢出,仅断言非空即可。
expect(float.style.left).not.toBe('')
expect(float.style.top).not.toBe('')
wrapper.unmount()
})
it('事件合并:用户 @click 与内部 toggle 均触发,不互相覆盖', async () => {
const TestHost = {
components: { Popover },
setup() {
const userClicks = ref(0)
return { userClicks }
},
template: `
<div>
<Popover trigger="click" unwrapped>
<template #trigger>
<button class="t-btn" @click="userClicks++">触发</button>
</template>
<template #content><div class="t-content">内容</div></template>
</Popover>
<span class="t-counter">{{ userClicks }}</span>
</div>
`,
}
const wrapper = mount(TestHost)
await wrapper.find('button.t-btn').trigger('click')
await flush()
// 用户 handler 先执行
expect(wrapper.find('.t-counter').text()).toBe('1')
// 内部 toggle 后执行 → 浮层打开
expect(bodyPopover()).not.toBeNull()
wrapper.unmount()
})
it('组件作触发器:事件注入到组件根元素,点击可开合', async () => {
const TestHost = {
components: { Popover, Button },
template: `
<Popover trigger="click" unwrapped>
<template #trigger><Button>点击</Button></template>
<template #content><div class="t-content">内容</div></template>
</Popover>
`,
}
const wrapper = mount(TestHost)
const btn = wrapper.find('button')
expect(btn.exists()).toBe(true)
await btn.trigger('click')
await flush()
expect(bodyPopover()).not.toBeNull()
wrapper.unmount()
})
})