import { expect, test } from "@playwright/test"; import { prepareApp } from "./fixtures"; test.beforeEach(async ({ page }) => { await prepareApp(page); }); test("未知图形元件使用带端口和名称的默认节点,并将动作结果写入控制台", async ({ page, }) => { await page.goto("/"); const paletteItem = page.getByRole("button", { name: /通用测试元件/ }); await expect(paletteItem).toBeVisible(); await paletteItem.dragTo(page.locator(".flow-canvas .react-flow__pane")); const node = page.locator('.flow-canvas .react-flow__node[data-id="generic_sensor_1"]'); await expect(node).toBeVisible(); await expect(node.locator(".sim-node")).toHaveClass(/generic-component/); await expect(node.locator(".node-symbol-anchor")).toHaveCount(0); await expect(node.locator(".node-label")).toHaveText("generic_sensor_1"); await expect(node.locator(".port-handle")).toHaveCount(2); await expect( node.locator('.port-index-label[data-port-name="port_a"]'), ).toHaveText("1"); await expect( node.locator('.port-index-label[data-port-name="port_b"]'), ).toHaveText("2"); const consolePanel = page.getByRole("complementary", { name: "仿真控制台", exact: true, }); await expect(consolePanel).not.toHaveClass(/has-unread/); await expect(consolePanel.locator(".simulation-console-unread")).toHaveCount(0); await expect(consolePanel).not.toContainText("已添加组件"); await page.getByRole("button", { name: "生成系统 XML" }).click(); await expect(consolePanel).toContainText("XML 已生成"); const xmlBlock = consolePanel.getByLabel("生成的系统 XML"); await expect(xmlBlock).toBeVisible(); await expect(xmlBlock).toContainText(''); await expect(xmlBlock).toContainText(' { await page.goto("/"); const paletteItem = page.getByRole("button", { name: /通用测试元件/ }); const pane = page.locator(".flow-canvas .react-flow__pane"); await paletteItem.dragTo(pane, { targetPosition: { x: 220, y: 220 } }); const firstNode = page.locator( '.flow-canvas .react-flow__node[data-id="generic_sensor_1"]', ); await expect(firstNode).toHaveClass(/selected/); await expect(firstNode.locator(".sim-node")).toHaveCSS( "outline-style", "dashed", ); await paletteItem.dragTo(pane, { targetPosition: { x: 480, y: 340 } }); const secondNode = page.locator( '.flow-canvas .react-flow__node[data-id="generic_sensor_2"]', ); await expect(firstNode).not.toHaveClass(/selected/); await expect(secondNode).toHaveClass(/selected/); await expect(firstNode.locator(".sim-node")).toHaveCSS("outline-style", "none"); await expect(secondNode.locator(".sim-node")).toHaveCSS( "outline-style", "dashed", ); await page .getByRole("button", { name: "顺时针旋转 90° (Ctrl+R)", exact: true }) .click(); await expect(firstNode.locator(".sim-node")).not.toHaveClass( /orientation-vertical/, ); await expect(secondNode.locator(".sim-node")).toHaveClass( /orientation-vertical/, ); }); test("框选结束后隐藏整体包围框并保留每个组件的虚线选中框", async ({ page, }) => { await page.goto("/"); const paletteItem = page.getByRole("button", { name: /通用测试元件/ }); const pane = page.locator(".flow-canvas .react-flow__pane"); await paletteItem.dragTo(pane, { targetPosition: { x: 220, y: 220 } }); await paletteItem.dragTo(pane, { targetPosition: { x: 480, y: 340 } }); const firstNode = page.locator( '.flow-canvas .react-flow__node[data-id="generic_sensor_1"]', ); const secondNode = page.locator( '.flow-canvas .react-flow__node[data-id="generic_sensor_2"]', ); const [paneBounds, firstBounds, secondBounds] = await Promise.all([ pane.boundingBox(), firstNode.boundingBox(), secondNode.boundingBox(), ]); expect(paneBounds).not.toBeNull(); expect(firstBounds).not.toBeNull(); expect(secondBounds).not.toBeNull(); const startX = Math.max( paneBounds!.x + 4, Math.min(firstBounds!.x, secondBounds!.x) - 14, ); const startY = Math.max( paneBounds!.y + 4, Math.min(firstBounds!.y, secondBounds!.y) - 14, ); const endX = Math.min( paneBounds!.x + paneBounds!.width - 4, Math.max( firstBounds!.x + firstBounds!.width, secondBounds!.x + secondBounds!.width, ) + 14, ); const endY = Math.min( paneBounds!.y + paneBounds!.height - 4, Math.max( firstBounds!.y + firstBounds!.height, secondBounds!.y + secondBounds!.height, ) + 14, ); await page.mouse.move(startX, startY); await page.mouse.down(); await page.mouse.move(endX, endY, { steps: 12 }); await page.mouse.up(); await expect(firstNode).toHaveClass(/selected/); await expect(secondNode).toHaveClass(/selected/); await expect(firstNode.locator(".sim-node")).toHaveCSS( "outline-style", "dashed", ); await expect(secondNode.locator(".sim-node")).toHaveCSS( "outline-style", "dashed", ); await expect(page.locator(".flow-canvas .react-flow__selection")).toHaveCount(0); const aggregateSelection = page.locator( ".flow-canvas .react-flow__nodesselection-rect", ); await expect(aggregateSelection).toHaveCount(1); await expect(aggregateSelection).toHaveCSS("border-top-width", "0px"); await expect(aggregateSelection).toHaveCSS("pointer-events", "none"); });