// @vitest-environment jsdom // docPaneMount.test.js —— DocPane 空态挂载 + removeLibrary 204 处理(T1 骨架,对齐 A1 交付) // 对应用例:TC-PRIV-L3-08(DocPane 空态挂载)、TC-PRIV-L1-41(removeLibrary 204)。 // 修正(A1 交付对齐):L1-41 只 mock http.js 边界——api/docgen.js 的 removeLibrary 走 // A1 真实实现(del 返回 null → 包装 {ok:true},即 204 空体兜底路径)。 import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mount, flushPromises } from '@vue/test-utils'; import { setActivePinia, createPinia } from 'pinia'; vi.mock('../../src/api/http.js', () => ({ // 204 空体契约:http 层对空体返回 null(A1 removeLibrary 依赖此语义) del: vi.fn(async (path) => null), get: vi.fn(async (path) => ({ ok: true, items: [] })), post: vi.fn(async () => ({ ok: true })), patch: vi.fn(async () => ({ ok: true })), ApiError: class ApiError extends Error { constructor(message, opts = {}) { super(message); this.code = opts.code || 'INTERNAL'; } }, })); // DocPane 依赖 docgen store(activeArtifact 驱动空态);面板组件随空态挂载 import DocPane from '../../src/components/bench/DocPane.vue'; import { useDocgenStore } from '../../src/stores/docgen.js'; describe('DocPane 空态挂载(FR-PRIV-006 / UI_SPEC §9.2)', () => { beforeEach(() => { setActivePinia(createPinia()); }); it('TC-PRIV-L3-08 空态:PrivateLibraryPane 挂载于 .doc-empty 内(顶部位置)', async () => { const docgen = useDocgenStore(); docgen.activeArtifact = null; const wrapper = mount(DocPane, { global: { stubs: { PrivateLibraryPane: true }, }, }); await flushPromises(); const empty = wrapper.find('.doc-empty'); expect(empty.exists()).toBe(true); const pane = wrapper.findComponent({ name: 'PrivateLibraryPane' }); expect(pane.exists()).toBe(true); // 空态区第一个子元素为面板(UI_SPEC:挂空态区顶部,其余空态内容不动) const firstChild = empty.element.children[0]; expect(firstChild).toBeTruthy(); expect(firstChild.className || firstChild.tagName).toBeTruthy(); // 图标与空态文案仍在(其余空态内容不动) expect(wrapper.text()).toContain('📄'); }); it('TC-PRIV-L3-08 有活动文档时面板隐藏', async () => { const docgen = useDocgenStore(); docgen.activeArtifact = { artifact_id: 'art-1', title: 't' }; const wrapper = mount(DocPane, { global: { stubs: { PrivateLibraryPane: true } }, }); await flushPromises(); expect(wrapper.find('.doc-empty').exists()).toBe(false); expect(wrapper.findComponent({ name: 'PrivateLibraryPane' }).exists()).toBe(false); }); }); describe('removeLibrary 204 空体处理(TC-PRIV-L1-41)', () => { beforeEach(() => { setActivePinia(createPinia()); }); it('DELETE 返回 204 空体时 removeLibrary 仍返回 {ok:true}(不抛 JSON 解析错)', async () => { const { removeLibrary } = await import('../../src/api/docgen.js'); // http mock 的 del 返回 null(204 空体兜底语义),走 A1 真实 removeLibrary const result = await removeLibrary('lib-1'); expect(result.ok).toBe(true); const { del } = await import('../../src/api/http.js'); expect(del).toHaveBeenCalledWith('/api/docgen/library/lib-1'); }); });