- 上下文清单、任务列表与详情、恢复控制、运行观察、创作对话与消息流、正式变更决策面板、正文写作与候选审阅、资料研究等页面同步本轮后端能力。 - 事件标签表补齐后端事件类型;查询键与生成客户端随 HTTP 契约重新生成。 - 依赖调整后 lockfile 同步;交互用例与浏览器旅程 spec 一并更新。 - 证据:make 前端检查(lint + typecheck + Pi 工具桥 tsc)exit 0;make 前端测试 37 文件 / 120 用例通过。
619 lines
22 KiB
TypeScript
619 lines
22 KiB
TypeScript
import { afterEach, expect, it, vi } from "vitest";
|
||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||
import {
|
||
cleanup,
|
||
act,
|
||
fireEvent,
|
||
render,
|
||
screen,
|
||
waitFor,
|
||
within,
|
||
} from "@testing-library/react";
|
||
import { Ui依赖装配 } from "../../src/应用/依赖装配";
|
||
import { Ui世界资料页 } from "../../src/功能/故事世界/世界资料页";
|
||
import { Ui事实审阅 } from "../../src/功能/故事世界/事实审阅";
|
||
import { Ui实体编辑器 } from "../../src/功能/故事世界/实体编辑器";
|
||
|
||
afterEach(() => {
|
||
cleanup();
|
||
vi.unstubAllGlobals();
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-world-edit-refresh","environment":"离线","given":"具名作者、固定来源、隔离测试配置与真实模块存储","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-world-edit-refresh:后台版本更新保留草稿并阻止沿旧基线保存"],"contract":"docs/系统架构/新版设计/模块设计/B02-故事世界.md#作者实体表单与字段提案"}
|
||
it("NC-world-edit-refresh:后台版本更新保留草稿并阻止沿旧基线保存", async () => {
|
||
let revision = 1;
|
||
const calls: Request[] = [];
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
calls.push(request);
|
||
return json(
|
||
表单结果(
|
||
"work-a",
|
||
"object-a",
|
||
{
|
||
dock_name: revision === 1 ? "旧码头" : "服务器新码头",
|
||
capacity: 10,
|
||
},
|
||
revision,
|
||
),
|
||
);
|
||
}),
|
||
);
|
||
const cache = new QueryClient({
|
||
defaultOptions: { queries: { retry: false } },
|
||
});
|
||
render(
|
||
<QueryClientProvider client={cache}>
|
||
<Ui实体编辑器
|
||
scope="session"
|
||
workId="work-a"
|
||
target={{
|
||
mode: "edit",
|
||
type: 类型清单[0],
|
||
entity: {
|
||
object_id: "object-a",
|
||
type_id: "boarding_point",
|
||
revision: 1,
|
||
knowledge_mode: "setting",
|
||
independent: true,
|
||
依据可用: true,
|
||
生效位置: null,
|
||
计划窗口: null,
|
||
content: { dock_name: "旧码头", capacity: 10 },
|
||
},
|
||
}}
|
||
onProposal={() => undefined}
|
||
onCancel={() => undefined}
|
||
/>
|
||
</QueryClientProvider>,
|
||
);
|
||
const input = await screen.findByLabelText(/码头名称/);
|
||
fireEvent.change(input, { target: { value: "作者仍在编辑的名字" } });
|
||
revision = 2;
|
||
await act(async () => {
|
||
await cache.invalidateQueries();
|
||
});
|
||
await screen.findByText(/已保留你的输入;取消后重新打开/);
|
||
expect((input as HTMLInputElement).value).toBe("作者仍在编辑的名字");
|
||
expect(
|
||
screen
|
||
.getByRole("button", { name: "保存为待决资料" })
|
||
.hasAttribute("disabled"),
|
||
).toBe(true);
|
||
expect(calls.every((c) => c.method === "GET")).toBe(true);
|
||
cache.clear();
|
||
});
|
||
|
||
const 登船点结构 = {
|
||
type: "object",
|
||
properties: {
|
||
dock_name: { type: "string", title: "码头名称" },
|
||
capacity: { type: "integer", title: "可容纳人数", minimum: 1 },
|
||
},
|
||
required: ["dock_name", "capacity"],
|
||
};
|
||
|
||
const 类型清单 = [
|
||
{
|
||
type_id: "boarding_point",
|
||
名称: "登船点",
|
||
语义域: "world",
|
||
实例族: ["entity"],
|
||
说明: "故事中可登船的地点",
|
||
schemas: [
|
||
{ schema_id: "boarding_point_v2", base_version: 4 },
|
||
{ schema_id: "boarding_point_v1", base_version: 3 },
|
||
],
|
||
},
|
||
{
|
||
type_id: "weather_station",
|
||
名称: "观天台",
|
||
语义域: "world",
|
||
实例族: ["entity"],
|
||
说明: "观测天象的地点",
|
||
schemas: [{ schema_id: "weather_station_v1", base_version: 1 }],
|
||
},
|
||
];
|
||
|
||
function json(value: unknown, status = 200) {
|
||
return new Response(JSON.stringify(value), {
|
||
status,
|
||
headers: { "Content-Type": "application/json" },
|
||
});
|
||
}
|
||
|
||
function 表单结果(
|
||
workId: string,
|
||
objectId: string,
|
||
content: Record<string, unknown> = {},
|
||
revision = 0,
|
||
) {
|
||
return {
|
||
work_id: workId,
|
||
object_id: objectId,
|
||
type_id: "boarding_point",
|
||
revision,
|
||
schema_binding: {
|
||
schema_id: "boarding_point_v1",
|
||
base_version: 3,
|
||
extension_version: 2,
|
||
effective_schema_hash: "schema-current",
|
||
projection_version: "projection-current",
|
||
},
|
||
content,
|
||
visible_schema: 登船点结构,
|
||
editable_schema: 登船点结构,
|
||
visible_projection_version: "projection-current",
|
||
source_current: true,
|
||
context: {
|
||
sources: [],
|
||
independent: true,
|
||
knowledge_mode: "setting",
|
||
effective_from: null,
|
||
known_to: [],
|
||
reader_visible: true,
|
||
planned_window: null,
|
||
},
|
||
};
|
||
}
|
||
|
||
// @muse-case {"case_id":"NC-world-entity-author-flow","environment":"离线","given":"具名作者、固定来源、隔离测试配置与真实模块存储","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-world-entity-author-flow:新类型动态字段保存为提案,展示内容后才确认"],"contract":"docs/系统架构/新版设计/模块设计/B02-故事世界.md#作者实体表单与字段提案"}
|
||
it("NC-world-entity-author-flow:新类型动态字段保存为提案,展示内容后才确认", async () => {
|
||
const saveBodies: Record<string, unknown>[] = [];
|
||
const decisionBodies: Record<string, unknown>[] = [];
|
||
let saveAttempts = 0;
|
||
let adopted = false;
|
||
let proposal: Record<string, unknown> | undefined;
|
||
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
const url = new URL(request.url);
|
||
const path = url.pathname;
|
||
if (path === "/api/v1/session")
|
||
return json({ author_id: "author-from-session" });
|
||
if (path === "/api/v1/world/types") return json(类型清单);
|
||
if (
|
||
path === "/api/v1/works/work-a/world/entities" &&
|
||
request.method === "GET"
|
||
)
|
||
return json(
|
||
adopted
|
||
? [
|
||
{
|
||
object_id: String(proposal!.object_id),
|
||
type_id: "boarding_point",
|
||
revision: 1,
|
||
knowledge_mode: "setting",
|
||
independent: true,
|
||
依据可用: true,
|
||
生效位置: null,
|
||
计划窗口: null,
|
||
content: proposal!.content,
|
||
},
|
||
]
|
||
: [],
|
||
);
|
||
if (path.endsWith("/form")) {
|
||
expect(url.searchParams.get("schema_id")).toBe("boarding_point_v1");
|
||
expect(url.searchParams.get("base_version")).toBe("3");
|
||
return json(表单结果("work-a", path.split("/").at(-2)!));
|
||
}
|
||
if (
|
||
path === "/api/v1/works/work-a/fact-proposals" &&
|
||
request.method === "POST"
|
||
) {
|
||
const body = (await request.json()) as Record<string, unknown>;
|
||
saveBodies.push(body);
|
||
saveAttempts += 1;
|
||
if (saveAttempts === 1) throw new Error("lost response");
|
||
const proposed = body.proposal as Record<string, unknown>;
|
||
const changes = proposed.changes as { 路径: string[]; 值: unknown }[];
|
||
const content = Object.fromEntries(
|
||
changes.map((change) => [change.路径[0], change.值]),
|
||
);
|
||
proposal = {
|
||
proposal_id: "proposal-new-type",
|
||
revision: 7,
|
||
proposal_hash: "proposal-hash-from-service",
|
||
work_id: "work-a",
|
||
object_id: proposed.object_id,
|
||
expected_revision: 0,
|
||
schema: proposed.schema,
|
||
content,
|
||
independent: true,
|
||
sources: [],
|
||
effective_from: null,
|
||
knowledge_mode: "setting",
|
||
known_to: [],
|
||
planned_window: null,
|
||
decision: "undecided",
|
||
};
|
||
return json(
|
||
{
|
||
receipt_id: "proposal-receipt",
|
||
command_id: body.command_id,
|
||
target_ref: "proposal-new-type",
|
||
action: "save",
|
||
author_review_id: null,
|
||
basis: {},
|
||
results: [
|
||
{
|
||
proposal_id: "proposal-new-type",
|
||
proposal_hash: "proposal-hash-from-service",
|
||
},
|
||
],
|
||
impact_ids: [],
|
||
},
|
||
201,
|
||
);
|
||
}
|
||
if (
|
||
path === "/api/v1/fact-proposals/proposal-new-type" &&
|
||
request.method === "GET"
|
||
)
|
||
return json(proposal);
|
||
if (path === "/api/v1/fact-proposals/proposal-new-type/reviews")
|
||
return json({
|
||
review_id: "review-from-service",
|
||
review_hash: "review-hash-from-service",
|
||
snapshot: {},
|
||
proposal,
|
||
});
|
||
if (path === "/api/v1/fact-proposals/proposal-new-type/decisions") {
|
||
const body = (await request.json()) as Record<string, unknown>;
|
||
decisionBodies.push(body);
|
||
adopted = body.action === "adopt";
|
||
return json({
|
||
receipt_id: "decision-receipt",
|
||
command_id: body.command_id,
|
||
target_ref: `world:${proposal!.object_id}`,
|
||
action: body.action,
|
||
author_review_id: body.author_review_id,
|
||
basis: {},
|
||
results: [],
|
||
impact_ids: [],
|
||
});
|
||
}
|
||
throw new Error(`unexpected request: ${request.method} ${path}`);
|
||
}),
|
||
);
|
||
|
||
render(
|
||
<Ui依赖装配>
|
||
<Ui世界资料页 workId="work-a" />
|
||
</Ui依赖装配>,
|
||
);
|
||
|
||
await screen.findByRole("option", { name: "登船点" });
|
||
fireEvent.change(screen.getByLabelText("资料类型"), {
|
||
target: { value: "boarding_point" },
|
||
});
|
||
fireEvent.change(screen.getByLabelText("资料格式"), {
|
||
target: { value: "1" },
|
||
});
|
||
const add = screen.getByRole("button", { name: "添加资料" });
|
||
await waitFor(() => expect((add as HTMLButtonElement).disabled).toBe(false));
|
||
fireEvent.click(add);
|
||
const editor = await screen.findByRole("region", { name: "添加资料" });
|
||
expect(
|
||
await within(editor).findByText("这是作者直接设定的资料,不依赖正文依据。"),
|
||
).toBeTruthy();
|
||
fireEvent.change(await within(editor).findByRole("textbox"), {
|
||
target: { value: "北渡口" },
|
||
});
|
||
fireEvent.change(await within(editor).findByRole("spinbutton"), {
|
||
target: { value: "80" },
|
||
});
|
||
fireEvent.click(screen.getByRole("button", { name: "保存为待决资料" }));
|
||
await screen.findByText("暂时无法连接工作台,请稍后重试。");
|
||
fireEvent.click(screen.getByRole("button", { name: "保存为待决资料" }));
|
||
|
||
await screen.findByRole("heading", { name: "提案内容" });
|
||
expect(screen.getAllByText("北渡口").length).toBeGreaterThan(0);
|
||
expect(screen.getAllByText("80").length).toBeGreaterThan(0);
|
||
expect(screen.getByText("本书还没有已确认的世界资料。")).toBeTruthy();
|
||
expect(saveBodies).toHaveLength(2);
|
||
expect(saveBodies[1]).toEqual(saveBodies[0]);
|
||
expect(saveBodies[0]).toMatchObject({
|
||
proposal: {
|
||
work_id: "work-a",
|
||
expected_revision: 0,
|
||
schema: {
|
||
schema_id: "boarding_point_v1",
|
||
base_version: 3,
|
||
extension_version: 2,
|
||
effective_schema_hash: "schema-current",
|
||
projection_version: "projection-current",
|
||
},
|
||
content: {},
|
||
independent: true,
|
||
knowledge_mode: "setting",
|
||
reader_visible: true,
|
||
sources: [],
|
||
effective_from: null,
|
||
known_to: [],
|
||
planned_window: null,
|
||
instance_family: "entity",
|
||
},
|
||
});
|
||
expect(saveBodies[0]).not.toHaveProperty("author_id");
|
||
expect((saveBodies[0].proposal as Record<string, unknown>).changes).toEqual(
|
||
expect.arrayContaining([
|
||
{ 路径: ["dock_name"], 动作: "set", 值: "北渡口" },
|
||
{ 路径: ["capacity"], 动作: "set", 值: 80 },
|
||
]),
|
||
);
|
||
|
||
const openNew = screen.getByRole("button", {
|
||
name: "核对当前作品并打开决定",
|
||
});
|
||
await waitFor(() =>
|
||
expect((openNew as HTMLButtonElement).disabled).toBe(false),
|
||
);
|
||
fireEvent.click(openNew);
|
||
fireEvent.click(await screen.findByRole("button", { name: "确认这份资料" }));
|
||
await screen.findByText("这份资料已确认。");
|
||
expect(decisionBodies).toHaveLength(1);
|
||
expect(decisionBodies[0]).toMatchObject({
|
||
proposal_revision: 7,
|
||
proposal_hash: "proposal-hash-from-service",
|
||
expected_revision: 0,
|
||
author_review_id: "review-from-service",
|
||
review_hash: "review-hash-from-service",
|
||
approved_changes: ["object"],
|
||
});
|
||
await screen.findByText(/登船点 · 北渡口 · r1/);
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-world-review-scope","environment":"离线","given":"具名作者、固定来源、隔离测试配置与真实模块存储","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-world-review-scope:其他作品的提案在打开审阅前被拒绝"],"contract":"docs/系统架构/新版设计/模块设计/B02-故事世界.md#作者实体表单与字段提案"}
|
||
it("NC-world-review-scope:其他作品的提案在打开审阅前被拒绝", async () => {
|
||
const calls: string[] = [];
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
const path = new URL(request.url).pathname;
|
||
calls.push(`${request.method} ${path}`);
|
||
if (path === "/api/v1/session") return json({ author_id: "author" });
|
||
if (path === "/api/v1/works/work-a/world/entities") return json([]);
|
||
if (path === "/api/v1/fact-proposals/proposal-b")
|
||
return json({
|
||
proposal_id: "proposal-b",
|
||
revision: 1,
|
||
proposal_hash: "hash-b",
|
||
work_id: "work-b",
|
||
object_id: "object-b",
|
||
expected_revision: 0,
|
||
schema: {
|
||
schema_id: "boarding_point_v1",
|
||
base_version: 3,
|
||
effective_schema_hash: "schema-current",
|
||
projection_version: "projection-current",
|
||
},
|
||
content: { dock_name: "不属于本书" },
|
||
independent: true,
|
||
sources: [],
|
||
effective_from: null,
|
||
knowledge_mode: "setting",
|
||
known_to: [],
|
||
planned_window: null,
|
||
decision: "undecided",
|
||
});
|
||
throw new Error(`unexpected request: ${request.method} ${path}`);
|
||
}),
|
||
);
|
||
render(
|
||
<Ui依赖装配>
|
||
<Ui事实审阅 workId="work-a" />
|
||
</Ui依赖装配>,
|
||
);
|
||
fireEvent.change(await screen.findByLabelText("待决资料编号"), {
|
||
target: { value: "proposal-b" },
|
||
});
|
||
fireEvent.click(screen.getByRole("button", { name: "查看内容" }));
|
||
await screen.findByText("这份待决资料不属于当前作品,不能在这里决定。");
|
||
expect(calls.some((call) => call.includes("/reviews"))).toBe(false);
|
||
expect(screen.queryByText("不属于本书")).toBeNull();
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-world-review-version","environment":"离线","given":"具名作者、固定来源、隔离测试配置与真实模块存储","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-world-review-version:当前实体版本与提案基线失配时不打开旧决定"],"contract":"docs/系统架构/新版设计/模块设计/B02-故事世界.md#作者实体表单与字段提案"}
|
||
it("NC-world-review-version:当前实体版本与提案基线失配时不打开旧决定", async () => {
|
||
const requests: string[] = [];
|
||
const proposal = {
|
||
proposal_id: "proposal-old",
|
||
revision: 2,
|
||
proposal_hash: "proposal-old-hash",
|
||
work_id: "work-a",
|
||
object_id: "object-a",
|
||
expected_revision: 1,
|
||
schema: {
|
||
schema_id: "boarding_point_v1",
|
||
base_version: 3,
|
||
extension_version: 2,
|
||
effective_schema_hash: "schema-current",
|
||
projection_version: "projection-current",
|
||
},
|
||
content: { dock_name: "旧提案名", capacity: 80 },
|
||
independent: true,
|
||
sources: [],
|
||
effective_from: null,
|
||
knowledge_mode: "setting",
|
||
known_to: [],
|
||
planned_window: null,
|
||
decision: "undecided",
|
||
};
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
const url = new URL(request.url);
|
||
requests.push(`${request.method} ${url.pathname}`);
|
||
if (url.pathname === "/api/v1/session")
|
||
return json({ author_id: "author" });
|
||
if (url.pathname === "/api/v1/works/work-a/world/entities")
|
||
return json([
|
||
{
|
||
object_id: "object-a",
|
||
type_id: "boarding_point",
|
||
revision: 2,
|
||
knowledge_mode: "setting",
|
||
independent: true,
|
||
依据可用: true,
|
||
生效位置: null,
|
||
计划窗口: null,
|
||
content: { dock_name: "当前名", capacity: 40 },
|
||
},
|
||
]);
|
||
if (url.pathname === "/api/v1/fact-proposals/proposal-old")
|
||
return json(proposal);
|
||
if (
|
||
url.pathname === "/api/v1/works/work-a/world/entities/object-a/form"
|
||
) {
|
||
expect(url.search).toBe("");
|
||
return json(
|
||
表单结果(
|
||
"work-a",
|
||
"object-a",
|
||
{ dock_name: "当前名", capacity: 40 },
|
||
2,
|
||
),
|
||
);
|
||
}
|
||
throw new Error(`unexpected request: ${request.method} ${url.pathname}`);
|
||
}),
|
||
);
|
||
render(
|
||
<Ui依赖装配>
|
||
<Ui事实审阅 workId="work-a" proposalId="proposal-old" />
|
||
</Ui依赖装配>,
|
||
);
|
||
const openOld = await screen.findByRole("button", {
|
||
name: "核对当前作品并打开决定",
|
||
});
|
||
await waitFor(() =>
|
||
expect((openOld as HTMLButtonElement).disabled).toBe(false),
|
||
);
|
||
fireEvent.click(openOld);
|
||
await screen.findByText(
|
||
"当前资料已经变化,这份待决资料不能按旧版本打开决定。请重新编辑。",
|
||
);
|
||
expect(requests.some((request) => request.includes("/reviews"))).toBe(false);
|
||
expect(screen.getAllByText("旧提案名").length).toBeGreaterThan(0);
|
||
});
|
||
|
||
// @muse-case {"case_id":"NC-world-editor-scope-reset","environment":"离线","given":"具名作者、固定来源、隔离测试配置与真实模块存储","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-world-editor-scope-reset:切类型、切实体与切作品不沿用旧表单"],"contract":"docs/系统架构/新版设计/模块设计/B02-故事世界.md#作者实体表单与字段提案"}
|
||
it("NC-world-editor-scope-reset:切类型、切实体与切作品不沿用旧表单", async () => {
|
||
vi.stubGlobal(
|
||
"fetch",
|
||
vi.fn(async (request: Request) => {
|
||
const url = new URL(request.url);
|
||
if (url.pathname === "/api/v1/session")
|
||
return json({ author_id: "author" });
|
||
if (url.pathname === "/api/v1/world/types") return json(类型清单);
|
||
if (url.pathname === "/api/v1/works/work-a/world/entities")
|
||
return json([
|
||
{
|
||
object_id: "object-a",
|
||
type_id: "boarding_point",
|
||
revision: 1,
|
||
knowledge_mode: "setting",
|
||
independent: true,
|
||
依据可用: true,
|
||
生效位置: null,
|
||
计划窗口: null,
|
||
content: { dock_name: "旧码头", capacity: 20 },
|
||
},
|
||
]);
|
||
if (url.pathname === "/api/v1/works/work-b/world/entities")
|
||
return json([]);
|
||
if (url.pathname.endsWith("/form")) {
|
||
const objectId = url.pathname.split("/").at(-2)!;
|
||
const workId = url.pathname.includes("work-a") ? "work-a" : "work-b";
|
||
if (url.searchParams.get("schema_id") === "weather_station_v1")
|
||
return json({
|
||
...表单结果(workId, objectId),
|
||
type_id: "weather_station",
|
||
schema_binding: {
|
||
schema_id: "weather_station_v1",
|
||
base_version: 1,
|
||
extension_version: null,
|
||
effective_schema_hash: "weather-schema-current",
|
||
projection_version: "weather-projection-current",
|
||
},
|
||
content: {},
|
||
visible_schema: {
|
||
type: "object",
|
||
properties: {
|
||
station_name: { type: "string", title: "观天台名称" },
|
||
},
|
||
required: ["station_name"],
|
||
},
|
||
editable_schema: {
|
||
type: "object",
|
||
properties: {
|
||
station_name: { type: "string", title: "观天台名称" },
|
||
},
|
||
required: ["station_name"],
|
||
},
|
||
});
|
||
return json(
|
||
表单结果(workId, objectId, { dock_name: "旧码头", capacity: 20 }, 1),
|
||
);
|
||
}
|
||
if (url.pathname.endsWith("/timeline"))
|
||
return json({
|
||
object_id: "object-a",
|
||
type_id: "boarding_point",
|
||
instance_family: "entity",
|
||
current_revision: 1,
|
||
版本: [],
|
||
});
|
||
if (url.pathname.endsWith("/impact"))
|
||
return json({
|
||
object_id: "object-a",
|
||
current_revision: 1,
|
||
受影响事实: [],
|
||
本对象待决提案: [],
|
||
章序影响: [],
|
||
复核说明: "",
|
||
});
|
||
throw new Error(`unexpected request: ${request.method} ${url.pathname}`);
|
||
}),
|
||
);
|
||
const view = render(
|
||
<Ui依赖装配>
|
||
<Ui世界资料页 workId="work-a" />
|
||
</Ui依赖装配>,
|
||
);
|
||
fireEvent.click(
|
||
await screen.findByRole("button", { name: /登船点 · 旧码头/ }),
|
||
);
|
||
fireEvent.click(screen.getByRole("button", { name: "编辑资料" }));
|
||
const editRegion = await screen.findByRole("region", { name: "编辑资料" });
|
||
const dock = await within(editRegion).findByRole("textbox");
|
||
fireEvent.change(dock, { target: { value: "未保存的新名" } });
|
||
fireEvent.change(screen.getByLabelText("资料类型"), {
|
||
target: { value: "weather_station" },
|
||
});
|
||
expect(screen.queryByDisplayValue("未保存的新名")).toBeNull();
|
||
const addStation = screen.getByRole("button", { name: "添加资料" });
|
||
await waitFor(() =>
|
||
expect((addStation as HTMLButtonElement).disabled).toBe(false),
|
||
);
|
||
fireEvent.click(addStation);
|
||
const addRegion = await screen.findByRole("region", { name: "添加资料" });
|
||
expect(await within(addRegion).findByRole("textbox")).toBeTruthy();
|
||
view.rerender(
|
||
<Ui依赖装配>
|
||
<Ui世界资料页 workId="work-b" />
|
||
</Ui依赖装配>,
|
||
);
|
||
await waitFor(() =>
|
||
expect((screen.getByLabelText("资料类型") as HTMLSelectElement).value).toBe(
|
||
"",
|
||
),
|
||
);
|
||
expect(screen.queryByLabelText("观天台名称")).toBeNull();
|
||
expect(screen.queryByDisplayValue("未保存的新名")).toBeNull();
|
||
});
|