- 上下文清单、任务列表与详情、恢复控制、运行观察、创作对话与消息流、正式变更决策面板、正文写作与候选审阅、资料研究等页面同步本轮后端能力。 - 事件标签表补齐后端事件类型;查询键与生成客户端随 HTTP 契约重新生成。 - 依赖调整后 lockfile 同步;交互用例与浏览器旅程 spec 一并更新。 - 证据:make 前端检查(lint + typecheck + Pi 工具桥 tsc)exit 0;make 前端测试 37 文件 / 120 用例通过。
60 lines
2.2 KiB
TypeScript
60 lines
2.2 KiB
TypeScript
import { useState } from "react";
|
||
import { afterEach, expect, it, vi } from "vitest";
|
||
import {
|
||
cleanup,
|
||
fireEvent,
|
||
render,
|
||
screen,
|
||
waitFor,
|
||
} from "@testing-library/react";
|
||
import { Ui正文编辑器 } from "../../src/界面/编辑器/正文编辑器";
|
||
import * as adapter from "../../src/界面/编辑器/文档适配";
|
||
afterEach(() => {
|
||
cleanup();
|
||
vi.restoreAllMocks();
|
||
});
|
||
// @muse-case {"case_id": "O01-editor-document-transaction", "environment": "离线组件测试", "given": "正文编辑器一次输入事务", "when": "组件重绘与光标移动", "then": ["转为正式文档只执行一次", "初始转换不受影响"], "contract": "docs/系统架构/新版设计/工作台设计.md"}
|
||
it("O01-editor-document-transaction:一次正文事务只转一次正式文档,组件重绘及光标移动不转正文", async () => {
|
||
const convert = vi.spyOn(adapter, "转为正文快照"),
|
||
initial = vi.spyOn(adapter, "转为编辑文档");
|
||
const changes = vi.fn();
|
||
function Editor() {
|
||
const [draft, setDraft] = useState<adapter.正文草稿>({
|
||
format_version: 1,
|
||
paragraphs: [{ paragraph_id: "p", content: [] }],
|
||
});
|
||
return (
|
||
<Ui正文编辑器
|
||
initial={draft}
|
||
onChange={(value, stats) => {
|
||
changes(value, stats);
|
||
setDraft(value);
|
||
}}
|
||
onComposition={() => {}}
|
||
/>
|
||
);
|
||
}
|
||
render(<Editor />);
|
||
const textbox = screen.getByRole("textbox", { name: "章节正文" });
|
||
const paragraph = textbox.querySelector("p")!;
|
||
paragraph.textContent = "新文😀";
|
||
fireEvent.input(textbox, { inputType: "insertText", data: "新文😀" });
|
||
await waitFor(() => expect(changes).toHaveBeenCalledTimes(1));
|
||
expect(convert).toHaveBeenCalledTimes(1);
|
||
expect(initial).toHaveBeenCalledTimes(1);
|
||
expect(changes.mock.calls[0][1]).toEqual({
|
||
visible_text: "新文😀",
|
||
codepoints: 3,
|
||
});
|
||
const selection = window.getSelection()!,
|
||
range = document.createRange();
|
||
range.setStart(paragraph.firstChild!, 1);
|
||
range.collapse(true);
|
||
selection.removeAllRanges();
|
||
selection.addRange(range);
|
||
fireEvent(document, new Event("selectionchange"));
|
||
fireEvent.focus(textbox);
|
||
expect(convert).toHaveBeenCalledTimes(1);
|
||
expect(changes).toHaveBeenCalledTimes(1);
|
||
});
|