prowler-test-ui

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).

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "prowler-test-ui" with this command: npx skills add prowler-cloud/prowler/prowler-cloud-prowler-prowler-test-ui

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!

StrategyUse Case
networkidleNEVER - flaky with polling/WebSockets
⚠️ loadOnly 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 }) => {
    // ...
  }
);
CategoryTags
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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

zod-4

No summary provided by upstream source.

Repository SourceNeeds Review
General

react-19

No summary provided by upstream source.

Repository SourceNeeds Review
General

tailwind-4

No summary provided by upstream source.

Repository SourceNeeds Review