Files
claude-skills-reference/engineering-team/playwright-pro/skills/generate/patterns.md
Alireza Rezvani d33d03da50 feat: add playwright-pro plugin — production-grade Playwright testing toolkit (#254)
Complete Claude Code plugin with:
- 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report)
- 3 specialized agents (test-architect, test-debugger, migration-planner)
- 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility)
- TestRail MCP server (TypeScript) — 8 tools for bidirectional sync
- BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing
- Smart hooks (auto-validate tests, auto-detect Playwright projects)
- 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests)
- Leverages Claude Code built-ins (/batch, /debug, Explore subagent)
- Zero-config for core features; TestRail/BrowserStack via env vars
- Both TypeScript and JavaScript support throughout

Co-authored-by: Leo <leo@openclaw.ai>
2026-03-05 13:50:05 +01:00

5.7 KiB

Test Generation Patterns

Pattern: Authentication Flow

test.describe('Authentication', () => {
  test('should login with valid credentials', async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('user@example.com');
    await page.getByLabel('Password').fill('password123');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  });

  test('should show error for invalid credentials', async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('wrong@example.com');
    await page.getByLabel('Password').fill('wrong');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await expect(page.getByRole('alert')).toHaveText(/invalid/i);
    await expect(page).toHaveURL('/login');
  });
});

Pattern: CRUD Operations

test.describe('Items', () => {
  test('should create a new item', async ({ page }) => {
    await page.goto('/items');
    await page.getByRole('button', { name: 'Add item' }).click();
    await page.getByLabel('Name').fill('Test Item');
    await page.getByRole('button', { name: 'Save' }).click();
    await expect(page.getByText('Test Item')).toBeVisible();
  });

  test('should edit an existing item', async ({ page }) => {
    await page.goto('/items');
    await page.getByRole('row', { name: /Test Item/ })
      .getByRole('button', { name: 'Edit' }).click();
    await page.getByLabel('Name').clear();
    await page.getByLabel('Name').fill('Updated Item');
    await page.getByRole('button', { name: 'Save' }).click();
    await expect(page.getByText('Updated Item')).toBeVisible();
  });

  test('should delete an item with confirmation', async ({ page }) => {
    await page.goto('/items');
    await page.getByRole('row', { name: /Test Item/ })
      .getByRole('button', { name: 'Delete' }).click();
    await page.getByRole('button', { name: 'Confirm' }).click();
    await expect(page.getByText('Test Item')).not.toBeVisible();
  });
});

Pattern: Form with Validation

test.describe('Contact Form', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/contact');
  });

  test('should submit valid form', async ({ page }) => {
    await page.getByLabel('Name').fill('Jane Doe');
    await page.getByLabel('Email').fill('jane@example.com');
    await page.getByLabel('Message').fill('Hello, this is a test message.');
    await page.getByRole('button', { name: 'Send' }).click();
    await expect(page.getByText('Message sent')).toBeVisible();
  });

  test('should show validation errors for empty required fields', async ({ page }) => {
    await page.getByRole('button', { name: 'Send' }).click();
    await expect(page.getByText('Name is required')).toBeVisible();
    await expect(page.getByText('Email is required')).toBeVisible();
  });

  test('should validate email format', async ({ page }) => {
    await page.getByLabel('Email').fill('not-an-email');
    await page.getByRole('button', { name: 'Send' }).click();
    await expect(page.getByText('Invalid email')).toBeVisible();
  });
});

Pattern: Search and Filter

test.describe('Product Search', () => {
  test('should return results for valid query', async ({ page }) => {
    await page.goto('/products');
    await page.getByPlaceholder('Search products').fill('laptop');
    await page.getByRole('button', { name: 'Search' }).click();
    await expect(page.getByRole('list')).toBeVisible();
    const results = page.getByRole('listitem');
    await expect(results).not.toHaveCount(0);
  });

  test('should show empty state for no results', async ({ page }) => {
    await page.goto('/products');
    await page.getByPlaceholder('Search products').fill('xyznonexistent');
    await page.getByRole('button', { name: 'Search' }).click();
    await expect(page.getByText('No products found')).toBeVisible();
  });

  test('should filter by category', async ({ page }) => {
    await page.goto('/products');
    await page.getByRole('combobox', { name: 'Category' }).selectOption('Electronics');
    await expect(page.getByRole('listitem')).not.toHaveCount(0);
  });
});

Pattern: Navigation and Layout

test.describe('Navigation', () => {
  test('should navigate between pages', async ({ page }) => {
    await page.goto('/');
    await page.getByRole('link', { name: 'About' }).click();
    await expect(page).toHaveURL('/about');
    await expect(page.getByRole('heading', { level: 1 })).toHaveText('About');
  });

  test('should show mobile menu on small screens', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/');
    await expect(page.getByRole('navigation')).not.toBeVisible();
    await page.getByRole('button', { name: 'Menu' }).click();
    await expect(page.getByRole('navigation')).toBeVisible();
  });
});

Pattern: API Mocking

test.describe('Dashboard with mocked API', () => {
  test('should display data from API', async ({ page }) => {
    await page.route('**/api/dashboard', (route) => {
      route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ revenue: 50000, users: 1200 }),
      });
    });
    await page.goto('/dashboard');
    await expect(page.getByText('$50,000')).toBeVisible();
    await expect(page.getByText('1,200')).toBeVisible();
  });

  test('should handle API errors gracefully', async ({ page }) => {
    await page.route('**/api/dashboard', (route) => {
      route.fulfill({ status: 500 });
    });
    await page.goto('/dashboard');
    await expect(page.getByText(/error|try again/i)).toBeVisible();
  });
});