MCPInstall

playwright

Playwright E2E testing patterns. Trigger: When writing Playwright E2E tests (Page Object Model, selectors, MCP exploration workflow). For Prowler-specific UI conventions under ui/tests, also use prowler-test-ui.

Installs
119
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/prowler-cloud/prowler --skill playwright
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads playwright automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

MCP Workflow (MANDATORY If Available)

⚠️ If you have Playwright MCP tools, ALWAYS use them BEFORE creating any test:

  1. Navigate to target page
  2. Take snapshot to see page structure and elements
  3. Interact with forms/elements to verify exact user flow
  4. Take screenshots to document expected states
  5. Verify page transitions through complete flow (loading, success, error)
  6. Document actual selectors from snapshots (use real refs and labels)
  7. Only after exploring create test code with verified selectors

If MCP NOT available: Proceed with test creation based on docs and code analysis.

Why This Matters:

  • ✅ Precise tests - exact steps needed, no assumptions
  • ✅ Accurate selectors - real DOM structure, not imagined
  • ✅ Real flow validation - verify journey actually works
  • ✅ Avoid over-engineering - minimal tests for what exists
  • ✅ Prevent flaky tests - real exploration = stable tests
  • ❌ Never assume how UI "should" work

File Structure

tests/
├── base-page.ts              # Parent class for ALL pages
├── helpers.ts                # Shared utilities
└── {page-name}/
    ├── {page-name}-page.ts   # Page Object Model
    ├── {page-name}.spec.ts   # ALL tests here (NO separate files!)
    └── {page-name}.md        # Test documentation

File Naming:

  • ✅ sign-up.spec.ts (all sign-up tests)
  • ✅ sign-up-page.ts (page object)
  • ✅ sign-up.md (documentation)
  • ❌ sign-up-critical-path.spec.ts (WRONG - no separate files)
  • ❌ sign-up-validation.spec.ts (WRONG)

Selector Priority (REQUIRED)

// 1. BEST - getByRole for interactive elements
this.submitButton = page.getByRole("button", { name: "Submit" });
this.navLink = page.getByRole("link", { name: "Dashboard" });

// 2. BEST - getByLabel for form controls
this.emailInput = page.getByLabel("Email");
this.passwordInput = page.getByLabel("Password");

// 3. SPARINGLY - getByText for static content only
this.errorMessage = page.getByText("Invalid credentials");
this.pageTitle = page.getByText("Welcome");

// 4. LAST RESORT - getByTestId when above fail
this.customWidget = page.getByTestId("date-picker");

// ❌ AVOID fragile selectors
this.button = page.locator(".btn-primary");  // NO
this.input = page.locator("#email");         // NO

Scope Detection (ASK IF AMBIGUOUS)

User SaysAction
"a test", "one test", "new test", "add test"Create ONE test() in existing spec
"comprehensive tests", "all tests", "test suite", "generate tests"Create full suite

Examples:

  • "Create a test for user sign-up" → ONE test only
  • "Generate E2E tests for login page" → Full suite
  • "Add a test to verify form validation" → ONE test to existing spec

Page Object Pattern

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

// BasePage - ALL pages extend this
export class BasePage {
  constructor(protected page: Page) {}

  async goto(path: string): Promise<void> {
    await this.page.goto(path);
    await this.page.waitForLoadState("networkidle");
  }

  // Common methods go here (see Refactoring Guidelines)
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }

  async verifyNotificationMessage(message: string): Promise<void> {
    const notification = this.page.locator('[role="status"]');
    await expect(notification).toContainText(message);
  }
}

// Page-specific implementation
export interface LoginData {
  email: string;
  password: string;
}

export class LoginPage extends BasePage {
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;

  constructor(page: Page) {
    super(page);
    this.emailInput = page.getByLabel("Email");
    this.passwordInput = page.getByLabel("Password");
    this.submitButton = page.getByRole("button", { name: "Sign in" });
  }

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

  async login(data: LoginData): Promise<void> {
    await this.emailInput.fill(data.email);
    await this.passwordInput.fill(data.password);
    await this.submitButton.click();
  }

  async verifyCriticalOutcome(): Promise<void> {
    await expect(this.page).toHaveURL("/dashboard");
  }
}

Page Object Reuse (CRITICAL)

Always check existing page objects before creating new ones!

// ✅ GOOD: Reuse existing page objects
import { SignInPage } from "../sign-in/sign-in-page";
import { HomePage } from "../home/home-page";

test("User can sign up and login", async ({ page }) => {
  const signUpPage = new SignUpPage(page);
  const signInPage = new SignInPage(page);  // REUSE
  const homePage = new HomePage(page);      // REUSE

  await signUpPage.signUp(userData);
  await homePage.verifyPageLoaded();  // REUSE method
  await homePage.signOut();           // REUSE method
  await signInPage.login(credentials); // REUSE method
});

// ❌ BAD: Recreating existing functionality
export class SignUpPage extends BasePage {
  async logout() { /* ... */ }  // ❌ HomePage already has this
  async login() { /* ... */ }   // ❌ SignInPage already has this
}

Guidelines:

  • Check tests/ for existing page objects first
  • Import and reuse existing pages
  • Create page objects only when page doesn't exist
  • If test requires multiple pages, ensure all page objects exist (create if needed)

Refactoring Guidelines

Move to BasePage when

  • ✅ Navigation helpers used by multiple pages (waitForPageLoad(), getCurrentUrl())
  • ✅ Common UI interactions (notifications, modals, theme toggles)
  • ✅ Verification patterns repeated across pages (isVisible(), waitForVisible())
  • ✅ Error handling that applies to all pages
  • ✅ Screenshot utilities for debugging

