旧版前端工程文件导入时版本对比查验、审阅与仿真时部分阻挡功能实现;前端参数输入格式统一规范

This commit is contained in:
lujingze committed 2026-09-12 14:15:40 +00:00
1 parent 22579e51c9
commit 44b6ea74ab
32 files changed
+2087 -322

No files matched your search

+1 -1
View File
@@ -2864,7 +2864,7 @@ test("缺失或错误工程版本、字符串端口和缺失 Handle 均拒绝恢
if (invalidVariant === "missing-version") {
delete project.projectSchemaVersion;
} else if (invalidVariant === "wrong-version") {
project.projectSchemaVersion = 2;
project.projectSchemaVersion = 3;
} else if (invalidVariant === "string-port") {
project.nodes[0].data.ports = ["port_1"];
} else {
+8 -15
View File
@@ -883,7 +883,7 @@ test("MECMAS21 旧的 0/1 编码不转换并阻止执行", async ({
await expect(consolePanel).toContainText("模型检查发现");
});
test("缺失模型版本的工程可查看但不补默认参数且不能执行", async ({
test("缺失模型版本保留来源并补当前默认值,生成 XML 只警告", async ({
page,
}) => {
await page.goto("/");
@@ -923,8 +923,8 @@ test("缺失模型版本的工程可查看但不补默认参数且不能执行",
const node = page.locator(
'.flow-canvas .react-flow__node[data-id="amesim_mecmas21_1"]',
);
await expect(node.locator('[data-model-contract-compatible="false"]')).toBeVisible();
await expect(node.getByLabel(/模型合同不兼容/)).toBeVisible();
await expect(node.locator('[data-model-contract-compatible="true"]')).toBeVisible();
await expect(node.getByLabel(/模型合同不兼容/)).toHaveCount(0);
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const persistedWithoutDefaults = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
@@ -950,7 +950,7 @@ test("缺失模型版本的工程可查看但不补默认参数且不能执行",
data &&
data.modelVersion === undefined &&
data.parameters &&
!("strib" in data.parameters),
("strib" in data.parameters),
);
});
expect(persistedWithoutDefaults).toBe(true);
@@ -959,20 +959,13 @@ test("缺失模型版本的工程可查看但不补默认参数且不能执行",
name: "仿真控制台",
exact: true,
});
await page.getByRole("button", { name: "检查模型", exact: true }).click();
await expect(consolePanel).toHaveClass(/minimized/);
await expect(
consolePanel.locator(".simulation-console-dock-summary"),
).toContainText("模型检查发现");
await expandSimulationConsole(page);
await expect(consolePanel.locator(".simulation-console-dock-log")).toContainText(
"缺少 modelVersion",
);
await expect(consolePanel).toContainText("工程包含旧版或版本未知的组件");
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
await expect(consolePanel).toContainText("不能生成执行 XML");
await page.getByRole("button", { name: "运行仿真", exact: true }).click();
await expect(consolePanel).toContainText("模型检查发现");
await expect(consolePanel).toContainText("XML 已生成");
await expect(consolePanel).toContainText("可能出现仿真失败或仿真结果与实际不符");
await expect(consolePanel).not.toContainText("不能生成执行 XML");
});
test("详情浮层只在参数列悬停且下拉选择期间不触发", async ({ page }) => {
+1 -1
View File
@@ -103,7 +103,7 @@ test("压力默认显示 Pa,bar 与 Pa 换算保持绝压且无大气压偏移
await expectPressureXml(page, 250000);
}
const exported = await exportProject(page);
expect(exported.project.nodes[0].data.parameters.reference_pressure).toBe(250000);
expect(exported.project.nodes[0].data.parameters.reference_pressure).toBe(2.5);
expect(exported.project.nodes[0].data.parameterUnits.reference_pressure).toBe("bar");
});
@@ -0,0 +1,59 @@
import { expect, test } from "@playwright/test";
import cases from "../../../tests/fixtures/parameter-expressions.json" with { type: "json" };
import { evaluateParameterExpression } from "../../src/parameterExpression";
import { componentVersionNotices, decodeProjectParameters, encodeProjectParameters } from "../../src/projectCompatibility";
const definitions = [{ type: "chamber", modelType: "chamber", modelVersion: "2", parameters: {
p0: { unit: "Pa", quantity: "pressure" }, T0: { unit: "K", quantity: "temperature" },
} }];
function project(value: number | string, version: 1 | 2 = 2) {
return { projectSchemaVersion: version, nodes: [{ id: "c", data: { label: "c", componentType: "chamber",
modelType: "chamber", modelVersion: "1", parameters: { p0: value, T0: 20 }, parameterUnits: { p0: "bar", T0: "degC" } } }] };
}
test("shared expression grammar and resource limits", () => {
for (const [source, expected] of cases.valid) {
const actual = evaluateParameterExpression(String(source));
expect(actual.ok, String(source)).toBe(true);
if (actual.ok) expect(actual.value).toBeCloseTo(Number(expected), 12);
}
for (const source of [...cases.invalid, "(".repeat(34) + "1" + ")".repeat(34), "1+".repeat(256) + "1", "1".repeat(513), `min(${Array(17).fill("1").join(",")})`]) {
expect(evaluateParameterExpression(source).ok, source).toBe(false);
}
});
test("v2 selected-unit numbers and expression values agree; legacy numbers retain SI", () => {
for (const value of [2.5, "2.5", "=2.5", "2+0.5"]) {
const input = project(value);
const original = structuredClone(input);
const internal = decodeProjectParameters(input, definitions);
const parameter = internal.nodes[0].data.parameters.p0;
const actual = typeof parameter === "number" ? parameter :
(() => { const e = evaluateParameterExpression(parameter); return e.ok ? e.value * 1e5 : NaN; })();
expect(actual).toBe(250000);
expect(internal.nodes[0].data.parameters.T0).toBe(293.15);
const exported = encodeProjectParameters(internal, definitions);
expect(exported.projectSchemaVersion).toBe(2);
expect(decodeProjectParameters(exported, definitions)).toEqual(internal);
expect(input).toEqual(original);
}
expect(decodeProjectParameters(project(250000, 1), definitions).nodes[0].data.parameters.p0).toBe(250000);
expect(() => encodeProjectParameters(project(2.5), definitions)).toThrow(/重复换算/);
});
test("version index finds stale and missing versions without mutating or per-node fetch", () => {
const input = project(2.5);
const original = structuredClone(input);
expect(componentVersionNotices(input.nodes, definitions)).toEqual([
{ componentId: "c", label: "c", storedVersion: "1", currentVersion: "2" },
]);
expect(input).toEqual(original);
input.nodes[0].data.modelVersion = "2";
expect(componentVersionNotices(input.nodes, definitions)).toEqual([]);
});
test("export preserves exact SI doubles when display-unit round trip loses a bit", () => {
const input = project(15299999.999999998, 1);
input.nodes[0].data.parameterUnits.p0 = "bar";
input.nodes[0].data.parameters.T0 = 293.15;
const encoded = encodeProjectParameters(input, definitions);
expect(encoded.nodes[0].data.parameterUnits.p0).toBe("Pa");
expect(encoded.nodes[0].data.parameters.p0).toBe(15299999.999999998);
expect(decodeProjectParameters(encoded, definitions).nodes[0].data.parameters).toEqual(input.nodes[0].data.parameters);
});