写作工作台重构剩余 3 项(多 agent 并行构建各自新文件,主线统一集成): - WFW-5 内容感知书名(#6):buildManuscriptExcerpt 取正文首尾摘录(硬上界控 token); GeneratorRunner 加 manuscriptText,有 brief 字段时出现「用当前正文」按钮把摘录填入 brief,让 book-title 等据正文反推。纯前端零后端;HITL——只填表单,生成仍需作者触发。 - WFW-6 上下文速查抽屉(#7):ContextDrawer 视口无关右侧 slide-over,设定库/大纲完整 只读速查 + 伏笔/规则/文风摘要跳链;底栏「速查」按钮触发。加法式、CONTEXT_DRAWER_ENABLED 一键回滚、旧侧栏/全宽页路由全保留。 - WFW-7 genre 采集 + 无大纲降级:项目无题材时首次写章前弹 GenrePicker 采集(临时并入 本次 directive 不落库);chapters 为空时编辑器显式提示「尚无大纲,将按设定自由生成」。 各项 TDD:manuscriptExcerpt/useGenreGate/contextDrawer/useContextDrawer 共 32 新单测。 门禁全绿:vitest 629 / tsc / lint / build / coverage 95.43%。
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
// @vitest-environment jsdom
|
||
import { act, renderHook } from "@testing-library/react";
|
||
import { describe, expect, it } from "vitest";
|
||
|
||
import { toGenreDirective, useGenreGate } from "./useGenreGate";
|
||
|
||
describe("toGenreDirective", () => {
|
||
it("有题材时形如「本章题材:<genre>」", () => {
|
||
expect(toGenreDirective("玄幻")).toBe("本章题材:玄幻");
|
||
});
|
||
|
||
it("空/纯空白/null → 空串", () => {
|
||
expect(toGenreDirective(null)).toBe("");
|
||
expect(toGenreDirective("")).toBe("");
|
||
expect(toGenreDirective(" ")).toBe("");
|
||
});
|
||
});
|
||
|
||
describe("useGenreGate", () => {
|
||
it("项目已有 genre:needsGenre=false,genreDirective 反映项目题材", () => {
|
||
const { result } = renderHook(() => useGenreGate("仙侠"));
|
||
expect(result.current.needsGenre).toBe(false);
|
||
expect(result.current.effectiveGenre).toBe("仙侠");
|
||
expect(result.current.genreDirective).toBe("本章题材:仙侠");
|
||
});
|
||
|
||
it("项目 genre 为纯空白也视为缺失", () => {
|
||
const { result } = renderHook(() => useGenreGate(" "));
|
||
expect(result.current.needsGenre).toBe(true);
|
||
expect(result.current.effectiveGenre).toBeNull();
|
||
expect(result.current.genreDirective).toBe("");
|
||
});
|
||
|
||
it("无 genre:needsGenre=true,初始无生效题材与题材指令", () => {
|
||
const { result } = renderHook(() => useGenreGate(null));
|
||
expect(result.current.needsGenre).toBe(true);
|
||
expect(result.current.chosenGenre).toBeNull();
|
||
expect(result.current.effectiveGenre).toBeNull();
|
||
expect(result.current.genreDirective).toBe("");
|
||
});
|
||
|
||
it("无 genre 时 setChosenGenre 后 effectiveGenre/genreDirective 更新", () => {
|
||
const { result } = renderHook(() => useGenreGate(null));
|
||
act(() => result.current.setChosenGenre("都市"));
|
||
expect(result.current.chosenGenre).toBe("都市");
|
||
expect(result.current.effectiveGenre).toBe("都市");
|
||
expect(result.current.genreDirective).toBe("本章题材:都市");
|
||
});
|
||
|
||
it("setChosenGenre 传空串 → 回落 null,不产生题材指令", () => {
|
||
const { result } = renderHook(() => useGenreGate(null));
|
||
act(() => result.current.setChosenGenre("科幻"));
|
||
act(() => result.current.setChosenGenre(" "));
|
||
expect(result.current.chosenGenre).toBeNull();
|
||
expect(result.current.genreDirective).toBe("");
|
||
});
|
||
|
||
it("项目 genre 优先于作者点选", () => {
|
||
const { result } = renderHook(() => useGenreGate("历史"));
|
||
act(() => result.current.setChosenGenre("悬疑"));
|
||
// needsGenre=false 时 UI 不会调 setChosenGenre,但即便调了项目题材仍优先。
|
||
expect(result.current.effectiveGenre).toBe("历史");
|
||
expect(result.current.genreDirective).toBe("本章题材:历史");
|
||
});
|
||
});
|