import { expect, test } from '@playwright/test' /** * DCS 编辑器 - 图层操作测试 * * 测试图层的添加、选择、删除等操作。 * 部分测试(如拖拽添加组件)因 Playwright 对 HTML5 drag-and-drop 的限制 * 而标记为 skip,仅验证可测试的交互路径。 */ /** 辅助函数:创建画布并进入编辑器 */ async function enterEditor(page: import('@playwright/test').Page, canvasName = '图层测试画布') { await page.goto('/') await page.waitForLoadState('networkidle') await page.locator('.add-card').click() const dialog = page.locator('.el-dialog') await expect(dialog).toBeVisible({ timeout: 5000 }) await dialog.locator('input').first().fill(canvasName) await dialog.locator('button').filter({ hasText: '创建' }).click() await expect(page.locator('.dcs-editor')).toBeVisible({ timeout: 10000 }) } /** 辅助函数:获取图层面板中的图层行数量 */ async function getLayerRowCount(page: import('@playwright/test').Page): Promise { return page.locator('.layer-list .layer-row').count() } test.describe('图层管理', () => { test.skip('通过拖拽添加组件到画布', async ({ page }) => { // 跳过:HTML5 drag-and-drop 在 Playwright 中较难模拟, // 且应用在 Tauri 环境还使用了 pointer 方案替代。 await enterEditor(page, '拖拽添加测试') // 切换到组件面板 await page.locator('.sidebar-tab-item').filter({ hasText: '组件' }).click() await expect(page.locator('.components-tab')).toBeVisible() // 获取矩形组件卡片和画布舞台区域 const rectCard = page.locator('.component-card').first() const canvas = page.locator('.canvas-wrapper') // 尝试拖拽(此操作可能不可靠) await rectCard.dragTo(canvas, { targetPosition: { x: 300, y: 300 }, }) // 验证图层面板中出现新图层 await page.locator('.sidebar-tab-item').filter({ hasText: '图层' }).click() const layerCount = await getLayerRowCount(page) expect(layerCount).toBeGreaterThan(0) }) test('初始状态下图层面板为空', async ({ page }) => { await enterEditor(page, '空图层面板测试') // 确认图层面板可见 await expect(page.locator('.layers-tab')).toBeVisible() // 新画布应该没有图层 const layerCount = await getLayerRowCount(page) expect(layerCount).toBe(0) }) test('属性面板初始显示空状态或底图信息', async ({ page }) => { await enterEditor(page, '属性面板初始测试') // 属性面板头部应该可见且显示"属性"标题 const propertyTitle = page.locator('.property-panel .title') await expect(propertyTitle).toContainText('属性') // 没有选中图层时,应显示提示文字 const summaryOrEmpty = page.locator('.property-panel .selection-summary, .property-panel .property-empty') await expect(summaryOrEmpty.first()).toBeVisible() }) test('属性面板有停靠/浮动切换按钮', async ({ page }) => { await enterEditor(page, '停靠切换测试') // 停靠/浮动切换按钮应存在 const dockBtn = page.locator('.dock-toggle-btn') await expect(dockBtn).toBeVisible() // 点击切换到浮动模式 await dockBtn.click() await expect(page.locator('.property-panel-shell--floating')).toBeVisible() // 再次点击切换回停靠模式 await page.locator('.dock-toggle-btn').click() await expect(page.locator('.property-panel-shell:not(.property-panel-shell--floating)')).toBeVisible() }) test('图层面板显示页面列表', async ({ page }) => { await enterEditor(page, '页面列表验证测试') // 页面树区域应显示至少一个页面 const pageRows = page.locator('.page-tree .page-row') await expect(pageRows.first()).toBeVisible() // 第一个页面应处于激活状态 await expect(page.locator('.page-row.active')).toBeVisible() }) test('图层面板有添加页面按钮', async ({ page }) => { await enterEditor(page, '添加页面按钮测试') // 页面头部的添加按钮应存在(fa-plus 图标) const addPageBtn = page.locator('.page-header').locator('button').filter({ has: page.locator('.fa-plus'), }) await expect(addPageBtn).toBeVisible() }) test('图层面板有搜索框', async ({ page }) => { await enterEditor(page, '图层搜索测试') // 搜索行应包含输入框 const searchInput = page.locator('.layer-search-row input') await expect(searchInput).toBeVisible() }) test('状态栏无选中时不显示选中计数', async ({ page }) => { await enterEditor(page, '状态栏选中计数测试') // 没有选中图层时,状态栏右侧不应显示"已选中" await expect(page.locator('.editor-status-bar .status-right')).not.toContainText('已选中') }) test('缩放信息显示在状态栏中', async ({ page }) => { await enterEditor(page, '状态栏缩放测试') // 状态栏应显示缩放百分比(如"100%") const statusItems = page.locator('.editor-status-bar .status-item') let foundZoom = false const count = await statusItems.count() for (let i = 0; i < count; i++) { const text = await statusItems.nth(i).textContent() if (text && text.includes('%')) { foundZoom = true break } } expect(foundZoom).toBe(true) }) test('画布舞台有标尺', async ({ page }) => { await enterEditor(page, '标尺测试') // 水平标尺 await expect(page.locator('.ruler-top')).toBeVisible() // 垂直标尺 await expect(page.locator('.ruler-left')).toBeVisible() // 标尺角落 await expect(page.locator('.ruler-corner')).toBeVisible() }) test.skip('选中图层后属性面板显示属性', async ({ page }) => { // 跳过:需要先有图层才能选中,添加图层依赖拖拽操作 await enterEditor(page, '选中属性测试') // 预期:选中图层后,属性面板标题下方应显示图层信息 const propertyBody = page.locator('.property-panel .property-body') await expect(propertyBody).toBeVisible() }) test.skip('可以删除选中的图层', async ({ page }) => { // 跳过:需要先有可选中的图层(添加图层依赖拖拽操作) await enterEditor(page, '删除图层测试') // 预期:选中图层后按 Delete/Backspace 可删除 // 或通过右键菜单删除 }) test.skip('删除图层后可撤销', async ({ page }) => { // 跳过:依赖先添加并删除图层 await enterEditor(page, '撤销删除测试') // 预期:删除图层后,撤销按钮变为可用,点击撤销可恢复图层 }) test('图层面板页面列表可以折叠和展开', async ({ page }) => { await enterEditor(page, '页面折叠测试') // 页面树应该初始可见 await expect(page.locator('.page-tree')).toBeVisible() // 点击折叠/展开按钮(page-header 中的角标按钮) const toggleBtn = page.locator('.page-header .header-actions button').filter({ has: page.locator('.fa-angle-up, .fa-angle-down'), }) await toggleBtn.click() // 页面树应该被隐藏(v-show 控制,元素仍在 DOM 中但不可见) await expect(page.locator('.page-tree')).toBeHidden() // 再次点击展开 await toggleBtn.click() await expect(page.locator('.page-tree')).toBeVisible() }) test('画布舞台 overlay 层存在', async ({ page }) => { await enterEditor(page, 'Overlay 测试') // overlay 是放置图层选择框等交互元素的层 await expect(page.locator('.canvas-wrapper .overlay')).toBeAttached() }) test('组件面板中的组件卡片显示描述信息', async ({ page }) => { await enterEditor(page, '组件描述测试') // 切换到组件面板 await page.locator('.sidebar-tab-item').filter({ hasText: '组件' }).click() await expect(page.locator('.components-tab')).toBeVisible() // 每个组件卡片应该有标题和描述 const firstCard = page.locator('.component-card').first() await expect(firstCard.locator('.card-title')).toBeVisible() await expect(firstCard.locator('.card-desc')).toBeVisible() // 验证具体描述内容(矩形:基础矩形图层) const rectDesc = page.locator('.component-card').filter({ hasText: '矩形' }).locator('.card-desc') await expect(rectDesc).toContainText('基础矩形图层') }) })