feat: integrate AMESim media models and editor UI

This commit is contained in:
ljz committed 2026-07-31 23:36:58 +08:00
1 parent 3e4f466114
commit e7177ab03e
181 files changed
+7042 -25270

No files matched your search

+273
View File
@@ -0,0 +1,273 @@
import { expect, test, type Locator } from "@playwright/test";
import { prepareApp } from "./fixtures";
test.beforeEach(async ({ page }) => {
await prepareApp(page);
await page.addInitScript(() => {
window.localStorage.removeItem(
"system-simulation-flow:palette-collapsed-sections",
);
});
});
async function expectFixedIconCardSize(
item: Locator,
) {
const itemBox = await item.boundingBox();
const iconBox = await item.locator(".palette-icon").boundingBox();
expect(itemBox).not.toBeNull();
expect(iconBox).not.toBeNull();
expect(itemBox!.width).toBeGreaterThanOrEqual(75);
expect(itemBox!.width).toBeLessThanOrEqual(77);
expect(itemBox!.height).toBeGreaterThanOrEqual(81);
expect(itemBox!.height).toBeLessThanOrEqual(83);
expect(iconBox!.width).toBeGreaterThanOrEqual(47);
expect(iconBox!.width).toBeLessThanOrEqual(49);
expect(iconBox!.height).toBeGreaterThanOrEqual(47);
expect(iconBox!.height).toBeLessThanOrEqual(49);
}
test("组件库可切换详细与仅图标显示,并在刷新后保留选择", async ({ page }) => {
await page.goto("/");
const palette = page.locator("aside.palette");
const detailedButton = page.getByRole("button", {
name: "详细显示",
exact: true,
});
const iconsButton = page.getByRole("button", {
name: "仅图标显示",
exact: true,
});
const airItem = palette.getByRole("button", {
name: "空气介质定义",
exact: true,
});
const heliumItem = palette.getByRole("button", {
name: "氦气介质定义",
exact: true,
});
await expect(palette).toHaveAttribute("data-view-mode", "detailed");
await expect(detailedButton).toHaveAttribute("aria-pressed", "true");
await expect(iconsButton).toHaveAttribute("aria-pressed", "false");
await expect(airItem.locator(".palette-item-copy")).toBeVisible();
const airSymbol = airItem.locator(
'[data-component-symbol="amesim_ideal_air_medium"]',
);
const heliumSymbol = heliumItem.locator(
'[data-component-symbol="amesim_helium_medium"]',
);
await expect(airSymbol.locator("svg")).toBeVisible();
await expect(
airSymbol.locator('[data-symbol-label="Air"]'),
).toHaveText("Air");
await expect(airSymbol.locator(".component-symbol-medium-svg path")).toHaveCount(0);
await expect(heliumSymbol.locator("svg")).toBeVisible();
await expect(
heliumSymbol.locator('[data-symbol-label="He"]'),
).toHaveText("He");
await expect(heliumSymbol.locator(".component-symbol-medium-svg path")).toHaveCount(0);
const unknownSymbol = palette
.getByRole("button", { name: "通用测试元件", exact: true })
.locator('[data-component-symbol="generic"]');
await expect(unknownSymbol.locator("svg")).toBeVisible();
await iconsButton.click();
await expect(palette).toHaveAttribute("data-view-mode", "icons");
await expect(detailedButton).toHaveAttribute("aria-pressed", "false");
await expect(iconsButton).toHaveAttribute("aria-pressed", "true");
await expect(airItem).toBeVisible();
await expect(airItem.locator(".palette-item-name")).toBeVisible();
await expect(airItem.locator(".palette-item-name")).toHaveText("空气介质定义");
await expect(airItem.locator(".palette-item-meta")).toBeHidden();
await expect(airItem).not.toHaveAttribute("title");
await page.reload();
await expect(palette).toHaveAttribute("data-view-mode", "icons");
await expect(iconsButton).toHaveAttribute("aria-pressed", "true");
await expect(airItem.locator(".palette-item-name")).toBeVisible();
await expect(airItem.locator(".palette-item-meta")).toBeHidden();
});
test("组件库和分类支持独立折叠并同步可访问状态", async ({ page }) => {
await page.goto("/");
const palette = page.locator("aside.palette");
const libraryToggle = palette.getByRole("button", {
name: "自动化测试组件库",
exact: true,
});
const categoryToggle = palette.getByRole("button", {
name: "介质物性",
exact: true,
});
await expect(libraryToggle).toHaveAttribute("aria-expanded", "true");
const libraryContentId = await libraryToggle.getAttribute("aria-controls");
expect(libraryContentId).toBeTruthy();
await expect(page.locator(`[id="${libraryContentId}"]`)).toBeVisible();
await expect(categoryToggle).toHaveAttribute("aria-expanded", "true");
const categoryItemsId = await categoryToggle.getAttribute("aria-controls");
expect(categoryItemsId).toBeTruthy();
await expect(page.locator(`[id="${categoryItemsId}"]`)).toBeVisible();
await expect(
palette.getByRole("button", { name: "空气介质定义", exact: true }),
).toBeVisible();
await categoryToggle.click();
await expect(categoryToggle).toHaveAttribute("aria-expanded", "false");
await expect(page.locator(`[id="${categoryItemsId}"]`)).toHaveCount(0);
await expect(
palette.getByRole("button", { name: "空气介质定义", exact: true }),
).toHaveCount(0);
await categoryToggle.click();
await expect(categoryToggle).toHaveAttribute("aria-expanded", "true");
await expect(page.locator(`[id="${categoryItemsId}"]`)).toBeVisible();
await libraryToggle.click();
await expect(libraryToggle).toHaveAttribute("aria-expanded", "false");
await expect(page.locator(`[id="${libraryContentId}"]`)).toHaveCount(0);
await expect(
palette.getByRole("button", { name: "介质物性", exact: true }),
).toHaveCount(0);
await libraryToggle.click();
await expect(libraryToggle).toHaveAttribute("aria-expanded", "true");
await expect(page.locator(`[id="${libraryContentId}"]`)).toBeVisible();
await expect(
palette.getByRole("button", { name: "介质物性", exact: true }),
).toHaveAttribute("aria-expanded", "true");
});
test("仅图标模式名称截断且卡片尺寸不随侧栏和视口改变", async ({ page }) => {
await page.goto("/");
await page
.getByRole("button", { name: "仅图标显示", exact: true })
.click();
const palette = page.locator("aside.palette");
const longNameItem = palette.getByRole("button", {
name: "AMESim 气动测试元件",
exact: true,
});
const name = longNameItem.locator(".palette-item-name");
const meta = longNameItem.locator(".palette-item-meta");
await expect(name).toBeVisible();
await expect(name).toHaveText("AMESim 气动测试元件");
await expect(meta).toBeHidden();
await expect(longNameItem).not.toHaveAttribute("title");
const truncation = await name.evaluate((element) => {
const style = window.getComputedStyle(element);
return {
clipped: element.scrollWidth > element.clientWidth,
overflow: style.overflow,
textOverflow: style.textOverflow,
whiteSpace: style.whiteSpace,
};
});
expect(truncation).toEqual({
clipped: true,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
});
await expectFixedIconCardSize(longNameItem);
const initialPaletteBox = await palette.boundingBox();
expect(initialPaletteBox).not.toBeNull();
const paletteSeparator = page.getByRole("separator", {
name: "调整组件库宽度",
});
await paletteSeparator.focus();
for (let index = 0; index < 5; index += 1) {
await page.keyboard.press("ArrowRight");
}
await expect(paletteSeparator).toHaveAttribute("aria-valuenow", "330");
const expandedPaletteBox = await palette.boundingBox();
expect(expandedPaletteBox).not.toBeNull();
expect(expandedPaletteBox!.width).toBeGreaterThan(initialPaletteBox!.width + 90);
await expectFixedIconCardSize(longNameItem);
await page.setViewportSize({ width: 1000, height: 760 });
await expect
.poll(async () => Number(await paletteSeparator.getAttribute("aria-valuenow")))
.toBeLessThan(330);
await expectFixedIconCardSize(longNameItem);
});
test("仅图标模式悬停后显示完整名称和图标预览并在移开后关闭", async ({ page }) => {
await page.goto("/");
await page
.getByRole("button", { name: "仅图标显示", exact: true })
.click();
const item = page.locator("aside.palette").getByRole("button", {
name: "AMESim 气动测试元件",
exact: true,
});
const preview = page.locator("body > .palette-icon-preview[role='tooltip']");
await item.hover();
await page.waitForTimeout(600);
await expect(preview).toBeVisible();
await expect(preview.locator(".palette-icon-preview-label")).toHaveText(
"AMESim 气动测试元件",
);
await expect(
preview.locator(
'.palette-icon-preview-symbol [data-component-symbol="generic"] svg',
),
).toBeVisible();
await expect(item).toHaveAttribute("aria-describedby", "palette-icon-preview");
await page.locator(".palette-panel-title").hover();
await expect(preview).toHaveCount(0);
await expect(item).not.toHaveAttribute("aria-describedby", "palette-icon-preview");
});
test("仅图标模式仍可拖入氦气介质并在画布显示专用图标", async ({ page }) => {
await page.goto("/");
await page
.getByRole("button", { name: "仅图标显示", exact: true })
.click();
const palette = page.locator("aside.palette");
const heliumItem = palette.getByRole("button", {
name: "氦气介质定义",
exact: true,
});
const canvas = page.locator(".flow-canvas .react-flow__pane");
await heliumItem.dragTo(canvas, {
targetPosition: { x: 420, y: 220 },
});
const heliumNode = page.locator(
'.flow-canvas .react-flow__node[data-id="amesim_helium_medium_1"]',
);
await expect(heliumNode).toBeVisible();
await expect(
heliumNode.locator(".sim-node.symbol-presentation-bare"),
).toBeVisible();
const heliumCanvasSymbol = heliumNode.locator(
'.node-symbol [data-component-symbol="amesim_helium_medium"]',
);
await expect(heliumCanvasSymbol).toHaveAttribute(
"data-symbol-presentation",
"bare",
);
await expect(heliumCanvasSymbol.locator("svg")).toBeVisible();
await expect(
heliumCanvasSymbol.locator('[data-symbol-label="He"]'),
).toHaveText("He");
});