import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import Button from '@/components/ui/button/button.vue' import Tag from '@/components/ui/tag/tag.vue' import Switch from '@/components/ui/switch/switch.vue' import Radio from '@/components/ui/radio/radio.vue' import RadioGroup from '@/components/ui/radio/radio-group.vue' import Checkbox from '@/components/ui/checkbox/checkbox.vue' import Spin from '@/components/ui/spin/spin.vue' describe('Button 组件', () => { it('默认渲染 primary 按钮并显示插槽内容', () => { const wrapper = mount(Button, { slots: { default: '提交' } }) expect(wrapper.find('button').classes()).toContain('button-primary') expect(wrapper.text()).toContain('提交') }) it('type/block/loading 映射 class', () => { const wrapper = mount(Button, { props: { type: 'danger', block: true, loading: true } }) const btn = wrapper.find('button') expect(btn.classes()).toContain('button-danger') expect(btn.classes()).toContain('button-block') expect(btn.classes()).toContain('button-loading') }) it('loading 或 disabled 时禁用原生按钮', () => { const loading = mount(Button, { props: { loading: true } }) expect(loading.find('button').attributes('disabled')).toBeDefined() const disabled = mount(Button, { props: { disabled: true } }) expect(disabled.find('button').attributes('disabled')).toBeDefined() }) it('htmlType 透传原生 type', () => { const wrapper = mount(Button, { props: { htmlType: 'submit' } }) expect(wrapper.find('button').attributes('type')).toBe('submit') }) }) describe('Tag 组件', () => { it('默认渲染 default 变体', () => { const wrapper = mount(Tag, { slots: { default: '标签' } }) expect(wrapper.text()).toContain('标签') }) it('variant 映射 class', () => { const wrapper = mount(Tag, { props: { variant: 'danger' } }) expect(wrapper.classes()).toContain('tag-danger') }) it('closable 点击关闭:emit close 并隐藏', async () => { const wrapper = mount(Tag, { props: { closable: true } }) await wrapper.find('[role="button"]').trigger('click') expect(wrapper.emitted('close')).toHaveLength(1) expect(wrapper.classes()).toContain('tag-hidden') }) it('disabled 时点击不关闭', async () => { const wrapper = mount(Tag, { props: { closable: true, disabled: true } }) await wrapper.find('[role="button"]').trigger('click') expect(wrapper.emitted('close')).toBeUndefined() }) it('href 渲染为链接', () => { const wrapper = mount(Tag, { props: { href: '/x' } }) expect(wrapper.find('a').exists()).toBe(true) }) }) describe('Switch 组件', () => { it('点击触发 update:modelValue 与 change', async () => { const wrapper = mount(Switch, { props: { modelValue: false } }) await wrapper.find('[role="switch"]').trigger('click') expect(wrapper.emitted('update:modelValue')[0]).toEqual([true]) expect(wrapper.emitted('change')[0][0]).toBe(true) }) it('checked 状态映射 class 与 aria-checked', () => { const wrapper = mount(Switch, { props: { modelValue: true } }) expect(wrapper.find('[role="switch"]').classes()).toContain('switch-checked') expect(wrapper.find('[role="switch"]').attributes('aria-checked')).toBe('true') }) it('disabled 时点击不触发', async () => { const wrapper = mount(Switch, { props: { disabled: true } }) await wrapper.find('[role="switch"]').trigger('click') expect(wrapper.emitted('update:modelValue')).toBeUndefined() }) }) describe('RadioGroup + Radio 组件', () => { it('选中 radio 触发 v-model 更新', async () => { const real = mount({ components: { RadioGroup, Radio }, template: '', data: () => ({ v: 'a' }), }) const radios = real.findAll('input[type="radio"]') expect(radios[0].element.checked).toBe(true) await radios[1].setValue() expect(real.vm.v).toBe('b') }) it('独立 Radio 未分组时默认不选中', () => { const wrapper = mount(Radio, { props: { value: 'x' } }) expect(wrapper.find('input').element.checked).toBe(false) }) }) describe('Checkbox 组件', () => { it('勾选触发 update:modelValue 与 change', async () => { const wrapper = mount(Checkbox, { props: { modelValue: false } }) await wrapper.find('input[type="checkbox"]').setValue(true) expect(wrapper.emitted('update:modelValue')[0]).toEqual([true]) expect(wrapper.emitted('change')[0][0]).toBe(true) }) it('checked/indeterminate/disabled 映射 class', () => { const checked = mount(Checkbox, { props: { modelValue: true } }) expect(checked.classes()).toContain('checkbox-checked') const semi = mount(Checkbox, { props: { indeterminate: true, modelValue: false } }) expect(semi.classes()).toContain('checkbox-indeterminate') const disabled = mount(Checkbox, { props: { disabled: true } }) expect(disabled.classes()).toContain('checkbox-disabled') }) }) describe('Spin 组件', () => { it('默认加载中显示 aria-busy', () => { const wrapper = mount(Spin) expect(wrapper.attributes('aria-busy')).toBe('true') }) it('spinning=false 不显示', () => { const wrapper = mount(Spin, { props: { spinning: false } }) expect(wrapper.attributes('aria-busy')).toBe('false') }) it('delay 延迟生效:立即不 active,延时后 active', async () => { const wrapper = mount(Spin, { props: { spinning: true, delay: 50 } }) expect(wrapper.attributes('aria-busy')).toBe('false') await new Promise((r) => setTimeout(r, 80)) expect(wrapper.attributes('aria-busy')).toBe('true') }) it('description 渲染加载文案', () => { const wrapper = mount(Spin, { props: { description: '加载中...' } }) expect(wrapper.text()).toContain('加载中...') }) })