Security

prowler-test-ui - Claude MCP Skill

E2E testing patterns for Prowler UI (Playwright). Trigger: When writing Playwright E2E tests under ui/tests in the Prowler UI (Prowler-specific base page/helpers, tags, flows).

SEO Guide: Enhance your AI agent with the prowler-test-ui tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to e2e testing patterns for prowler ui (playwright). trigger: when writing playwright e2e tests under u... Download and configure this skill to unlock new capabilities for your AI workflow.

🌟378 stars • 2376 forks
šŸ“„0 downloads

Documentation

SKILL.md
> **Generic Patterns**: For base Playwright patterns (Page Object Model, selectors, helpers), see the `playwright` skill.
> This skill covers **Prowler-specific** conventions only.

## Prowler UI Test Structure

```text
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)
```

---

## MANDATORY Checklist (Create or Modify Tests)

**āš ļø ALWAYS verify BEFORE completing any E2E task:**

### When CREATING new tests
- [ ] `{page-name}-page.ts` - Page Object created/updated
- [ ] `{page-name}.spec.ts` - Tests added with correct tags (@TEST-ID)
- [ ] `{page-name}.md` - Documentation created with ALL test cases
- [ ] Test IDs in `.md` match tags in `.spec.ts`

### When MODIFYING existing tests
- [ ] `{page-name}.md` MUST be updated if:
  - Test cases were added/removed
  - Test flow changed (steps)
  - Preconditions or expected results changed
  - Tags or priorities changed
- [ ] Test IDs synchronized between `.md` and `.spec.ts`

### Quick validation
```bash
# 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
```

> [!IMPORTANT]
> āŒ An E2E change is NOT considered complete without updating the corresponding `.md` file.

---

## MCP Workflow - CRITICAL

**āš ļø MANDATORY: If Playwright MCP tools are available, ALWAYS use them BEFORE creating tests.**

1. **Navigate** to target page
2. **Take snapshot** to see actual DOM structure
3. **Interact** with forms/elements to verify real flow
4. **Document actual selectors** from snapshots
5. **Only then** write test code

**Why**: Prevents tests based on assumptions. Real exploration = stable tests.

---

## Wait Strategies (CRITICAL)

**āš ļø NEVER use `networkidle` - it causes flaky tests!**

| Strategy | Use Case |
|----------|----------|
| āŒ `networkidle` | NEVER - flaky with polling/WebSockets |
| āš ļø `load` | Only when absolutely necessary |
| āœ… `expect(element).toBeVisible()` | PREFERRED - wait for specific UI state |
| āœ… `page.waitForURL()` | Wait for navigation |
| āœ… `pageObject.verifyPageLoaded()` | BEST - encapsulated verification |

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

**BAD:**
```typescript
await page.waitForLoadState("networkidle"); // āŒ FLAKY
await page.waitForTimeout(2000);            // āŒ ARBITRARY WAIT
```

---

## Prowler Base Page

```typescript
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);
  }
}
```

---

## Page Navigation Verification Pattern

**āš ļø URL assertions belong in Page Objects, NOT in tests!**

When verifying redirects or page navigation, create dedicated methods in the target Page Object:

```typescript
// āœ… 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();
```

**Naming convention:** `verifyOn{PageName}Page()` for redirect verification methods.

---

## Prowler-Specific Pages

### Providers Page

```typescript
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 Page

```typescript
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();
  }
}
```

---

## Test Tags for Prowler

```typescript
test("Provider CRUD operations",
  { tag: ["@critical", "@e2e", "@providers", "@PROV-E2E-001"] },
  async ({ page }) => {
    // ...
  }
);
```

| Category | Tags |
|----------|------|
| Priority | `@critical`, `@high`, `@medium`, `@low` |
| Type | `@e2e`, `@smoke`, `@regression` |
| Feature | `@providers`, `@scans`, `@findings`, `@compliance`, `@signin`, `@signup` |
| Test ID | `@PROV-E2E-001`, `@SCAN-E2E-002` |

---

## Prowler Test Documentation Template

**Keep under 60 lines. Focus on flow, preconditions, expected results only.**

```markdown
### 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}
```

---

## Commands

```bash
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
```

## Resources

- **Documentation**: See [references/](references/) for links to local developer guide

Signals

Avg rating⭐ 0.0
Reviews0
Favorites0

Information

Repository
prowler-cloud/prowler
Author
prowler-cloud
Last Sync
9/5/2026
Repo Updated
9/4/2026
Created
1/12/2026

Reviews (0)

No reviews yet. Be the first to review this skill!

Related Skills

firecrawl-build-search

Integrate Firecrawl `/search` into product code and agent workflows. Use when an app needs discovery before extraction, when the feature starts with a query instead of a URL, or when the system should search the web and optionally hydrate result content.

⭐ 35092

firecrawl-build-onboarding

Get Firecrawl credentials and SDK setup into a project. Use when an application needs `FIRECRAWL_API_KEY`, when an agent should add Firecrawl to `.env`, when the user wants to authenticate Firecrawl for app code, or when choosing the first SDK and docs for a new Firecrawl integration. This skill includes its own browser auth flow, so it does not depend on the website onboarding skill.

⭐ 35092

firecrawl-build

Integrate Firecrawl into application code whenever a product, agent, or workflow needs web data inside the app — web search, live search results, page scraping, structured extraction, or browser interaction. Use when building any feature that needs data from the web in code, even if the user does not mention Firecrawl explicitly and only describes wanting web data, website content, search, scraping, or interaction in an application. Trigger for Firecrawl requests, "fire girl" shorthand, and generic app-level web-data needs that should map to `/scrape`, `/search`, or `/interact`. Do not use this skill for one-off terminal-only web tasks during the current session; use `firecrawl/cli` for those.

⭐ 35092

firecrawl-build-interact

Integrate Firecrawl `/interact` into product code for dynamic pages and browser actions after scraping. Use when a feature needs clicks, form fills, pagination, authentication-aware flows, or other multi-step interactions that plain `/scrape` cannot complete.

⭐ 35092

Related Guides