P1-6 useStyleLearn/useKimiOauth 修 stale-closure 依赖。 P1-7 CommandPalette/Drawer 加 focus trap(新 lib/a11y/focusTrap)。 P1-8 SSE 解析与 server.ts 去 as 强转改类型守卫/运行时校验。 P2 删冗余强转、开 noUncheckedIndexedAccess、Toast 清理+稳定 key、列表 key 稳定化、 rel=noopener、useMemo 大文本、ConflictCard role=group、useInjection 加锁、 client.test 真断言。 codegen 重生成 schema.d.ts + 消费 DimensionEntry/ReviewConflictView 强类型。
69 lines
1.8 KiB
TypeScript
69 lines
1.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
|
||
import { fetchDraft, fetchOutline } from "./server";
|
||
|
||
// 纯加载/降级逻辑(mock fetch,无网络/DOM):大纲解包/降级、草稿 404→null。
|
||
function mockFetch(status: number, body: unknown): void {
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async () =>
|
||
new Response(JSON.stringify(body), {
|
||
status,
|
||
headers: { "content-type": "application/json" },
|
||
}),
|
||
),
|
||
);
|
||
}
|
||
|
||
afterEach(() => {
|
||
vi.unstubAllGlobals();
|
||
});
|
||
|
||
describe("fetchOutline", () => {
|
||
it("returns the persisted chapters on 200", async () => {
|
||
mockFetch(200, { chapters: [{ no: 1, volume: 1, beats: ["开场"] }] });
|
||
|
||
const chapters = await fetchOutline("p1");
|
||
|
||
expect(chapters).toHaveLength(1);
|
||
expect(chapters[0]?.no).toBe(1);
|
||
});
|
||
|
||
it("returns empty array when the project has no outline", async () => {
|
||
mockFetch(200, { chapters: [] });
|
||
|
||
expect(await fetchOutline("p1")).toEqual([]);
|
||
});
|
||
|
||
it("falls back to empty array on any error (does not throw)", async () => {
|
||
mockFetch(404, { error: { code: "NOT_FOUND", message: "no project" } });
|
||
|
||
expect(await fetchOutline("missing")).toEqual([]);
|
||
});
|
||
});
|
||
|
||
describe("fetchDraft", () => {
|
||
it("returns the saved draft view on 200", async () => {
|
||
mockFetch(200, {
|
||
project_id: "p1",
|
||
chapter_no: 1,
|
||
volume: 1,
|
||
status: "draft",
|
||
version: 2,
|
||
content: "第一章正文",
|
||
length: 5,
|
||
});
|
||
|
||
const draft = await fetchDraft("p1", 1);
|
||
|
||
expect(draft?.content).toBe("第一章正文");
|
||
expect(draft?.version).toBe(2);
|
||
});
|
||
|
||
it("returns null when no draft exists (404 → empty editor)", async () => {
|
||
mockFetch(404, { error: { code: "NOT_FOUND", message: "no draft" } });
|
||
|
||
expect(await fetchDraft("p1", 9)).toBeNull();
|
||
});
|
||
});
|