Move to helpers.ts when

  • ✅ Test data generation (generateUniqueEmail(), generateTestUser())
  • ✅ Setup/teardown utilities (createTestUser(), cleanupTestData())
  • ✅ Custom assertions (expectNotificationToContain())
  • ✅ API helpers for test setup (seedDatabase(), resetState())
  • ✅ Time utilities (waitForCondition(), retryAction())

Before (BAD):

// Repeated in multiple page objects
export class SignUpPage extends BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }
}
export class SignInPage extends BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');  // DUPLICATED!
  }
}

After (GOOD):

// BasePage - shared across all pages
export class BasePage {
  async waitForNotification(): Promise<void> {
    await this.page.waitForSelector('[role="status"]');
  }
}

// helpers.ts - data generation
export function generateUniqueEmail(): string {
  return `test.${Date.now()}@example.com`;
}

export function generateTestUser() {
  return {
    name: "Test User",
    email: generateUniqueEmail(),
    password: "TestPassword123!",
  };
}

Test Pattern with Tags

import { test, expect } from "@playwright/test";
import { LoginPage } from "./login-page";

test.describe("Login", () => {
  test("User can login successfully",
    { tag: ["@critical", "@e2e", "@login", "@LOGIN-E2E-001"] },
    async ({ page }) => {
      const loginPage = new LoginPage(page);

      await loginPage.goto();
      await loginPage.login({ email: "user@test.com", password: "pass123" });

      await expect(page).toHaveURL("/dashboard");
    }
  );
});

Tag Categories:

  • Priority: @critical, @high, @medium, @low
  • Type: @e2e
  • Feature: @signup, @signin, @dashboard
  • Test ID: @SIGNUP-E2E-001, @LOGIN-E2E-002

Test Documentation Format ({page-name}.md)

### E2E Tests: {Feature Name}

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

---

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

**Priority:** `{critical|high|medium|low}`

**Tags:**
- type → @e2e
- feature → @{feature-name}

**Description/Objective:** {Brief description}

**Preconditions:**
- {Prerequisites for test to run}
- {Required data or state}

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

### Expected Result:
- {Expected outcome 1}
- {Expected outcome 2}

### Key verification points:
- {Assertion 1}
- {Assertion 2}

### Notes:
- {Additional considerations}

Documentation Rules:

  • ❌ NO general test running instructions
  • ❌ NO file structure explanations
  • ❌ NO code examples or tutorials
  • ❌ NO troubleshooting sections
  • ✅ Focus ONLY on specific test case
  • ✅ Keep under 60 lines when possible

Commands

npx playwright test                    # Run all
npx playwright test --grep "login"     # Filter by name
npx playwright test --ui               # Interactive UI
npx playwright test --debug            # Debug mode
npx playwright test tests/login/       # Run specific folder

Prowler-Specific Patterns

For Prowler UI E2E testing with authentication setup, environment variables, and test IDs, see:

  • Documentation: references/prowler-e2e.md

FAQ

What does the playwright skill do?

Playwright E2E testing patterns. Trigger: When writing Playwright E2E tests (Page Object Model, selectors, MCP exploration workflow). For Prowler-specific UI conventions under ui/tests, also use prowler-test-ui.

How do I install playwright?

Run npx skills add https://github.com/prowler-cloud/prowler --skill playwright in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/playwright/ (personal) or .claude/skills/playwright/ (project).

Which AI agents can use playwright?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is playwright safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains playwright?

It is published by prowler-cloud in prowler-cloud/prowler. It has 119 recorded installs.

More from prowler-cloud/prowler

View all →

zod-4

prowler-cloud/prowler

Zod 4 schema validation patterns. Trigger: When creating or updating Zod v4 schemas for validation/parsing (forms, request payloads, adapters), including v3 - v4 migration patterns.

zustand-5

prowler-cloud/prowler

Zustand 5 state management patterns. Trigger: When implementing client-side state with Zustand (stores, selectors, persist middleware, slices).

911 installs

react-19

prowler-cloud/prowler

React 19 patterns with React Compiler. Trigger: When writing React 19 components/hooks in .tsx (React Compiler rules, hook patterns, refs as props). If using Next.js App Router/Server Actions, also use nextjs-16.

Frontend & Design671 installs

tailwind-4

prowler-cloud/prowler

Tailwind CSS 4 patterns and best practices. Trigger: When styling with Tailwind (className, variants, cn()), especially when dynamic styling or CSS variables are involved (no var() in className).

Frontend & Design612 installs

django-drf

prowler-cloud/prowler

Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API specifics (RLS/RBAC/Providers), also use prowler-api.

Backend & APIs482 installs

typescript

prowler-cloud/prowler

TypeScript strict patterns and best practices. Trigger: When implementing or refactoring TypeScript in .ts/.tsx (types, interfaces, generics, const maps, type guards, removing any, tightening unknown).

Popular testing & qa skills

View category →

tdd

mattpocock/skills

Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

Testing & QA1.2M installs

grilling

mattpocock/skills

Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.

Testing & QA996.2K installs

migrate-to-shoehorn

mattpocock/skills

Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.

Testing & QA484.1K installs

run-train

lllllllama/rigorpilot-skills

Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.

Testing & QA337.8K installs

test-driven-development

obra/superpowers

Use when implementing any feature or bugfix, before writing implementation code

Testing & QA261.8K installs

Use when implementation is complete, all tests pass, and you need to decide how to integrate the work

Testing & QA216.8K installs