/** * FmsTreePanel:面板骨架把未声明属性经 $attrs 透传给 FmsTree。 * * 这里用渲染函数而非 props 传参,是为了还原模板编译后的真实形态: * 页面写的是 `:selected-keys`(kebab),编译出来 vnode.props 的键名就是 'selected-keys', * 而不是 'selectedKeys'。用 props 传 camelCase 会绕过 Vue 的键名归一化,测不出问题。 */ import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import { h } from 'vue' import FmsTreePanel from '@/components/fms-tree/FmsTreePanel.vue' const TREE = [ { b_id: 'c1', b_name: '分类一', children: [{ b_id: 'd1', b_name: '数据一' }], }, { b_id: 'd2', b_name: '数据二' }, ] const renderPanel = (extraProps = {}) => () => h(FmsTreePanel, { treeData: TREE, showRoot: true, ...extraProps }) /** 按标题取节点行(children 渲染在行外的兄弟容器里,行自身的 text 只含本节点标题) */ const rowOf = (wrapper, label) => wrapper.findAll('[role="treeitem"]').find((row) => row.text().includes(label)) describe('FmsTreePanel 选中态(selected-keys 透传)', () => { it('kebab 写的 selected-keys 能选中业务节点,虚拟根让位', () => { const wrapper = mount({ render: renderPanel({ 'selected-keys': ['d2'] }) }) const root = wrapper.findAll('[role="treeitem"]')[0] expect(root.text()).toContain('全部分类') expect(root.attributes('aria-selected')).toBe('false') expect(rowOf(wrapper, '数据二').attributes('aria-selected')).toBe('true') }) it('分类节点同样按 selected-keys 高亮', () => { const wrapper = mount({ render: renderPanel({ 'selected-keys': ['c1'] }) }) expect(rowOf(wrapper, '分类一').attributes('aria-selected')).toBe('true') expect(wrapper.findAll('[role="treeitem"]')[0].attributes('aria-selected')).toBe('false') }) it('selected-keys 为空时由虚拟根呈现选中态', () => { const wrapper = mount({ render: renderPanel({ 'selected-keys': [] }) }) expect(wrapper.findAll('[role="treeitem"]')[0].attributes('aria-selected')).toBe('true') }) it('点击节点抛出 select,交由调用方回写 selected-keys', async () => { const selected = [] const onSelect = (keys) => selected.push(...keys) const wrapper = mount({ render: renderPanel({ 'selected-keys': [], onSelect }) }) await rowOf(wrapper, '数据二').find('.tree-node-content').trigger('click') expect(selected).toEqual(['d2']) }) })