高温氦气物性补全;三通四通能量计算bug修正

This commit is contained in:
ljz committed 2026-09-15 17:57:20 +08:00
1 parent 6fc9afe41d
commit 2b07d996cf
133 files changed
+5230 -253310

No files matched your search

+44 -10
View File
@@ -499,6 +499,7 @@ type SimulationResult = {
final: Record<string, number>;
series: Record<string, number[]>;
diagnostics: {
propertyWarnings?: { code: string; message: string }[];
pressureFlow?: {
solveCount: number;
maxScaledResidual: number;
@@ -4213,7 +4214,7 @@ function FlowWorkbench() {
const [alignmentGuides, setAlignmentGuides] = useState<AlignmentGuide[]>([]);
const [alignmentEnabled, setAlignmentEnabled] = useState(true);
const [nodeDragActive, setNodeDragActive] = useState(false);
const [projectName, setProjectName] = useState("demo-system");
const [projectName, setProjectName] = useState(loadInitialProjectName);
const [simulationConfig, setSimulationConfig] = useState<SimulationConfig>(
defaultSimulationConfig,
);
@@ -5146,6 +5147,7 @@ function FlowWorkbench() {
),
[edges, nodeDragActive, nodes, projectName, simulationConfig],
);
const lastAutosaveSignatureRef = useRef(projectChangeSignature);
const validationSignature = useMemo(
() =>
nodeDragActive && validationSignatureRef.current
@@ -7501,6 +7503,7 @@ function FlowWorkbench() {
const project = prepareProjectForStorage(buildCurrentProject());
localStorage.setItem(localProjectKey(project.name), JSON.stringify(project));
localStorage.setItem(AUTOSAVE_KEY, JSON.stringify(project));
lastAutosaveSignatureRef.current = projectChangeSignature;
appendConsoleEntry(
"success",
`工程已保存到浏览器:${project.nodes.length} 个组件,${project.edges.length} 条连接`,
@@ -7514,17 +7517,19 @@ function FlowWorkbench() {
if (pendingPasteRef.current) {
return;
}
const name = projectName.trim() || "untitled";
try {
const project = parseProjectPayload(localStorage.getItem(localProjectKey(name)));
if (!project) {
appendConsoleEntry("warning", `没有找到本地工程:${name}`);
const raw = localStorage.getItem(AUTOSAVE_KEY);
if (!raw) {
appendConsoleEntry("warning", "尚无自动保存或保存到浏览器的工程,请先导入或创建工程");
return;
}
const project = parseProjectPayload(raw);
if (!project) {
throw new Error("最后一次浏览器存档格式无效,已保留当前画布和原存档");
}
applyProject(
// Older entries may still contain a name that differs from their storage key.
{ ...project, name },
`工程已加载:${project.nodes.length} 个组件,${project.edges.length} 条连接`,
project,
`已加载最后一次浏览器存档:${project.name}(${project.nodes.length} 个组件,${project.edges.length} 条连接)`,
);
} catch (error) {
appendConsoleEntry("error", `加载失败:${formatError(error)}`);
@@ -7612,7 +7617,14 @@ function FlowWorkbench() {
const publishSimulationResult = (
result: SimulationResult,
project: ExecutableProjectPayload,
shownWarnings: Set<string> = new Set(),
) => {
for (const warning of result.diagnostics.propertyWarnings ?? []) {
if (!shownWarnings.has(warning.message)) {
appendConsoleEntry("warning", warning.message);
shownWarnings.add(warning.message);
}
}
const resultStatus = result.status ?? (result.success ? "completed" : "failed");
const sampleCount = Math.min(
result.diagnostics.sampleCount ?? result.series.time?.length ?? 0,
@@ -7773,6 +7785,7 @@ function FlowWorkbench() {
activeSimulationIdRef.current = simulationId;
setSimulationStopPending(false);
let lastLoggedPhase = "";
const shownPropertyWarnings = new Set<string>();
let lastLoggedBucket = -1;
let lastWatchdogStatus: SimulationActivityWatchdogStatus = "progressing";
try {
@@ -7782,6 +7795,13 @@ function FlowWorkbench() {
if (event.event !== "progress") {
return;
}
if (event.phase === "property-warning") {
if (!shownPropertyWarnings.has(event.message)) {
appendConsoleEntry("warning", event.message);
shownPropertyWarnings.add(event.message);
}
return;
}
const normalizedProgress = normalizeSimulationProgressEvent(
event,
project.simulation,
@@ -7847,7 +7867,7 @@ function FlowWorkbench() {
},
simulationId,
);
publishSimulationResult(result, project);
publishSimulationResult(result, project, shownPropertyWarnings);
} catch (error) {
let failure: unknown = error;
if (
@@ -7867,7 +7887,7 @@ function FlowWorkbench() {
await requestSimulationCancellation(simulationId, "stalled");
const task = await waitForSimulationTaskCompletion(simulationId);
if (task.result) {
publishSimulationResult(task.result, project);
publishSimulationResult(task.result, project, shownPropertyWarnings);
return;
}
if (task.error?.event === "error") {
@@ -8065,6 +8085,9 @@ function FlowWorkbench() {
if (!autosaveReady || nodeDragActive) {
return;
}
// Restoring the name into an initially empty editor is not an edit.
// Leave the latest snapshot intact until the user actually changes content.
if (lastAutosaveSignatureRef.current === projectChangeSignature) return;
const timer = window.setTimeout(() => {
try {
const project = buildProjectPayload(
@@ -8075,6 +8098,7 @@ function FlowWorkbench() {
);
const storedProject = prepareProjectForStorage(project);
localStorage.setItem(AUTOSAVE_KEY, JSON.stringify(storedProject));
lastAutosaveSignatureRef.current = projectChangeSignature;
lastAutosaveErrorRef.current = "";
if (project.nodes.length || project.edges.length) {
pendingAutosaveNoticeRef.current = { name: project.name, savedAt: new Date() };
@@ -10997,6 +11021,16 @@ function localProjectKey(name: string) {
return `${PROJECT_KEY_PREFIX}${encodeURIComponent(name.trim() || "untitled")}`;
}
function loadInitialProjectName() {
try {
const project = parseProjectPayload(localStorage.getItem(AUTOSAVE_KEY));
return project ? project.name.trim() || "untitled" : "demo-system";
} catch {
// The recovery/loading paths report malformed or inaccessible storage.
return "demo-system";
}
}
function isEditableTarget(target: EventTarget | null) {
return (
target instanceof HTMLElement &&
+1 -1
View File
@@ -2,7 +2,7 @@ import { expect, test, type Locator, type Page } from "@playwright/test";
import { buildAlignmentIndex, findAlignment, type AlignmentAnchor } from "../../src/alignmentGuides";
import { componentCatalog, expectAllNodesInsideCanvas, prepareApp, wideProject } from "./fixtures";
const KEY = "system-simulation-flow:project:demo-system";
const KEY = "system-simulation-flow:autosave";
const anchor = (nodeId: string, kind: AlignmentAnchor["kind"], x: number, y: number): AlignmentAnchor =>
({ id: `${nodeId}:${kind}`, nodeId, kind, x, y });
+10 -10
View File
@@ -1782,7 +1782,7 @@ test("旧版 LMECHN1 工程会把固定参考端口迁移到动态最大编号",
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const downgraded = await page.evaluate(() => {
const key = Object.keys(localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? localStorage.getItem(key) : null;
if (!key || !raw) {
@@ -1848,7 +1848,7 @@ test("旧版 LMECHN1 工程会把固定参考端口迁移到动态最大编号",
.poll(() =>
page.evaluate(() => {
const key = Object.keys(localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? localStorage.getItem(key) : null;
const project = raw ? JSON.parse(raw) : null;
@@ -1982,7 +1982,7 @@ test("旧版 FORC 工程会迁移为默认正向且不再依赖图标旋转", as
const downgraded = await page.evaluate(() => {
const key = Object.keys(localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? localStorage.getItem(key) : null;
if (!key || !raw) {
@@ -2026,7 +2026,7 @@ test("旧版 FORC 工程会迁移为默认正向且不再依赖图标旋转", as
.poll(() =>
page.evaluate(() => {
const key = Object.keys(localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? localStorage.getItem(key) : null;
const project = raw ? JSON.parse(raw) : null;
@@ -2898,7 +2898,7 @@ test("恢复工程时采用当前目录端口并明确丢弃已删除端口的
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const changed = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!key || !raw) {
@@ -3168,7 +3168,7 @@ test("AMESim canvas nodes use icon anchors and highlight only compatible free po
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const routedEdge = await page.evaluate(() => {
const raw = window.localStorage.getItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
);
const project = raw ? JSON.parse(raw) : null;
return project?.edges?.[0] ?? null;
@@ -3659,7 +3659,7 @@ test("兼容接口接近时吸附为无连线接触连接,拖开后自动断
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const savedEdge = await page.evaluate(() => {
const raw = window.localStorage.getItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
);
const project = raw ? JSON.parse(raw) : null;
return project?.edges?.[0] ?? null;
@@ -3823,7 +3823,7 @@ test("信号输出连接后隐藏且拒绝第二条连接", async ({ page }) =>
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const addedLegacyFanOut = await page.evaluate(() => {
const key = "system-simulation-flow:project:demo-system";
const key = "system-simulation-flow:autosave";
const raw = window.localStorage.getItem(key);
if (!raw) {
return false;
@@ -3860,7 +3860,7 @@ test("信号输出连接后隐藏且拒绝第二条连接", async ({ page }) =>
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const normalizedEdgeIds = await page.evaluate(() => {
const raw = window.localStorage.getItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
);
const project = raw ? JSON.parse(raw) : null;
return (project?.edges ?? []).map((edge: { id?: string }) => edge.id);
@@ -3949,7 +3949,7 @@ test("一次拖动会吸附同一落点内的全部兼容接口", async ({ page
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const savedEndpointKeys = await page.evaluate(() => {
const raw = window.localStorage.getItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
);
const edges = raw ? (JSON.parse(raw)?.edges ?? []) : [];
return edges.flatMap(
+1 -1
View File
@@ -3,7 +3,7 @@ import { Position } from "@xyflow/react";
import { componentCatalog, expectAllNodesInsideCanvas, prepareApp, wideProject } from "./fixtures";
import { confirmOrthogonalCorner, extendOrthogonalRoute } from "../../src/edgeRouting";
const PROJECT_KEY = "system-simulation-flow:project:demo-system";
const PROJECT_KEY = "system-simulation-flow:autosave";
test("每次左键只确认一段并转向,空白预览不插入自动中点", () => {
const source = { x: 0, y: 0 };
+5 -5
View File
@@ -2,7 +2,7 @@ import { expect, test, type Locator, type Page } from "@playwright/test";
import { prepareApp, wideProject } from "./fixtures";
const PROJECT_KEY = "system-simulation-flow:project:demo-system";
const PROJECT_KEY = "system-simulation-flow:autosave";
test.beforeEach(async ({ page }) => {
await prepareApp(page);
@@ -316,7 +316,7 @@ test("选中正交连接线不显示拐点,仍可拖动线段、撤销并保
};
await page.addInitScript((storedProject) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(storedProject),
);
}, project);
@@ -447,7 +447,7 @@ test("选中正交连接线不显示拐点,仍可拖动线段、撤销并保
test("缩小画布后连接线端点仍与端口图标边缘对齐", async ({ page }) => {
await page.addInitScript((storedProject) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(storedProject),
);
}, wideProject);
@@ -552,7 +552,7 @@ for (const route of [
};
await page.addInitScript((storedProject) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(storedProject),
);
}, project);
@@ -688,7 +688,7 @@ test("同一组件不同端口发出的线路在远端交叉时仍显示跨线
};
await page.addInitScript((storedProject) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(storedProject),
);
}, project);
+4 -4
View File
@@ -323,7 +323,7 @@ test("加载本地工程后自动适配建模画布", async ({ page }) => {
await prepareApp(page);
await page.addInitScript((project) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(project),
);
}, wideProject);
@@ -341,7 +341,7 @@ test("加载本地工程后自动适配建模画布", async ({ page }) => {
);
await expect(consolePanel.locator(".simulation-console-dock-log")).toHaveCount(0);
await page.getByRole("button", { name: "展开仿真控制台" }).click();
await expect(consolePanel).toContainText("工程已加载");
await expect(consolePanel).toContainText("已加载最后一次浏览器存档");
});
test("导入工程 JSON 后自动适配建模画布", async ({ page }) => {
@@ -495,7 +495,7 @@ test("切换到结果页时自动适配只读系统图,旧网格线设置不
JSON.stringify(snapshot),
);
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(project),
);
window.localStorage.setItem(
@@ -704,7 +704,7 @@ test("从结果页返回建模页后保留用户调整的建模视口", async ({
JSON.stringify(snapshot),
);
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(project),
);
}, { project: wideProject, snapshot: resultSnapshot });
@@ -0,0 +1,58 @@
import { test, expect } from "@playwright/test";
import { expandSimulationConsole, prepareApp, wideProject } from "./fixtures";
const latestKey = "system-simulation-flow:autosave";
test.beforeEach(async ({ page }) => { await prepareApp(page); });
test("刷新恢复最新工程名称,初始空画布及忽略提示不会覆盖最新存档", async ({ page }) => {
await page.goto("/");
const project = { ...wideProject, name: "持久化 工程名称.v2" };
await page.evaluate(({ key, project }) => localStorage.setItem(key, JSON.stringify(project)), { key: latestKey, project });
await page.clock.install();
await page.reload();
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(project.name);
await page.getByRole("button", { name: "忽略", exact: true }).click();
await page.clock.runFor(6000);
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(0);
expect(await page.evaluate(key => JSON.parse(localStorage.getItem(key)!), latestKey)).toEqual(project);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(4);
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(project.name);
});
test("自动保存和手动保存共用最新入口,哪个后完成就加载哪个", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("button", { name: /通用测试元件/ })).toBeVisible();
for (const [name, count, manual] of [["手动 A", 4, true], ["自动 B", 2, false], ["手动 C", 3, true]] as const) {
await page.locator('input[type="file"]').setInputFiles({ name: `${name}.json`, mimeType: "application/json",
buffer: Buffer.from(JSON.stringify({ ...wideProject, nodes: wideProject.nodes.slice(0, count), edges: [] })) });
if (manual) await page.getByRole("button", { name: "保存工程", exact: true }).click();
await expect.poll(() => page.evaluate(key => JSON.parse(localStorage.getItem(key) || "null")?.name, latestKey)).toBe(name);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(count);
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(name);
}
});
test("没有最新存档时不猜选历史命名工程,也不改动画布", async ({ page }) => {
await page.goto("/");
await page.evaluate(project => localStorage.setItem("system-simulation-flow:project:demo-system", JSON.stringify(project)), wideProject);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await expandSimulationConsole(page);
await expect(page.getByRole("complementary", { name: "仿真控制台", exact: true })).toContainText("尚无自动保存或保存到浏览器的工程");
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(0);
});
test("明确保存的空工程也是最新工程,不回退到旧的非空副本", async ({ page }) => {
await page.goto("/");
const empty = { ...wideProject, name: "已清空工程", nodes: [], edges: [] };
await page.evaluate(({ key, empty, old }) => {
localStorage.setItem(key, JSON.stringify(empty));
localStorage.setItem("system-simulation-flow:pending-recovery", JSON.stringify(old));
}, { key: latestKey, empty, old: wideProject });
await page.reload();
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(empty.name);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(0);
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(empty.name);
});
+3 -3
View File
@@ -86,7 +86,7 @@ test("新版存档拒绝目录未定义的参数,保留当前有效工程及 X
await page.getByRole("button", { name: "保存工程", exact: true }).click();
await page.evaluate(() => {
const key = "system-simulation-flow:project:demo-system";
const key = "system-simulation-flow:autosave";
const raw = window.localStorage.getItem(key);
if (!raw) {
throw new Error("测试工程未保存");
@@ -119,7 +119,7 @@ test("XML 参数使用可往返的双精度文本", async ({ page }) => {
await page.getByRole("button", { name: "保存工程", exact: true }).click();
await page.evaluate(() => {
const key = "system-simulation-flow:project:demo-system";
const key = "system-simulation-flow:autosave";
const raw = window.localStorage.getItem(key);
if (!raw) {
throw new Error("测试工程未保存");
@@ -302,7 +302,7 @@ test("复制已连接组件时副本 ID 避让画布中的同名节点且不会
};
await page.addInitScript((storedProject) => {
window.localStorage.setItem(
"system-simulation-flow:project:demo-system",
"system-simulation-flow:autosave",
JSON.stringify(storedProject),
);
}, project);
+47 -1
View File
@@ -1,6 +1,13 @@
import { expect, test } from "@playwright/test";
import { expandSimulationConsole, prepareApp, resultSnapshot, wideProject } from "./fixtures";
test.beforeEach(async ({ page }) => {
await page.route("**/api/simulation/runtime-check", route => route.fulfill({ json: {
checkId: "test-runtime", status: "completed", stage: "complete",
message: "仿真环境自检通过", durationMs: 1, error: null, failure: null,
} }));
});
for (const diagnosticsMode of ["native", "legacy-native", "python"] as const) {
test(`completed ${diagnosticsMode} results can be published and restored`, async ({ page }) => {
await prepareApp(page);
@@ -21,7 +28,7 @@ for (const diagnosticsMode of ["native", "legacy-native", "python"] as const) {
sourceHandle: "port_b", targetHandle: "port_a", data: { isContactEdge: false },
}] })),
});
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue(wideProject.name);
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue("native-result");
await page.getByRole("button", { name: "运行仿真", exact: true }).click();
await expandSimulationConsole(page);
const consolePanel = page.getByRole("complementary", { name: "仿真控制台", exact: true });
@@ -45,3 +52,42 @@ for (const diagnosticsMode of ["native", "legacy-native", "python"] as const) {
await expect(page.getByText("3 个采样点", { exact: false }).first()).toBeVisible();
});
}
for (const live of [true, false]) {
test(`property temperature warning is shown once with live delivery ${live}`, async ({ page }) => {
await prepareApp(page);
const message = "氦气物性有效温度越界:状态方程及热力性质的真实气体修正项,t = 0.001 s,T = 1600 K > Tmax = 1500 K。仿真继续计算,超范围物性不保证有效性。";
const warning = { code: "helium-equation-of-state-high", message };
const events = [
...(live ? [0, 1].map(() => ({ event: "progress", progress: 1,
phase: "property-warning", message, simulatedTime: .001, totalTime: 1 })) : []),
{ event: "result", progress: 100, phase: "completed", result: {
...resultSnapshot.result, diagnostics: { backend: "native-c", stateCount: 1,
sampleCount: 3, propertyWarnings: [warning] },
} },
];
await page.route("**/api/system-xml/simulate-stream", route => route.fulfill({
contentType: "application/x-ndjson", body: events.map(e => JSON.stringify(e)).join("\n") + "\n",
}));
await page.goto("/");
await page.locator('input[type="file"]').setInputFiles({
name: "property-warning.json", mimeType: "application/json",
buffer: Buffer.from(JSON.stringify({ ...wideProject, edges: [...wideProject.edges, {
id: "close-test-loop", source: "generic_sensor_4", target: "generic_sensor_1",
sourceHandle: "port_b", targetHandle: "port_a", data: { isContactEdge: false },
}] })),
});
await expect(page.getByRole("textbox", { name: "工程", exact: true })).toHaveValue("property-warning");
await page.getByRole("button", { name: "运行仿真", exact: true }).click();
await expandSimulationConsole(page);
const panel = page.getByRole("complementary", { name: "仿真控制台", exact: true });
await expect(panel.getByText(message, { exact: true })).toHaveCount(1);
await expect(panel).toContainText("仿真完成,已生成新的结果,共 3 个采样点");
await expect(panel).not.toContainText("仿真失败");
await expect.poll(() => page.evaluate(async () => {
// @ts-expect-error Vite serves the persistence module in the test browser.
const { loadStoredResultSnapshot } = await import("/src/resultPersistence.ts");
return (await loadStoredResultSnapshot())?.result.diagnostics.propertyWarnings;
})).toEqual([warning]);
});
}
+3 -3
View File
@@ -824,7 +824,7 @@ test("MECMAS21 旧的 0/1 编码不转换并阻止执行", async ({
const changed = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!key || !raw) {
@@ -891,7 +891,7 @@ test("缺失模型版本加载时警告并补默认值,保存时升级当前
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const changed = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!key || !raw) {
@@ -928,7 +928,7 @@ test("缺失模型版本加载时警告并补默认值,保存时升级当前
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const persistedWithCurrentVersion = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
candidate === "system-simulation-flow:autosave",
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!raw) {
@@ -15,7 +15,7 @@ for (const action of ["import", "load", "recover"] as const) {
await expect(page.locator(".flow-canvas")).not.toHaveClass(/is-fitting-project/);
}
await page.evaluate(({ project, action }) => {
const key = action === "recover" ? "system-simulation-flow:autosave" : "system-simulation-flow:project:target";
const key = "system-simulation-flow:autosave";
localStorage.setItem(key, JSON.stringify(project));
}, { project: wideProject, action });
if (action === "recover") {
@@ -243,7 +243,7 @@ test("不能编码新版的工程不覆盖已有浏览器存档,也不下载
expect(downloads).toEqual([]);
});
test("浏览器工程内容保留旧名称时,按存储名称连续加载仍能恢复工程", async ({
test("加载始终读取最后一次浏览器存档并采用存档名称,不按输入名称选历史工程", async ({
page,
}) => {
const storedName = "已重命名 工程.v3";
@@ -251,8 +251,9 @@ test("浏览器工程内容保留旧名称时,按存储名称连续加载仍
({ name, project }) => {
localStorage.setItem(
`system-simulation-flow:project:${encodeURIComponent(name)}`,
JSON.stringify(project),
JSON.stringify({ ...project, name, nodes: [] , edges: [] }),
);
localStorage.setItem("system-simulation-flow:autosave", JSON.stringify(project));
},
{ name: storedName, project: wideProject },
);
@@ -261,14 +262,14 @@ test("浏览器工程内容保留旧名称时,按存储名称连续加载仍
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(
wideProject.nodes.length,
);
await expect(projectNameField(page)).toHaveValue(storedName);
await expect(projectNameField(page)).toHaveValue(wideProject.name);
await deleteFirstNode(page);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(
wideProject.nodes.length,
);
await expect(projectNameField(page)).toHaveValue(storedName);
await expect(projectNameField(page)).toHaveValue(wideProject.name);
});
for (const { internalName, expectedName } of [
+1 -1
View File
@@ -65,7 +65,7 @@ async function importMirrorProject(page: Page, project: ReturnType<typeof mirror
async function saveProject(page: Page) {
await page.getByRole("button", { name: "保存工程", exact: true }).click();
return page.evaluate(() => JSON.parse(window.localStorage.getItem(
"system-simulation-flow:project:vertical-mirror",
"system-simulation-flow:autosave",
)!)) as Promise<ReturnType<typeof mirrorProject>>;
}