mcpField
P
prowler-test-ui

prowler-test-ui

github.com/prowler-cloud测试与质量15.1k64 次安装今天更新
代码仓库

关于 prowler-test-ui

prowler-test-ui 是面向 Prowler UI 端到端测试开发的技能,提供项目专用的 Playwright 约定。它强调测试、页面对象和 Markdown 文档保持同步,并在编写测试前通过 MCP 工具探索实际页面。

主要功能

  1. 01规定 UI 测试目录结构
  2. 02要求测试与文档同步
  3. 03编写测试前探索实际页面
  4. 04提供等待与导航验证规范
  5. 05定义测试标签及运行命令

适用场景

  1. 01为 Prowler 页面新增端到端测试
  2. 02修改测试时同步用例文档
  3. 03根据实际页面结构编写稳定测试

MCP 相关

说明了 Playwright MCP 工具的使用流程:编写测试前先导航、检查页面快照并与页面交互,以确认真实结构和选择器。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/prowler-cloud/prowler --skill prowler-test-ui

安装后在 Claude Code 中输入/prowler-test-ui确认已加载。

附带文件references/e2e-docs.md

详细介绍

通用模式:有关基础 Playwright 模式(页面对象模型、选择器、辅助函数),请参阅 playwright 技能。 此技能仅涵盖 Prowler 特定约定。

Prowler UI 测试结构

ui/tests/
├── base-page.ts              # Prowler-specific base page
├── helpers.ts                # Prowler test utilities
└── {page-name}/
    ├── {page-name}-page.ts   # Page Object Model
    ├── {page-name}.spec.ts   # ALL tests (single file per feature)
    └── {page-name}.md        # Test documentation (MANDATORY - sync with spec.ts)

强制检查清单(创建或修改测试)

⚠️ 完成任何 E2E 任务前,务必进行验证:

创建新测试时
  • {page-name}-page.ts - 已创建/更新页面对象
  • {page-name}.spec.ts - 已添加带正确标签(@TEST-ID)的测试
  • {page-name}.md - 已创建包含所有测试用例的文档
  • .md 中的测试 ID 与 .spec.ts 中的标签一致
修改现有测试时
  • 若出现以下情况,必须更新 {page-name}.md:
    • 添加/删除了测试用例
    • 测试流程(步骤)发生变化
    • 前置条件或预期结果发生变化
    • 标签或优先级发生变化
  • .md 与 .spec.ts 中的测试 ID 保持同步
快速验证
# Verify .md exists for each test folder
ls ui/tests/{feature}/{feature}.md

# Verify test IDs match
grep -o "@[A-Z]*-E2E-[0-9]*" ui/tests/{feature}/{feature}.spec.ts | sort -u
grep -o "\`[A-Z]*-E2E-[0-9]*\`" ui/tests/{feature}/{feature}.md | sort -u

重要

❌ 未更新对应的 .md 文件,E2E 更改就不算完成。


MCP 工作流 - 至关重要

⚠️ 强制要求:如果可以使用 Playwright MCP 工具,创建测试前务必先使用这些工具。

  1. 导航到目标页面
  2. 获取快照以查看实际 DOM 结构
  3. 与表单/元素交互以验证真实流程
  4. 记录快照中的实际选择器
  5. 然后再编写测试代码

原因:避免测试建立在假设之上。实际探索 = 稳定的测试。


等待策略(至关重要)

⚠️ 切勿使用 networkidle - 这会导致测试不稳定!

策略使用场景
❌ networkidle切勿使用 - 在轮询/WebSockets 场景下会导致测试不稳定
⚠️ load仅在绝对必要时使用
✅ expect(element).toBeVisible()首选 - 等待特定 UI 状态
✅ page.waitForURL()等待导航
✅ pageObject.verifyPageLoaded()最佳 - 封装验证逻辑

正确示例:

await homePage.verifyPageLoaded();
await expect(page).toHaveURL("/dashboard");
await expect(page.getByRole("heading", { name: "Overview" })).toBeVisible();

错误示例:

await page.waitForLoadState("networkidle"); // ❌ FLAKY
await page.waitForTimeout(2000);            // ❌ ARBITRARY WAIT

Prowler 基础页面

import { Page, Locator, expect } from "@playwright/test";

export class BasePage {
  constructor(protected page: Page) {}

