90 lines
3.9 KiB
TypeScript
90 lines
3.9 KiB
TypeScript
/** 冲突后的重看必须等待最新基线,不能靠渲染时序取到旧版本。 */
|
||
import { afterEach, expect, it, vi } from "vitest";
|
||
import {
|
||
cleanup,
|
||
fireEvent,
|
||
render,
|
||
screen,
|
||
waitFor,
|
||
} from "@testing-library/react";
|
||
import { MemoryRouter } from "react-router-dom";
|
||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||
|
||
afterEach(() => {
|
||
cleanup();
|
||
vi.unstubAllGlobals();
|
||
sessionStorage.clear();
|
||
});
|
||
|
||
const 空模式 = { type: "object", properties: {}, required: [] };
|
||
|
||
it("NC-plan-rebase-await-fresh:冲突后立即重看并保存,携带最新正式版本", async () => {
|
||
const 保存请求: unknown[] = [];
|
||
let 表单次数 = 0;
|
||
const 延迟 = (毫秒: number) => new Promise((r) => setTimeout(r, 毫秒));
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
const 地址 = new URL(request.url).pathname;
|
||
const 应答 = (体: unknown, 状态 = 200) =>
|
||
new Response(JSON.stringify(体), {
|
||
status: 状态,
|
||
headers: { "Content-Type": "application/json" },
|
||
});
|
||
if (地址.startsWith("/api/v1/session")) return 应答({});
|
||
if (地址.includes("/plan-nodes"))
|
||
return 应答({ work_id: "work", revision: 1, nodes: [] });
|
||
if (地址.includes("/plans/form")) {
|
||
表单次数 += 1;
|
||
if (表单次数 === 1) return 应答({ work_id: "work", chapter_id: null, node_id: null, plan_id: "plan", revision: 1, schema_binding: {}, content: {}, visible_schema: 空模式, editable_schema: 空模式 });
|
||
// 冲突后的刷新故意延迟,放大“重看取到旧基线”的竞态窗口。
|
||
await 延迟(250);
|
||
return 应答({ work_id: "work", chapter_id: null, node_id: null, plan_id: "plan", revision: 2, schema_binding: {}, content: {}, visible_schema: 空模式, editable_schema: 空模式 });
|
||
}
|
||
if (地址.includes("/plans/candidates") && request.method === "GET")
|
||
return 应答([]);
|
||
if (地址.includes("/plans/candidates")) {
|
||
保存请求.push(await request.clone().json());
|
||
if (保存请求.length === 1)
|
||
return 应答(
|
||
{ code: "REVISION_CONFLICT", message: "规划正式版本已变化" },
|
||
409,
|
||
);
|
||
return 应答({ receipt_id: "r2", action: "manual_save", results: [] });
|
||
}
|
||
if (地址.includes("/explorations/selection"))
|
||
return new Response("404", { status: 404 });
|
||
return 应答({});
|
||
}),
|
||
);
|
||
const { Ui规划工作区 } = await import("../../src/功能/作品规划/规划工作区");
|
||
const 客户端 = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||
render(
|
||
<QueryClientProvider client={客户端}>
|
||
<MemoryRouter initialEntries={["/works/work/plans"]}>
|
||
<Ui规划工作区 />
|
||
</MemoryRouter>
|
||
</QueryClientProvider>,
|
||
);
|
||
fireEvent.click(await screen.findByRole("button", { name: "另拟大纲" }));
|
||
fireEvent.click(screen.getByRole("button", { name: "保存规划候选" }));
|
||
expect(
|
||
await screen.findByText("保存基线已变化。你的输入仍在下方,请先比较当前内容。"),
|
||
).toBeTruthy();
|
||
// 冲突提示出现时最新表单仍在途;重看按钮等待新数据后才可点击。
|
||
const 重看 = screen.getByRole("button", { name: "保留我的改动,基于当前版本重看" });
|
||
await waitFor(() => expect((重看 as HTMLButtonElement).disabled).toBe(false));
|
||
fireEvent.click(重看);
|
||
await waitFor(() =>
|
||
expect(
|
||
screen.queryByText("保存基线已变化。你的输入仍在下方,请先比较当前内容。"),
|
||
).toBeNull(),
|
||
);
|
||
fireEvent.click(screen.getByRole("button", { name: "保存规划候选" }));
|
||
expect(
|
||
await screen.findByText("规划候选已保存,请打开审阅。"),
|
||
).toBeTruthy();
|
||
const 第二次 = 保存请求.at(-1) as { request: { expected_revision: number } };
|
||
expect(第二次.request.expected_revision).toBe(2);
|
||
});
|