import { it, expect, vi, afterEach } from 'vitest'; import { mount, flushPromises, enableAutoUnmount } from '@vue/test-utils'; import ViewUIPlus from 'view-ui-plus'; import TaskHandle from '@/views/flow/TaskHandle.vue'; const { api } = vi.hoisted(() => ({ api: { taskDetail: vi.fn(), approve: vi.fn() } })); vi.mock('@/api/module/flow', () => ({ flowApi: api, INSTANCE_STATUS: { 10: '审批中' } })); vi.mock('vue-router', () => ({ useRoute: () => ({ params: { taskId: '1' }, path: '/task/1', query: { embed: '1' } }), useRouter: () => ({ push: vi.fn() }) })); vi.mock('@/utils/taskEmbed', () => ({ isBareTaskRoute: () => true, postToParent: vi.fn() })); enableAutoUnmount(afterEach); const detail = { instanceId: 9, status: 10, myPendingTaskIds: [1], trace: [{ taskId: 1, status: 0 }], outlets: [] }; async function click(text: string) { const b = Array.from(document.querySelectorAll('button')).find(x => x.textContent?.trim() === text); expect(b).toBeTruthy(); b!.click(); await flushPromises(); } for (const lost of [true, false]) it(lost ? 'POST 丢响应不声称未生效' : 'POST 成功但刷新失败保留成功并关闭办理入口', async () => { document.body.innerHTML = ''; let reads = 0; api.taskDetail.mockImplementation(() => ({ send: async () => { if (++reads > 1) throw new Error('refresh failed'); return detail; } })); api.approve.mockImplementation(() => ({ send: async () => { if (lost) throw new TypeError('Failed to fetch'); return {}; } })); mount(TaskHandle, { global: { plugins: [ViewUIPlus], stubs: { TraceTimeline: true } }, attachTo: document.body }); await flushPromises(); await click('同意'); await click('确定'); const text = document.body.textContent || ''; expect(text).not.toContain('本次办理未生效'); expect(text).not.toContain('任务仍是你的待办'); if (lost) expect(text).toContain('结果待确认'); else { expect(text).toContain('已提交成功'); expect(document.querySelector('.actions')).toBeNull(); } });