完善建模交互、组件图标与系统协议

This commit is contained in:
ljz committed 2026-08-15 17:40:18 +08:00
1 parent 456c29b3b6
commit 6572defaa4
66 files changed
+10067 -4163

No files matched your search

+188 -20
View File
@@ -1,6 +1,6 @@
import { expect, test, type Locator, type Page } from "@playwright/test";
import { prepareApp } from "./fixtures";
import { expandSimulationConsole, prepareApp } from "./fixtures";
test.beforeEach(async ({ page }) => {
await prepareApp(page);
@@ -695,7 +695,7 @@ test("MECMAS21 参数组折叠和条件切换保留值并清理详情浮层", as
);
});
test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本", async ({
test("MECMAS21 当前参数编码由工程版本统一并写入 XML", async ({
page,
}) => {
await page.goto("/");
@@ -709,7 +709,7 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
await expect(frictionGroup).toHaveCount(0);
await page.getByRole("button", { name: "保存工程", exact: true }).click();
const savedEncodingVersions = await page.evaluate(() =>
const savedContracts = await page.evaluate(() =>
Object.keys(window.localStorage)
.filter((key) => key.startsWith("system-simulation-flow:project:"))
.flatMap((key) => {
@@ -718,24 +718,32 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
return [];
}
const project = JSON.parse(raw) as {
amesimParameterEncodingVersion?: number | null;
nodes?: Array<{ id?: string }>;
projectSchemaVersion?: number;
nodes?: Array<{ id?: string; data?: { modelVersion?: string } }>;
};
return project.nodes?.some(
const node = project.nodes?.find(
(node) => node.id === "amesim_mecmas21_1",
)
? [project.amesimParameterEncodingVersion]
);
return node
? [{
projectSchemaVersion: project.projectSchemaVersion,
modelVersion: node.data?.modelVersion,
}]
: [];
}),
);
expect(savedEncodingVersions).toContain(1);
expect(savedContracts).toContainEqual({
projectSchemaVersion: 1,
modelVersion: "0.2.0",
});
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
await expect(page.getByLabel("生成的系统 XML")).toContainText(
'amesimParameterEncodingVersion="1"',
'type="amesim_mecmas21" modelVersion="0.2.0"',
);
const migratedProjectCount = await page.evaluate(() => {
const changedProjectCount = await page.evaluate(() => {
let changed = 0;
Object.keys(window.localStorage)
.filter((key) =>
@@ -750,8 +758,6 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
return;
}
const project = JSON.parse(raw) as {
mediumReferenceVersion?: number | null;
amesimParameterEncodingVersion?: number | null;
nodes?: Array<{
id?: string;
data?: {
@@ -766,18 +772,13 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
if (!node?.data?.parameters) {
return;
}
project.amesimParameterEncodingVersion = null;
project.mediumReferenceVersion = null;
delete node.data.modelType;
node.data.parameters.useFriction = 0;
delete node.data.parameters.strib;
node.data.parameters.unrelatedCode = 9;
window.localStorage.setItem(key, JSON.stringify(project));
changed += 1;
});
return changed;
});
expect(migratedProjectCount).toBeGreaterThanOrEqual(2);
expect(changedProjectCount).toBeGreaterThanOrEqual(2);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
await page
@@ -791,6 +792,7 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
const frictionType = page.getByLabel("摩擦类型", { exact: true });
await frictionType.selectOption("2");
const strib = page.getByLabel("Stribeck 选项", { exact: true });
await strib.selectOption("2");
await expect(strib).toHaveValue("2");
await expect(strib.locator("option:checked")).toHaveText("启用");
await expect(page.getByLabel("无关编码", { exact: true })).toHaveValue("9");
@@ -813,6 +815,166 @@ test("MECMAS21 旧参数编码只迁移一次且新工程和 XML 写入版本",
await expect(strib).toHaveValue("2");
});
test("MECMAS21 旧的 0/1 编码不转换并阻止执行", async ({
page,
}) => {
await page.goto("/");
await addMecmasEncodingTestComponent(page);
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:"),
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!key || !raw) {
return false;
}
const project = JSON.parse(raw) as {
nodes?: Array<{
id?: string;
data?: {
parameters?: Record<string, number | string>;
};
}>;
};
const node = project.nodes?.find(
(candidate) => candidate.id === "amesim_mecmas21_1",
);
if (!node?.data?.parameters) {
return false;
}
node.data.parameters.useFriction = 0;
window.localStorage.setItem(key, JSON.stringify(project));
return true;
});
expect(changed).toBe(true);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
const node = page.locator(
'.flow-canvas .react-flow__node[data-id="amesim_mecmas21_1"]',
);
await expect(node.locator('[data-model-contract-compatible="true"]')).toBeVisible();
await node.click();
const useFriction = page.getByLabel("启用摩擦", { exact: true });
await expect(useFriction).toHaveValue("");
await expect(useFriction.locator("option:checked")).toHaveText(
"当前选项无效,请重新选择",
);
const consolePanel = page.getByRole("complementary", {
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(
"useFriction=0",
);
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("模型检查发现");
});
test("缺失模型版本的工程可查看但不补默认参数且不能执行", async ({
page,
}) => {
await page.goto("/");
await addMecmasEncodingTestComponent(page);
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:"),
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!key || !raw) {
return false;
}
const project = JSON.parse(raw) as {
nodes?: Array<{
id?: string;
data?: {
modelVersion?: string;
parameters?: Record<string, number | string>;
};
}>;
};
const node = project.nodes?.find(
(candidate) => candidate.id === "amesim_mecmas21_1",
);
if (!node?.data?.parameters) {
return false;
}
delete node.data.modelVersion;
delete node.data.parameters.strib;
window.localStorage.setItem(key, JSON.stringify(project));
return true;
});
expect(changed).toBe(true);
await page.getByRole("button", { name: "加载工程", exact: true }).click();
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 page.getByRole("button", { name: "保存工程", exact: true }).click();
const persistedWithoutDefaults = await page.evaluate(() => {
const key = Object.keys(window.localStorage).find((candidate) =>
candidate.startsWith("system-simulation-flow:project:"),
);
const raw = key ? window.localStorage.getItem(key) : null;
if (!raw) {
return false;
}
const project = JSON.parse(raw) as {
nodes?: Array<{
id?: string;
data?: {
modelVersion?: string;
parameters?: Record<string, number | string>;
};
}>;
};
const data = project.nodes?.find(
(candidate) => candidate.id === "amesim_mecmas21_1",
)?.data;
return Boolean(
data &&
data.modelVersion === undefined &&
data.parameters &&
!("strib" in data.parameters),
);
});
expect(persistedWithoutDefaults).toBe(true);
const consolePanel = page.getByRole("complementary", {
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 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("模型检查发现");
});
test("详情浮层只在参数列悬停且下拉选择期间不触发", async ({ page }) => {
await page.goto("/");
await addMecmasEncodingTestComponent(page);
@@ -995,6 +1157,7 @@ test("参数说明只在参数列悬停显示,单位切换不改变 XML 基础
);
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
const consolePanel = page.getByRole("complementary", {
name: "仿真控制台",
exact: true,
@@ -1056,6 +1219,7 @@ test("科学计数法在提交、单位换算和工程加载后保持", async ({
await expect(temperatureInput).toHaveValue("1e-10");
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
await expect(page.getByLabel("生成的系统 XML")).toContainText(
'<Parameter name="reference_pressure" value="1200000"',
);
@@ -1235,6 +1399,7 @@ test("组件参数公式保留原式,并在说明中显示计算值且 XML 只
);
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
const consolePanel = page.getByRole("complementary", {
name: "仿真控制台",
exact: true,
@@ -1275,6 +1440,7 @@ test("非法组件参数公式显示错误并阻止 XML 生成", async ({ page }
).toBeVisible();
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
const consolePanel = page.getByRole("complementary", {
name: "仿真控制台",
exact: true,
@@ -1305,6 +1471,7 @@ test("仿真数值设置同样保留公式,并在执行协议中使用计算
).toContainText("7 s");
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
const xmlBlock = page.getByLabel("生成的系统 XML");
await expect(xmlBlock).toContainText('tStop="7"');
await expect(xmlBlock).not.toContainText(expression);
@@ -1330,7 +1497,8 @@ test("仿真数值设置中的科学计数法在保存加载后保持", async ({
await expect(stepInput).toHaveValue("1e-5");
await expect(maxStepInput).toHaveValue("1.20E-4");
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
await expandSimulationConsole(page);
const xmlBlock = page.getByLabel("生成的系统 XML");
await expect(xmlBlock).toContainText('step="0.00001"');
await expect(xmlBlock).toContainText('sampleStep="0.00001"');
await expect(xmlBlock).toContainText('maxStep="0.00012"');
});