2026-09-07 21:42:22 +08:00
|
|
|
|
// @vitest-environment jsdom
|
2026-09-07 22:41:36 +08:00
|
|
|
|
// privateLibraryPane.test.js —— 我的资料面板(T1 骨架,对齐 A1 交付)
|
2026-09-07 21:42:22 +08:00
|
|
|
|
// 对应用例: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 上传。」;加载失败显示重试按钮。
|
2026-09-07 22:41:36 +08:00
|
|
|
|
// 修正(A1 交付对齐):
|
|
|
|
|
|
// - 可变状态用 vi.hoisted(vi.mock 工厂被提升,引用顶层 let 会 TDZ);
|
|
|
|
|
|
// - A1 onDelete 内部 await setTimeout(0)(宏任务)后才刷新——用例必须等待宏任务
|
|
|
|
|
|
// (vitest 默认未启用 fake timers,setTimeout 真实执行,flushPromises 不排干它;
|
|
|
|
|
|
// 用小轮询或 vi.useFakeTimers+advanceTimersByTimeAsync;本文件用真实定时器轮询辅助函数)。
|
2026-09-07 21:42:22 +08:00
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
|
import { mount, flushPromises } from '@vue/test-utils';
|
|
|
|
|
|
import { setActivePinia, createPinia } from 'pinia';
|
|
|
|
|
|
|
2026-09-07 22:41:36 +08:00
|
|
|
|
const h = vi.hoisted(() => ({
|
|
|
|
|
|
listImpl: () => ({ ok: true, items: [] }),
|
|
|
|
|
|
removeImpl: () => ({ ok: true }),
|
|
|
|
|
|
}));
|
2026-09-07 21:42:22 +08:00
|
|
|
|
|
|
|
|
|
|
vi.mock('../../src/api/docgen.js', () => ({
|
2026-09-07 22:41:36 +08:00
|
|
|
|
listLibrary: vi.fn(async () => h.listImpl()),
|
|
|
|
|
|
removeLibrary: vi.fn(async (id) => h.removeImpl(id)),
|
2026-09-07 21:42:22 +08:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
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' };
|
|
|
|
|
|
|
2026-09-07 22:41:36 +08:00
|
|
|
|
/** 等待宏任务(A1 onDelete 的 setTimeout(0) 刷新点) */
|
|
|
|
|
|
async function waitMacroTask() {
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
|
|
|
|
await flushPromises();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-07 21:42:22 +08:00
|
|
|
|
describe('PrivateLibraryPane(FR-PRIV-006)', () => {
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
setActivePinia(createPinia());
|
2026-09-07 22:41:36 +08:00
|
|
|
|
h.listImpl = () => ({ ok: true, items: [] });
|
|
|
|
|
|
h.removeImpl = () => ({ ok: true });
|
2026-09-07 21:42:22 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('TC-PRIV-L3-04 列表加载:标题 + 计数 + 条目(名称)', async () => {
|
2026-09-07 22:41:36 +08:00
|
|
|
|
h.listImpl = () => ({ ok: true, items: [ROW_A, ROW_B] });
|
2026-09-07 21:42:22 +08:00
|
|
|
|
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);
|
2026-09-07 22:41:36 +08:00
|
|
|
|
h.listImpl = () => ({ ok: true, items: [ROW_A, ROW_B] });
|
|
|
|
|
|
h.removeImpl = vi.fn(async () => ({ ok: true }));
|
2026-09-07 21:42:22 +08:00
|
|
|
|
const wrapper = mount(PrivateLibraryPane);
|
|
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
2026-09-07 22:41:36 +08:00
|
|
|
|
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');
|
2026-09-07 21:42:22 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('TC-PRIV-L3-07 加载失败显示重试按钮,重试后恢复', async () => {
|
|
|
|
|
|
let failFirst = true;
|
2026-09-07 22:41:36 +08:00
|
|
|
|
h.listImpl = () => {
|
2026-09-07 21:42:22 +08:00
|
|
|
|
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');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|