Agent Skills: Verify .md exists for each test folder

>

UncategorizedID: prowler-cloud/prowler/prowler-test-ui

Install this agent skill to your local

pnpm dlx add-skill https://github.com/prowler-cloud/prowler/tree/HEAD/skills/prowler-test-ui

Skill Files

Browse the full folder contents for prowler-test-ui.

Download Skill

Loading file tree…

skills/prowler-test-ui/SKILL.md

Skill Metadata

Name
prowler-test-ui
Description
>

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

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:

# 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

❌ 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:

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

BAD:

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

Prowler Base Page

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:

// ✅ 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

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

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

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.

### 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

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/ for links to local developer guide