- 上下文清单、任务列表与详情、恢复控制、运行观察、创作对话与消息流、正式变更决策面板、正文写作与候选审阅、资料研究等页面同步本轮后端能力。 - 事件标签表补齐后端事件类型;查询键与生成客户端随 HTTP 契约重新生成。 - 依赖调整后 lockfile 同步;交互用例与浏览器旅程 spec 一并更新。 - 证据:make 前端检查(lint + typecheck + Pi 工具桥 tsc)exit 0;make 前端测试 37 文件 / 120 用例通过。
94 lines
4.0 KiB
TypeScript
94 lines
4.0 KiB
TypeScript
/** 创作对话组件传输替身:消息流折叠、发起回执与检查报告渲染;真实旅程由 Playwright 承担。 */
|
||
import { afterEach, expect, it, vi } from "vitest";
|
||
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
||
import { MemoryRouter } from "react-router-dom";
|
||
import { Ui依赖装配 } from "../../src/应用/依赖装配";
|
||
import { Ui消息流 } from "../../src/功能/创作对话/消息流";
|
||
import { Ui发起生成 } from "../../src/功能/创作对话/会话面板";
|
||
import type { 任务事件 } from "../../src/功能/任务运行/数据";
|
||
|
||
afterEach(() => {
|
||
cleanup();
|
||
vi.unstubAllGlobals();
|
||
});
|
||
|
||
function 事件(类型: string, 载荷: Record<string, unknown>, 序 = 1): 任务事件 {
|
||
return {
|
||
event_id: `e-${序}`,
|
||
sequence: 序,
|
||
event_type: 类型,
|
||
occurred_at: "2026-09-11T10:00:00Z",
|
||
payload: 载荷,
|
||
} as 任务事件;
|
||
}
|
||
|
||
// @muse-case {"case_id":"NC-ui-dialog-stream","environment":"离线组件传输替身","given":"创作对话组件传输替身","when":"渲染与提交","then":["按合同呈现"],"contract":"docs/系统架构/新版设计/接口契约/任务工具与事件.md"}
|
||
it("NC-ui-dialog-stream:正文段落按序累积、动作事件标注", () => {
|
||
render(
|
||
<Ui依赖装配>
|
||
<Ui消息流
|
||
事件={[
|
||
事件("step.started", {}, 1),
|
||
事件("text.delta", { 文本: "雨点敲着铁皮屋顶。" }, 2),
|
||
事件("text.delta", { 文本: "林深把信压在掌心。" }, 3),
|
||
事件("candidate.saved", { candidate_id: "c-1" }, 4),
|
||
]}
|
||
/>
|
||
</Ui依赖装配>,
|
||
);
|
||
expect(screen.getByText("雨点敲着铁皮屋顶。")).toBeTruthy();
|
||
expect(screen.getByText("林深把信压在掌心。")).toBeTruthy();
|
||
expect(screen.getByText("开始处理")).toBeTruthy();
|
||
expect(screen.getByText("候选已保存")).toBeTruthy();
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-ui-dialog-empty","environment":"离线组件传输替身","given":"创作对话组件传输替身","when":"渲染与提交","then":["按合同呈现"],"contract":"docs/系统架构/新版设计/接口契约/任务工具与事件.md"}
|
||
it("NC-ui-dialog-empty:无事件时给出找回提示不空白", () => {
|
||
render(
|
||
<Ui依赖装配>
|
||
<Ui消息流 事件={[]} />
|
||
</Ui依赖装配>,
|
||
);
|
||
expect(screen.getByText(/页面重开后仍可找回/)).toBeTruthy();
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-ui-dialog-start","environment":"离线组件传输替身","given":"创作对话组件传输替身","when":"渲染与提交","then":["按合同呈现"],"contract":"docs/系统架构/新版设计/接口契约/任务工具与事件.md"}
|
||
it("NC-ui-dialog-start:发起成功展示任务回执", async () => {
|
||
const 请求: string[] = [];
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
请求.push(request.url);
|
||
if (new URL(request.url).pathname === "/api/v1/works/w-9/writing-tasks") {
|
||
const 体 = await request.clone().json();
|
||
expect(体.granularity).toBe("new_chapter");
|
||
return new Response(
|
||
JSON.stringify({ task_id: "task-9", writing_task_id: "wt-9" }),
|
||
{ status: 201, headers: { "Content-Type": "application/json" } },
|
||
);
|
||
}
|
||
return new Response(JSON.stringify({ author_id: "author" }), {
|
||
headers: { "Content-Type": "application/json" },
|
||
});
|
||
}),
|
||
);
|
||
render(
|
||
<Ui依赖装配>
|
||
<MemoryRouter>
|
||
<Ui发起生成 scope="w-9" 默认作品="w-9" />
|
||
</MemoryRouter>
|
||
</Ui依赖装配>,
|
||
);
|
||
const 输入框 = Array.from(
|
||
document.querySelectorAll("input"),
|
||
) as HTMLInputElement[];
|
||
expect(输入框[0].value).toBe("w-9");
|
||
fireEvent.input(输入框[1], { target: { value: "ch-9" } });
|
||
fireEvent.input(输入框[2], { target: { value: "cfg-1" } });
|
||
expect(
|
||
screen.getByRole("link", { name: "打开正文圈选" }).getAttribute("href"),
|
||
).toBe("/works/w-9/write/ch-9");
|
||
fireEvent.click(screen.getByRole("button", { name: "发起生成" }));
|
||
expect(await screen.findByText(/task-9/)).toBeTruthy();
|
||
});
|