diff --git a/src/web/tests/playwright/alerts.spec.ts b/src/web/tests/playwright/alerts.spec.ts index 5182d54..e03d486 100644 --- a/src/web/tests/playwright/alerts.spec.ts +++ b/src/web/tests/playwright/alerts.spec.ts @@ -41,7 +41,7 @@ test.describe("Alerts 页面功能测试", () => { await activeOption.waitFor({ state: 'visible', timeout: 5000 }); await activeOption.click(); - await expect(stateSelect).toHaveValue("active"); + await expect(stateSelect).toHaveValue("Active"); // ========================== // 3️⃣ 选择“节点”下拉框(示例) @@ -99,7 +99,7 @@ test.describe("Alerts 页面功能测试", () => { await page.waitForSelector("table"); // 找到开关和刷新按钮 - const switchBtn = page.locator('input[role="switch"]').first(); + const switchBtn = page.getByRole('switch', { name: '自动刷新' }); const refreshBtn = page.getByTitle("刷新").first(); // 确保二者都可见 @@ -121,7 +121,7 @@ test.describe("Alerts 页面功能测试", () => { // 2 测试自动刷新开关 // ================================ const beforeState = await switchBtn.isChecked(); - await switchBtn.click(); // 切换状态 + await switchBtn.click({ force: true }); // 验证状态确实切换了 const afterState = await switchBtn.isChecked(); diff --git a/src/web/tests/playwright/helpers/entrycards-helpers.ts b/src/web/tests/playwright/helpers/entrycards-helpers.ts index 4a513de..943ff32 100644 --- a/src/web/tests/playwright/helpers/entrycards-helpers.ts +++ b/src/web/tests/playwright/helpers/entrycards-helpers.ts @@ -18,7 +18,7 @@ export async function testEntryCards( // 检查图标 const img = card.locator('img'); await expect(img).toBeVisible(); - await expect(img).toHaveAttribute('src', /\/assets\/.+/); + await expect(img).toHaveAttribute('src', /(\/assets\/.+|data:image\/png;base64,)/); } } diff --git a/src/web/tests/playwright/node-info.spec.ts b/src/web/tests/playwright/node-info.spec.ts index 1a5004e..d8b9efe 100644 --- a/src/web/tests/playwright/node-info.spec.ts +++ b/src/web/tests/playwright/node-info.spec.ts @@ -30,7 +30,7 @@ test.describe("节点信息页面 NodeInfo", () => { await expect(drawer).toBeVisible(); // ======================== - // 1️⃣ 验证基础信息 + // 1 验证基础信息 // ======================== for (const label of ['注册时间', '最近上报时间', '最后更新时间']) { const el = drawer.locator(`text=${label}`).first(); @@ -39,51 +39,94 @@ test.describe("节点信息页面 NodeInfo", () => { } // ======================== - // 2️⃣ NodeConfigCard 编辑/保存 + // 2 NodeConfigCard 编辑/保存 // ======================== const configEditBtn = drawer.locator('div:has-text("配置信息") >> role=button').first(); await configEditBtn.scrollIntoViewIfNeeded(); await configEditBtn.click({force: true}); + // 等待 input 渲染完成 const keyInput = drawer.locator('input[placeholder="Key"]').first(); const valueInput = drawer.locator('input[placeholder="Value"]').first(); - await keyInput.fill('testKey'); - await valueInput.fill('testValue'); + await expect(keyInput).toBeVisible({timeout: 10000}); + await expect(valueInput).toBeVisible({timeout: 10000}); + // 新增 key/value + const testKey = 'testKey'; + const testValue = 'testValue'; + await keyInput.fill(testKey); + await valueInput.fill(testValue); const saveBtn = drawer.locator('div:has-text("配置信息") >> role=button').first(); await saveBtn.click(); - await expect(drawer.locator('text=testKey').first()).toBeVisible(); - await expect(drawer.locator('text=testValue').first()).toBeVisible(); + + // 验证新增成功 + await expect(drawer.locator(`text=${testKey}`).first()).toBeVisible({timeout: 5000}); + await expect(drawer.locator(`text=${testValue}`).first()).toBeVisible({timeout: 5000}); + + + // 再次编辑,删除刚才新增的 key/value + await configEditBtn.click({force: true}); + + // 精准找到新增的 key 所在行 + const newRow = drawer.locator(`input[value="${testKey}"]`).first() + .locator('xpath=ancestor::div[contains(@class,"mantine-Group-root")]'); + + await expect(newRow).toBeVisible({timeout: 5000}); + + // 点击删除按钮删除该行 + const deleteBtn = newRow.locator('role=button', {hasText: '删除'}); // 如果实际是 IconX 或其他标识,请替换 + await deleteBtn.click({force: true}); + + // 保存修改 + await saveBtn.click(); + + // 验证刚新增的 key/value 已被删除 + await expect(drawer.locator(`text=${testKey}`)).toHaveCount(0); + await expect(drawer.locator(`text=${testValue}`)).toHaveCount(0); + // ======================== - // 3️⃣ NodeLabelCard 标签管理 + // 3 NodeLabelCard 标签管理 // ======================== const labelEditBtn = drawer.locator('div:has-text("标签信息") >> role=button').first(); await labelEditBtn.click({force: true}); + const newTagName = 'newTag1'; + + // 新增标签 const newTagInput = drawer.locator('input[placeholder="新增标签"]'); - await newTagInput.fill('newTag1'); + await newTagInput.fill(newTagName); const addTagBtn = drawer.locator('div:has-text("标签信息") >> role=button').nth(1); await addTagBtn.click({force: true}); - + // 保存新增标签 const saveLabelBtn = drawer.locator('div:has-text("标签信息") >> role=button').first(); await saveLabelBtn.click({force: true}); - await expect(drawer.locator('text=newTag1').first()).toBeVisible(); + + // 验证新增成功 + await expect(drawer.locator(`text=${newTagName}`).first()).toBeVisible({timeout: 5000}); + + // 再次编辑,删除刚才新增的标签 + await labelEditBtn.click({force: true}); + + // 精准定位刚新增的标签所在行 + const newTagRow = drawer.locator(`input[value="${newTagName}"]`).first() + .locator('xpath=ancestor::div[contains(@class,"mantine-Group-root")]'); + + await expect(newTagRow).toBeVisible({timeout: 5000}); + + // 点击删除按钮删除该标签 + const deleteTagBtn = newTagRow.locator('role=button', {hasText: '删除'}); // 如果实际是 IconTrash,可改为 .locator('svg[data-icon="trash"]') + await deleteTagBtn.click({force: true}); + + // 保存修改 + await saveLabelBtn.click(); + + // 验证刚新增的标签已被删除 + await expect(drawer.locator(`text=${newTagName}`)).toHaveCount(0); // ======================== - // 4️⃣ NodeHealthCard 健康信息展示 - // ======================== - const healthModule = drawer.locator('div:has-text("健康信息") >> text=healthy').first(); - await expect(healthModule).toBeVisible(); - - const infoBtn = drawer.locator('div:has-text("健康信息") >> role=button').first(); - await infoBtn.click({force: true}); - const errorCount = await drawer.locator('text=Error').count(); - expect(errorCount).toBeGreaterThan(0); - - // ======================== - // 5️⃣ Drawer 关闭 + // 4 Drawer 关闭 // ======================== const closeBtn = drawer.locator('button[aria-label="Close"]').first(); await closeBtn.scrollIntoViewIfNeeded();