  async goto(path: string): Promise<void> {
    await this.page.goto(path);
    // Child classes should override verifyPageLoaded() to wait for specific elements
  }

  // Override in child classes to wait for page-specific elements
  async verifyPageLoaded(): Promise<void> {
    await expect(this.page.locator("main")).toBeVisible();
  }

  // Prowler-specific: notification handling
  async waitForNotification(): Promise<Locator> {
    const notification = this.page.locator('[role="status"]');
    await notification.waitFor({ state: "visible" });
    return notification;
  }

  async verifyNotificationMessage(message: string): Promise<void> {
    const notification = await this.waitForNotification();
    await expect(notification).toContainText(message);
  }
}

页面导航验证模式

⚠️ URL 断言应放在页面对象中,而不是测试中!

验证重定向或页面导航时,应在目标页面对象中创建专用方法:

// ✅ GOOD - In SignInPage
async verifyOnSignInPage(): Promise<void> {
  await expect(this.page).toHaveURL(/\/sign-in/);
  await expect(this.pageTitle).toBeVisible();
}

// ✅ GOOD - In test
await homePage.goto();  // Try to access protected route
await signInPage.verifyOnSignInPage();  // Verify redirect

// ❌ BAD - Direct assertions in test
await homePage.goto();
await expect(page).toHaveURL(/\/sign-in/);  // Should be in Page Object
await expect(page.getByText("Sign in")).toBeVisible();

**命名约定:**重定向验证方法使用 verifyOn{PageName}Page()。


Prowler 特定页面

Providers 页面
import { BasePage } from "../base-page";

export class ProvidersPage extends BasePage {
  readonly addButton = this.page.getByRole("button", { name: "Add Provider" });
  readonly providerTable = this.page.getByRole("table");

  async goto(): Promise<void> {
    await super.goto("/providers");
  }

  async addProvider(type: string, alias: string): Promise<void> {
    await this.addButton.click();
    await this.page.getByLabel("Provider Type").selectOption(type);
    await this.page.getByLabel("Alias").fill(alias);
    await this.page.getByRole("button", { name: "Create" }).click();
  }
}
Scans 页面
export class ScansPage extends BasePage {
  readonly newScanButton = this.page.getByRole("button", { name: "New Scan" });
  readonly scanTable = this.page.getByRole("table");

  async goto(): Promise<void> {
    await super.goto("/scans");
  }

  async startScan(providerAlias: string): Promise<void> {
    await this.newScanButton.click();
    await this.page.getByRole("combobox", { name: "Provider" }).click();
    await this.page.getByRole("option", { name: providerAlias }).click();
    await this.page.getByRole("button", { name: "Start Scan" }).click();
  }
}

Prowler 测试标签

test("Provider CRUD operations",
  { tag: ["@critical", "@e2e", "@providers", "@PROV-E2E-001"] },
  async ({ page }) => {
    // ...
  }
);
类别标签
优先级@critical、@high、@medium、@low
类型@e2e、@smoke、@regression
功能@providers、@scans、@findings、@compliance、@signin、@signup
测试 ID@PROV-E2E-001、@SCAN-E2E-002

Prowler 测试文档模板

控制在 60 行以内。仅关注流程、前置条件和预期结果。

### E2E Tests: {Feature Name}

**Suite ID:** `{SUITE-ID}`
**Feature:** {Feature description}

---

## Test Case: `{TEST-ID}` - {Test case title}

**Priority:** `{critical|high|medium|low}`
**Tags:** @e2e, @{feature-name}

**Preconditions:**
- {Prerequisites}

### Flow Steps:
1. {Step}
2. {Step}

### Expected Result:
- {Outcome}

### Key Verification Points:
- {Assertion}

命令

cd ui && pnpm run test:e2e                              # All tests
cd ui && pnpm run test:e2e tests/providers/             # Specific folder
cd ui && pnpm run test:e2e --grep "provider"            # By pattern
cd ui && pnpm run test:e2e:ui                           # With UI
cd ui && pnpm run test:e2e:debug                        # Debug mode
cd ui && pnpm run test:e2e:headed                       # See browser
cd ui && pnpm run test:e2e:report                       # Generate report

资源

  • 文档:有关本地开发者指南的链接,请参阅 references/

评论

支持 Markdown