Files

94 lines
4.2 KiB
JavaScript
Raw Permalink Normal View History

// @vitest-environment jsdom
// privateLibraryPane.test.js —— 我的资料面板(T1 骨架,对齐 A1 交付)
// 对应用例:TC-PRIV-L3-04 ~ TC-PRIV-L3-07(docs/team/TEST_CASES_PRIVATE.md)
// UI_SPEC §9.2:标题「📁 我的资料」+ 计数;列表项 name/size/time;删除 confirm → removeLibrary → 刷新;
// 空态「尚无上传资料。在对话框拖拽 PDF / DOCX / TXT 上传。」;加载失败显示重试按钮。
// 修正(A1 交付对齐):
// - 可变状态用 vi.hoisted(vi.mock 工厂被提升,引用顶层 let 会 TDZ);
// - A1 onDelete 内部 await setTimeout(0)(宏任务)后才刷新——用例必须等待宏任务
// (vitest 默认未启用 fake timers,setTimeout 真实执行,flushPromises 不排干它;
// 用小轮询或 vi.useFakeTimers+advanceTimersByTimeAsync;本文件用真实定时器轮询辅助函数)。
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { setActivePinia, createPinia } from 'pinia';
const h = vi.hoisted(() => ({
listImpl: () => ({ ok: true, items: [] }),
removeImpl: () => ({ ok: true }),
}));
vi.mock('../../src/api/docgen.js', () => ({
listLibrary: vi.fn(async () => h.listImpl()),
removeLibrary: vi.fn(async (id) => h.removeImpl(id)),
}));
import PrivateLibraryPane from '../../src/components/bench/PrivateLibraryPane.vue';
const ROW_A = { id: 'a', name: '水稻资料.pdf', category: '', tags: '', description: '', as_template: false, active: true, created_at: '2026-09-01T00:00:00Z' };
const ROW_B = { id: 'b', name: '小麦试验.docx', category: '', tags: '', description: '', as_template: false, active: true, created_at: '2026-09-02T00:00:00Z' };
/** 等待宏任务(A1 onDelete 的 setTimeout(0) 刷新点) */
async function waitMacroTask() {
await new Promise((resolve) => setTimeout(resolve, 0));
await flushPromises();
}
describe('PrivateLibraryPane(FR-PRIV-006)', () => {
beforeEach(() => {
setActivePinia(createPinia());
h.listImpl = () => ({ ok: true, items: [] });
h.removeImpl = () => ({ ok: true });
});
it('TC-PRIV-L3-04 列表加载:标题 + 计数 + 条目(名称)', async () => {
h.listImpl = () => ({ ok: true, items: [ROW_A, ROW_B] });
const wrapper = mount(PrivateLibraryPane);
await flushPromises();
expect(wrapper.text()).toContain('我的资料');
expect(wrapper.text()).toContain('2');
expect(wrapper.text()).toContain('水稻资料.pdf');
expect(wrapper.text()).toContain('小麦试验.docx');
});
it('TC-PRIV-L3-05 空态文案', async () => {
const wrapper = mount(PrivateLibraryPane);
await flushPromises();
expect(wrapper.text()).toContain('尚无上传资料');
expect(wrapper.text()).toContain('PDF / DOCX / TXT');
});
it('TC-PRIV-L3-06 删除:confirm 二次确认 → removeLibrary → 列表刷新', async () => {
vi.spyOn(window, 'confirm').mockReturnValue(true);
h.listImpl = () => ({ ok: true, items: [ROW_A, ROW_B] });
h.removeImpl = vi.fn(async () => ({ ok: true }));
const wrapper = mount(PrivateLibraryPane);
await flushPromises();
const deleteBtn = wrapper.find('button[data-action="delete"]');
expect(deleteBtn.exists()).toBe(true);
await deleteBtn.trigger('click');
expect(window.confirm).toHaveBeenCalled();
// 删除后刷新:下一次 listLibrary 返回单条(A1 在宏任务后刷新)
h.listImpl = () => ({ ok: true, items: [ROW_B] });
await waitMacroTask();
expect(wrapper.text()).not.toContain('水稻资料.pdf');
expect(wrapper.text()).toContain('小麦试验.docx');
});
it('TC-PRIV-L3-07 加载失败显示重试按钮,重试后恢复', async () => {
let failFirst = true;
h.listImpl = () => {
if (failFirst) throw new Error('network');
return { ok: true, items: [ROW_A] };
};
const wrapper = mount(PrivateLibraryPane);
await flushPromises();
const retryBtn = wrapper.find('button');
expect(retryBtn.exists()).toBe(true);
failFirst = false;
await retryBtn.trigger('click');
await flushPromises();
expect(wrapper.text()).toContain('水稻资料.pdf');
});
});