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

164 lines
5.7 KiB
Markdown

# Test Generation Patterns
## Pattern: Authentication Flow
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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();
});
});
```