bobmatnyc/claude-mpm-skills2 files

Playwright

Playwright modern end-to-end testing framework with cross-browser automation, auto-wait, and built-in test runner

Specification
Skill ID
bobmatnyc/claude-mpm-skills/playwright
Publisher
bobmatnyc
Repository
claude-mpm-skills
Installs
0
Files
2
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

bobmatnyc/claude-mpm-skills/playwrightInstalls these files
  • SKILL.md
  • metadata.json

What this skill tells the agent

Playwright E2E Testing Skill


progressive_disclosure: entry_point: summary: "Modern E2E testing framework with cross-browser automation and built-in test runner" when_to_use:

  • "When testing web applications end-to-end"
  • "When needing cross-browser testing"
  • "When testing user flows and interactions"
  • "When needing screenshot/video recording" quick_start:
  • "npm init playwright@latest"
  • "Choose TypeScript and test location"
  • "npx playwright test"
  • "npx playwright show-report" token_estimate: entry: 75-90 full: 4200-5200

<!-- ENTRY POINT - Load this section by default (75-90 tokens) -->

Overview

Playwright is a modern end-to-end testing framework that provides cross-browser automation with a built-in test runner, auto-wait mechanisms, and excellent developer experience.

Key Features

  • Auto-wait: Automatically waits for elements to be ready
  • Cross-browser: Chromium, Firefox, WebKit support
  • Built-in runner: Parallel execution, retries, reporters
  • Network control: Mock and intercept network requests
  • Debugging: UI mode, trace viewer, inspector

<!-- FULL CONTENT - Load on demand (4200-5200 tokens) -->

Installation

# Initialize new Playwright project
npm init playwright@latest

# Or add to existing project
npm install -D @playwright/test

# Install browsers
npx playwright install

Configuration

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',

  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
  ],

  webServer: {
    command: 'npm run start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Fundamentals

Basic Test Structure

import { test, expect } from '@playwright/test';

test('basic test', async ({ page }) => {
  await page.goto('https://example.com');

  // Wait for element and check visibility
  const title = page.locator('h1');
  await expect(title).toBeVisible();
  await expect(title).toHaveText('Example Domain');

  // Get page title
  await expect(page).toHaveTitle(/Example/);
});

test.describe('User authentication', () => {
  test('should login successfully', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[name="username"]', 'testuser');
    await page.fill('[name="password"]', 'password123');
    await page.click('button[type="submit"]');

    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('.welcome-message')).toContainText('Welcome');
  });

  test('should show error for invalid credentials', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[name="username"]', 'invalid');
    await page.fill('[name="password"]', 'wrong');
    await page.click('button[type="submit"]');

    await expect(page.locator('.error-message')).toBeVisible();
    await expect(page.locator('.error-message')).toHaveText('Invalid credentials');
  });
});

Test Hooks

import { test, expect } from '@playwright/test';

test.describe('Dashboard tests', () => {
  test.beforeEach(async ({ page }) => {
    // Run before each test
    await page.goto('/dashboard');
    await page.waitForLoadState('networkidle');
  });

  test.afterEach(async ({ page }) => {
    // Cleanup after each test
    await page.close();
  });

  test.beforeAll(async ({ browser }) => {
    // Run once before all tests in describe block
    console.log('Starting test suite');
  });

  test.afterAll(async ({ browser }) => {
    // Run once after all tests
    console.log('Test suite complete');
  });

  test('displays user data', async ({ page }) => {
    await expect(page.locator('.user-name')).toBeVisible();
  });
});

Locator Strategies

Best Practice: Role-based Locators

import { test, expect } from '@playwright/test';

test('accessible locators', async ({ page }) => {
  await page.goto('/form');

  // By role (BEST - accessible and stable)
  await page.getByRole('button', { name: 'Submit' }).click();
  await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
  await page.getByRole('checkbox', { name: 'Subscribe' }).check();
  await page.getByRole('link', { name: 'Learn more' }).click();

  // By label (good for forms)
  await page.getByLabel('Password').fill('secret123');

  // By placeholder
  await page.getByPlaceholder('Search...').fill('query');

  // By text
  await page.getByText('Welcome back').click();
  await page.getByText(/hello/i).isVisible();

  // By test ID (good for dynamic content)
  await page.getByTestId('user-profile').click();

  // By title
  await page.getByTitle('Close dialog').click();

  // By alt text (images)
  await page.getByAltText('User avatar').click();
});

CSS and XPath Locators

test('CSS and XPath locators', async ({ page }) => {
  // CSS selectors
  await page.locator('button.primary').click();
  await page.locator('#user-menu').click();
  await page.locator('[data-testid="submit-btn"]').click();
  await page.locator('div.card:first-child').click();

  // XPath (use sparingly)
  await page.locator('xpath=//button[contains(text(), "Submit")]').click();

  // Chaining locators
  const form = page.locator('form#login-form');
  await form.locator('input[name="email"]').fill('user@example.com');
  await form.locator('button[type="submit"]').click();

  // Filter locators
  await page.getByRole('listitem')
    .filter({ hasText: 'Product 1' })
    .getByRole('button', { name: 'Add to cart' })
    .click();
});

Page Object Model

Page Class Pattern

// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly usernameInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.usernameInput = page.getByLabel('Username');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton = page.getByRole('button', { name: 'Log in' });
    this.errorMessage = page.locator('.error-message');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(username: string, password: string) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectErrorMessage(message: string) {
    await this.errorMessage.waitFor({ state: 'visible' });
    await expect(this.errorMessage).toHaveText(message);
  }
}

// pages/DashboardPage.ts
export class DashboardPage {
  readonly page: Page;
  readonly welcomeMessage: Locator;
  readonly logoutButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.welcomeMessage = page.locator('.welcome-message');
    this.logoutButton = page.getByRole('button', { name: 'Logout' });
  }

  async waitForLoad() {
    await this.welcomeMessage.waitFor({ state: 'visible' });
  }

  async logout() {
    await this.logoutButton.click();
  }
}

// tests/auth.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';

test('successful login flow', async ({ page }) => {

Use this skill in a project

Open any RiverX project, search the Skills panel for bobmatnyc/claude-mpm-skills/playwright, and install it in one click.

Use this skill