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 TrashPanel from './TrashPanel.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(TrashPanel, { 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().includes(text)) } describe('TrashPanel 展示', () => { beforeEach(() => setActivePinia(createPinia())) it('回收站为空时显示空状态', () => { const { wrapper } = setup() expect(wrapper.text()).toContain('回收站是空的') }) it('列出标题、删除时间与原列,并统计数量', () => { const { wrapper } = setup((store) => { const task = store.createTask({ title: '被删除的任务', status: 'doing' }) store.removeTask(task?.id ?? '') }) expect(wrapper.text()).toContain('被删除的任务') expect(wrapper.text()).toContain('原列 进行中') expect(wrapper.text()).toContain('删除于') expect(wrapper.text()).toContain('创建于') expect(wrapper.text()).toContain('共 1 个任务') }) it('已归档的删除项带「归档」徽标', () => { const { wrapper } = setup((store) => { const task = store.createTask({ title: '归档任务', status: 'done' }) store.archiveTasks([task?.id ?? '']) store.removeTask(task?.id ?? '') }) expect(wrapper.text()).toContain('归档') }) }) describe('TrashPanel 操作', () => { beforeEach(() => setActivePinia(createPinia())) it('恢复后回到看板,回收站变空', async () => { const { store, wrapper } = setup((store) => { const task = store.createTask({ title: '可恢复' }) store.removeTask(task?.id ?? '') }) await buttonByText(wrapper, '恢复')?.trigger('click') expect(store.activeTasks).toHaveLength(1) expect(store.trashTasks).toHaveLength(0) expect(wrapper.text()).toContain('回收站是空的') }) it('永久删除后数据真正消失', async () => { const { store, wrapper } = setup((store) => { const task = store.createTask({ title: '永久删除' }) store.removeTask(task?.id ?? '') }) await buttonByText(wrapper, '永久删除')?.trigger('click') expect(store.tasks).toHaveLength(0) expect(wrapper.text()).toContain('回收站是空的') }) it('清空回收站需要二次确认', async () => { const { store, wrapper } = setup((store) => { const a = store.createTask({ title: 'A' }) const b = store.createTask({ title: 'B' }) store.removeTasks([a?.id ?? '', b?.id ?? '']) }) await buttonByText(wrapper, '清空回收站')?.trigger('click') expect(wrapper.text()).toContain('此操作不可撤销') expect(store.tasks).toHaveLength(2) await buttonByText(wrapper, '确认清空')?.trigger('click') expect(store.tasks).toHaveLength(0) expect(wrapper.text()).toContain('回收站是空的') }) it('取消清空不会删除数据', async () => { const { store, wrapper } = setup((store) => { const task = store.createTask({ title: 'A' }) store.removeTask(task?.id ?? '') }) await buttonByText(wrapper, '清空回收站')?.trigger('click') await buttonByText(wrapper, '取消')?.trigger('click') expect(store.trashTasks).toHaveLength(1) expect(wrapper.text()).not.toContain('此操作不可撤销') }) })