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.
Navigate to target page Take snapshot to see actual DOM structure Interact with forms/elements to verify real flow Document actual selectors from snapshots 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
// Override in child classes to wait for page-specific elements
async verifyPageLoaded(): Promise
// Prowler-specific: notification handling
async waitForNotification(): Promise
async verifyNotificationMessage(message: string): Promise
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
// ✅ 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
async addProvider(type: string, alias: string): Promise
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
async startScan(providerAlias: string): Promise
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:
Suite ID: {SUITE-ID}
Feature: {Feature description}
Test Case: {TEST-ID} -
Priority: {critical|high|medium|low}
Tags: @e2e, @{feature-name}
Preconditions: - {Prerequisites}
Flow Steps:
- {Step}
- {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