import { mount } from '@vue/test-utils' import type { VueWrapper } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' import { beforeEach, describe, expect, it } from 'vitest' import { createMemoryTaskRepository } from '@/data/taskRepository' import { useTaskStore } from '@/stores/tasks' import TagManagerPanel from './TagManagerPanel.vue' function setup(prepare?: (store: ReturnType) => void) { const pinia = createPinia() setActivePinia(pinia) const store = useTaskStore() store.init({ repository: createMemoryTaskRepository(), crossTab: false }) prepare?.(store) const wrapper = mount(TagManagerPanel, { props: { open: true }, global: { plugins: [pinia], stubs: { teleport: true } }, }) return { store, wrapper } } function buttonByText(wrapper: VueWrapper, text: string) { return wrapper.findAll('button').find((node) => node.text() === text) } describe('TagManagerPanel 展示', () => { beforeEach(() => setActivePinia(createPinia())) it('没有标签时显示空状态', () => { const { wrapper } = setup() expect(wrapper.text()).toContain('还没有任何标签') }) it('列出标签与使用数量', () => { const { wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端'] }) store.createTask({ title: 'B', tags: ['前端'] }) }) expect(wrapper.text()).toContain('#前端') expect(wrapper.text()).toContain('2 个任务') }) }) describe('TagManagerPanel 重命名', () => { beforeEach(() => setActivePinia(createPinia())) it('重命名后所有任务同步', async () => { const { store, wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端'] }) store.createTask({ title: 'B', tags: ['前端', '设计'] }) }) const input = wrapper.find('input[aria-label="重命名标签 前端"]') await input.setValue('Frontend') await buttonByText(wrapper, '保存')?.trigger('click') expect([...store.allTags].sort()).toEqual(['Frontend', '设计'].sort()) expect(wrapper.text()).toContain('#Frontend') expect(wrapper.text()).not.toContain('#前端') }) it('清空输入后保存不改动标签', async () => { const { store, wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端'] }) }) await wrapper.find('input[aria-label="重命名标签 前端"]').setValue(' ') await buttonByText(wrapper, '保存')?.trigger('click') expect(store.allTags).toEqual(['前端']) }) }) describe('TagManagerPanel 合并与删除', () => { beforeEach(() => setActivePinia(createPinia())) it('合并两个标签后去重', async () => { const { store, wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端', '设计'] }) store.createTask({ title: 'B', tags: ['设计'] }) }) await wrapper.find('select[aria-label="把标签 前端 合并到"]').setValue('设计') await buttonByText(wrapper, '合并')?.trigger('click') expect(store.allTags).toEqual(['设计']) expect(store.activeTasks.every((task) => !task.tags.includes('前端'))).toBe(true) }) it('删除标签需要二次确认,且不删除任务', async () => { const { store, wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端'] }) }) await buttonByText(wrapper, '删除')?.trigger('click') expect(store.allTags).toEqual(['前端']) await buttonByText(wrapper, '确认删除')?.trigger('click') expect(store.allTags).toEqual([]) expect(store.activeTasks).toHaveLength(1) }) it('取消删除保留标签', async () => { const { store, wrapper } = setup((store) => { store.createTask({ title: 'A', tags: ['前端'] }) }) await buttonByText(wrapper, '删除')?.trigger('click') await buttonByText(wrapper, '取消')?.trigger('click') expect(store.allTags).toEqual(['前端']) expect(wrapper.text()).not.toContain('确认删除') }) })