dev_1.0.0_xuxt_3 完成web和alert的集成测试 #38
@ -54,7 +54,7 @@ done
|
||||
#=============================
|
||||
# Step 2: Run Playwright tests
|
||||
#=============================
|
||||
log_info "[2/4] Running Playwright automated tests..."
|
||||
log_info "[2/4] Running Playwright automated tests in headless mode..."
|
||||
|
||||
cd "$WEB_DIR"
|
||||
|
||||
@ -70,9 +70,9 @@ npx playwright install --with-deps > /dev/null
|
||||
# Clean previous reports
|
||||
rm -rf "$REPORT_DIR"
|
||||
|
||||
# Run Playwright tests with reporters
|
||||
set +e # temporarily disable exit-on-error to capture test result
|
||||
BASE_URL=${FRONTEND_URL} npx playwright test tests/playwright --reporter=list
|
||||
# Run Playwright tests wrapped with xvfb-run to avoid GUI
|
||||
set +e # temporarily disable exit-on-error
|
||||
env BASE_URL="$FRONTEND_URL" xvfb-run --auto-servernum npx playwright test tests/playwright --reporter=list
|
||||
TEST_RESULT=$?
|
||||
set -e # re-enable strict mode
|
||||
|
||||
@ -100,4 +100,4 @@ else
|
||||
log_warn "Report directory not found. Check Playwright execution logs."
|
||||
fi
|
||||
|
||||
log_success "Web frontend verify success. Playwright automated tests passed."
|
||||
log_success "Web frontend verify finished."
|
||||
|
||||
@ -10,7 +10,16 @@ export default defineConfig({
|
||||
viewport: { width: 1280, height: 720 },
|
||||
ignoreHTTPSErrors: true,
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'retain-on-failure'
|
||||
video: 'retain-on-failure',
|
||||
launchOptions: {
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-gpu',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-software-rasterizer',
|
||||
'--headless=new'
|
||||
],
|
||||
},
|
||||
},
|
||||
reporter: [
|
||||
['list'],
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {test, expect} from "@playwright/test";
|
||||
import { BASE_URL } from './helpers/utils'
|
||||
import {BASE_URL} from './helpers/utils'
|
||||
|
||||
test.describe("Alerts 页面功能测试", () => {
|
||||
test.beforeEach(async ({page}) => {
|
||||
@ -7,17 +7,17 @@ test.describe("Alerts 页面功能测试", () => {
|
||||
});
|
||||
|
||||
test("页面加载并显示告警统计", async ({page}) => {
|
||||
await expect(page.locator("text=告警详情")).toBeVisible();
|
||||
await expect(page.locator("text=总数")).toBeVisible();
|
||||
await expect(page.locator("text=严重")).toBeVisible();
|
||||
await expect(page.locator("text=警告")).toBeVisible();
|
||||
await expect(page.locator("text=信息")).toBeVisible();
|
||||
await expect(page.locator("text=告警详情").first()).toBeVisible();
|
||||
await expect(page.locator("text=总数").first()).toBeVisible();
|
||||
await expect(page.locator("text=严重").first()).toBeVisible();
|
||||
await expect(page.locator("text=警告").first()).toBeVisible();
|
||||
await expect(page.locator("text=信息").first()).toBeVisible();
|
||||
});
|
||||
|
||||
test("筛选功能验证", async ({page}) => {
|
||||
const severitySelect = page.getByLabel("严重性");
|
||||
const stateSelect = page.getByLabel("状态");
|
||||
const nodeSelect = page.getByLabel("节点");
|
||||
const severitySelect = page.getByRole('combobox', {name: '严重性'});
|
||||
const stateSelect = page.getByRole('combobox', {name: '状态'});
|
||||
const nodeSelect = page.getByRole('combobox', {name: '节点'});
|
||||
|
||||
await severitySelect.selectOption("critical");
|
||||
await expect(severitySelect).toHaveValue("critical");
|
||||
@ -30,18 +30,18 @@ test.describe("Alerts 页面功能测试", () => {
|
||||
});
|
||||
|
||||
test("排序功能", async ({page}) => {
|
||||
const severityHeader = page.locator("th:has-text('严重性') button");
|
||||
const severityHeader = page.locator("th:has-text('严重性') button").first();
|
||||
await severityHeader.click(); // 切换升序
|
||||
await severityHeader.click(); // 切换降序
|
||||
|
||||
const instanceHeader = page.locator("th:has-text('节点') button");
|
||||
const instanceHeader = page.locator("th:has-text('节点') button").first();
|
||||
await instanceHeader.click();
|
||||
await instanceHeader.click();
|
||||
});
|
||||
|
||||
test("分页功能", async ({page}) => {
|
||||
const nextButton = page.locator("button:has-text('下一页')");
|
||||
const prevButton = page.locator("button:has-text('上一页')");
|
||||
const nextButton = page.locator("button:has-text('下一页')").first();
|
||||
const prevButton = page.locator("button:has-text('上一页')").first();
|
||||
|
||||
if (await nextButton.isEnabled()) {
|
||||
await nextButton.click();
|
||||
@ -61,8 +61,8 @@ test.describe("Alerts 页面功能测试", () => {
|
||||
});
|
||||
|
||||
test("自动刷新开关与刷新按钮", async ({page}) => {
|
||||
const switchBtn = page.getByRole("switch", {name: "自动刷新"});
|
||||
const refreshBtn = page.getByTitle("刷新");
|
||||
const switchBtn = page.locator("div[role='switch']").first();
|
||||
const refreshBtn = page.getByTitle("刷新").first();
|
||||
|
||||
await expect(switchBtn).toBeVisible();
|
||||
await expect(refreshBtn).toBeVisible();
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { BASE_URL } from './helpers/utils'
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {BASE_URL} from './helpers/utils'
|
||||
|
||||
test.describe('Dashboard 页面测试', () => {
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.beforeEach(async ({page}) => {
|
||||
// 打开仪表盘页面
|
||||
await page.goto(`${BASE_URL}/dashboard`, { waitUntil: 'networkidle' });
|
||||
await page.goto(`${BASE_URL}/dashboard`, {waitUntil: 'networkidle'});
|
||||
});
|
||||
|
||||
test('应能成功加载页面并显示标题', async ({ page }) => {
|
||||
await expect(page.locator('text=仪表盘')).toBeVisible();
|
||||
test('应能成功加载页面并显示标题', async ({page}) => {
|
||||
await expect(page.locator('text=仪表盘').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示节点健康状态卡片', async ({ page }) => {
|
||||
test('应显示节点健康状态卡片', async ({page}) => {
|
||||
const healthCard = page.locator('text=节点健康状态');
|
||||
await expect(healthCard).toBeVisible();
|
||||
|
||||
@ -22,21 +22,21 @@ test.describe('Dashboard 页面测试', () => {
|
||||
expect(ringCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('应显示告警统计信息', async ({ page }) => {
|
||||
test('应显示告警统计信息', async ({page}) => {
|
||||
const alertCard = page.locator('text=告警统计');
|
||||
await expect(alertCard).toBeVisible();
|
||||
|
||||
// 检查告警类别
|
||||
const labels = ['总数', '严重', '警告', '信息'];
|
||||
for (const label of labels) {
|
||||
await expect(page.locator(`text=${label}`)).toBeVisible();
|
||||
await expect(page.locator(`text=${label}`).first()).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应正确渲染集群节点表格', async ({ page }) => {
|
||||
test('应正确渲染集群节点表格', async ({page}) => {
|
||||
const tableHeaders = ['ID', '名称', '状态', '类型', '版本'];
|
||||
for (const header of tableHeaders) {
|
||||
await expect(page.locator(`th:has-text("${header}")`)).toBeVisible();
|
||||
await expect(page.locator(`th:has-text("${header}")`).first()).toBeVisible();
|
||||
}
|
||||
|
||||
// 至少有一行节点数据
|
||||
@ -44,14 +44,7 @@ test.describe('Dashboard 页面测试', () => {
|
||||
expect(rows).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('“查看更多”链接应存在并指向 /nodeInfo', async ({ page }) => {
|
||||
const link = page.locator('a:has-text("查看更多")');
|
||||
await expect(link).toBeVisible();
|
||||
const href = await link.getAttribute('href');
|
||||
expect(href).toContain('/nodeInfo');
|
||||
});
|
||||
|
||||
test('页面应无加载错误提示', async ({ page }) => {
|
||||
test('页面应无加载错误提示', async ({page}) => {
|
||||
await expect(page.locator('text=加载中...')).toHaveCount(0);
|
||||
await expect(page.locator('text=数据加载失败')).toHaveCount(0);
|
||||
});
|
||||
|
||||
@ -1,21 +1,25 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
import type { metricsEntries } from '../../../src/config/entries';
|
||||
|
||||
export async function testEntryCards(page: Page, entries: typeof metricsEntries, checkLinkNavigation = false) {
|
||||
export async function testEntryCards(
|
||||
page: Page,
|
||||
entries: typeof metricsEntries,
|
||||
checkLinkNavigation = false
|
||||
) {
|
||||
for (const entry of entries) {
|
||||
// 卡片文本可见
|
||||
const card = page.locator(`text=${entry.label}`);
|
||||
await expect(card).toBeVisible();
|
||||
// 更具体选择器,直接定位 a 标签包含文本
|
||||
const link = page.locator(`a:has-text("${entry.label}")`);
|
||||
await expect(link).toBeVisible({ timeout: 10000 }); // 等待元素可见
|
||||
|
||||
// 卡片链接正确
|
||||
const link = card.locator('..').locator('a');
|
||||
// href 属性检查
|
||||
await expect(link).toHaveAttribute('href', entry.href);
|
||||
|
||||
// 图标存在
|
||||
const img = card.locator('..').locator('img');
|
||||
// 图标存在:寻找 a 下的 img
|
||||
const img = link.locator('img');
|
||||
await expect(img).toBeVisible();
|
||||
await expect(img).toHaveAttribute('src', /\/assets\/.+/);
|
||||
|
||||
// 可选:点击链接检查导航
|
||||
if (checkLinkNavigation) {
|
||||
const [newPage] = await Promise.all([
|
||||
page.context().waitForEvent('page'),
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { logsEntries } from './test-entries';
|
||||
import { testEntryCards } from './helpers/entrycards-helpers';
|
||||
import { BASE_URL } from './helpers/utils'
|
||||
import { BASE_URL } from './helpers/utils';
|
||||
|
||||
test.describe('Logs Page', () => {
|
||||
test('should render all log cards', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/logs`);
|
||||
await expect(page.locator('h2', { hasText: '日志详情' })).toBeVisible();
|
||||
|
||||
// 等待标题可见
|
||||
const title = page.locator('h2', { hasText: '日志详情' });
|
||||
await expect(title).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// 测试所有 log card
|
||||
await testEntryCards(page, logsEntries);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { metricsEntries } from './test-entries';
|
||||
import { testEntryCards } from './helpers/entrycards-helpers';
|
||||
import { BASE_URL } from './helpers/utils'
|
||||
|
||||
import { BASE_URL } from './helpers/utils';
|
||||
|
||||
test.describe('Metrics Page', () => {
|
||||
test('should render all metric cards', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/metrics`);
|
||||
await expect(page.locator('h2', { hasText: '指标详情' })).toBeVisible();
|
||||
|
||||
const title = page.locator('h2', { hasText: '指标详情' });
|
||||
await expect(title).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await testEntryCards(page, metricsEntries);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,73 +1,75 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { BASE_URL } from './helpers/utils'
|
||||
import {test, expect} from "@playwright/test";
|
||||
import {BASE_URL} from './helpers/utils'
|
||||
|
||||
test.describe("节点信息页面 NodeInfo", () => {
|
||||
// 每次测试前打开目标页面
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.beforeEach(async ({page}) => {
|
||||
await page.goto(`${BASE_URL}/node`);
|
||||
});
|
||||
|
||||
test("页面标题应该正确显示", async ({ page }) => {
|
||||
const title = page.getByRole("heading", { name: "节点信息" });
|
||||
test("页面标题应该正确显示", async ({page}) => {
|
||||
const title = page.locator('h1,h2,h3:has-text("节点信息")').first();
|
||||
await title.waitFor({timeout: 10000});
|
||||
await expect(title).toBeVisible();
|
||||
});
|
||||
|
||||
test("节点表格应该加载数据", async ({ page }) => {
|
||||
test("节点表格应该加载数据", async ({page}) => {
|
||||
const rows = page.locator("table tbody tr");
|
||||
await rows.first().waitFor({timeout: 10000});
|
||||
const count = await rows.count();
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('节点详情测试', async ({ page }) => {
|
||||
// 点击第一个节点的“查看详情”
|
||||
test('节点详情测试', async ({page}) => {
|
||||
const firstDetailBtn = page.locator('text=查看详情').first();
|
||||
await firstDetailBtn.click();
|
||||
await firstDetailBtn.waitFor({timeout: 10000});
|
||||
await firstDetailBtn.scrollIntoViewIfNeeded();
|
||||
await firstDetailBtn.click({force: true});
|
||||
|
||||
const drawer = page.locator('role=dialog[name="节点详情"]');
|
||||
await drawer.waitFor({timeout: 10000});
|
||||
await expect(drawer).toBeVisible();
|
||||
|
||||
// ========================
|
||||
// 1️⃣ 验证基础信息
|
||||
// ========================
|
||||
await expect(drawer.locator('text=注册时间')).toBeVisible();
|
||||
await expect(drawer.locator('text=最近上报时间')).toBeVisible();
|
||||
await expect(drawer.locator('text=最后更新时间')).toBeVisible();
|
||||
for (const label of ['注册时间', '最近上报时间', '最后更新时间']) {
|
||||
const el = drawer.locator(`text=${label}`).first();
|
||||
await el.waitFor({timeout: 5000});
|
||||
await expect(el).toBeVisible();
|
||||
}
|
||||
|
||||
// ========================
|
||||
// 2️⃣ NodeConfigCard 编辑/保存
|
||||
// ========================
|
||||
const configEditBtn = drawer.locator('div:has-text("配置信息") >> role=button', { hasText: '' });
|
||||
await configEditBtn.click(); // 开启编辑
|
||||
const configEditBtn = drawer.locator('div:has-text("配置信息") >> role=button').first();
|
||||
await configEditBtn.scrollIntoViewIfNeeded();
|
||||
await configEditBtn.click({force: true});
|
||||
|
||||
const keyInput = drawer.locator('input[placeholder="Key"]').first();
|
||||
const valueInput = drawer.locator('input[placeholder="Value"]').first();
|
||||
|
||||
await keyInput.fill('testKey');
|
||||
await valueInput.fill('testValue');
|
||||
|
||||
const saveBtn = drawer.locator('div:has-text("配置信息") >> role=button', { hasText: '' }).filter({ hasText: '' }).first();
|
||||
const saveBtn = drawer.locator('div:has-text("配置信息") >> role=button').first();
|
||||
await saveBtn.click();
|
||||
|
||||
// 保存后,新配置应该显示在列表中
|
||||
await expect(drawer.locator('text=testKey')).toBeVisible();
|
||||
await expect(drawer.locator('text=testValue')).toBeVisible();
|
||||
await expect(drawer.locator('text=testKey').first()).toBeVisible();
|
||||
await expect(drawer.locator('text=testValue').first()).toBeVisible();
|
||||
|
||||
// ========================
|
||||
// 3️⃣ NodeLabelCard 标签管理
|
||||
// ========================
|
||||
const labelEditBtn = drawer.locator('div:has-text("标签信息") >> role=button', { hasText: '' });
|
||||
await labelEditBtn.click(); // 开启编辑
|
||||
const labelEditBtn = drawer.locator('div:has-text("标签信息") >> role=button').first();
|
||||
await labelEditBtn.click({force: true});
|
||||
|
||||
const newTagInput = drawer.locator('input[placeholder="新增标签"]');
|
||||
await newTagInput.fill('newTag1');
|
||||
|
||||
const addTagBtn = drawer.locator('div:has-text("标签信息") >> role=button', { hasText: '' }).nth(1);
|
||||
await addTagBtn.click();
|
||||
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', { hasText: '' }).first();
|
||||
await saveLabelBtn.click();
|
||||
await expect(drawer.locator('text=newTag1')).toBeVisible();
|
||||
const saveLabelBtn = drawer.locator('div:has-text("标签信息") >> role=button').first();
|
||||
await saveLabelBtn.click({force: true});
|
||||
await expect(drawer.locator('text=newTag1').first()).toBeVisible();
|
||||
|
||||
// ========================
|
||||
// 4️⃣ NodeHealthCard 健康信息展示
|
||||
@ -75,23 +77,24 @@ test.describe("节点信息页面 NodeInfo", () => {
|
||||
const healthModule = drawer.locator('div:has-text("健康信息") >> text=healthy').first();
|
||||
await expect(healthModule).toBeVisible();
|
||||
|
||||
// 可选择打开 Info Popover
|
||||
const infoBtn = drawer.locator('div:has-text("健康信息") >> role=button').first();
|
||||
await infoBtn.click();
|
||||
await infoBtn.click({force: true});
|
||||
const errorCount = await drawer.locator('text=Error').count();
|
||||
expect(errorCount).toBeGreaterThan(0);
|
||||
|
||||
// ========================
|
||||
// 5️⃣ Drawer 关闭
|
||||
// ========================
|
||||
const closeBtn = drawer.locator('button[aria-label="Close"]');
|
||||
await closeBtn.click();
|
||||
await expect(drawer).toHaveCount(0);
|
||||
const closeBtn = drawer.locator('button[aria-label="Close"]').first();
|
||||
await closeBtn.scrollIntoViewIfNeeded();
|
||||
await closeBtn.click({force: true});
|
||||
await expect(drawer).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test("Grafana按钮链接应正确", async ({ page }) => {
|
||||
const grafanaLink = page.getByRole("link", { name: "Grafana" }).first();
|
||||
test("Grafana按钮链接应正确", async ({page}) => {
|
||||
await page.goto(`${BASE_URL}/node`);
|
||||
const grafanaLink = page.getByRole("link", {name: "Grafana"}).first();
|
||||
await grafanaLink.waitFor({timeout: 10000});
|
||||
await expect(grafanaLink).toHaveAttribute("href", /\/d\/node_gpu_metrics/);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user