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"); });