chore: initialize frontend miniapp repository
This commit is contained in:
249
.agents/skills/playwright-e2e-tester/SKILL.md
Normal file
249
.agents/skills/playwright-e2e-tester/SKILL.md
Normal file
@@ -0,0 +1,249 @@
|
||||
---
|
||||
name: playwright-e2e-tester
|
||||
description: Expert in end-to-end testing with Playwright, the modern cross-browser testing framework. Specializes in test generation, page object patterns, visual regression testing, and CI/CD integration.
|
||||
Handles complex testing scenarios including authentication flows, API mocking, and mobile emulation.
|
||||
version: 1.0.0
|
||||
metadata:
|
||||
category: testing
|
||||
tags:
|
||||
- e2e
|
||||
- playwright
|
||||
- testing
|
||||
- automation
|
||||
- ci-cd
|
||||
- cross-browser
|
||||
pairs-with:
|
||||
- skill: test-automation-expert
|
||||
reason: Playwright E2E tests are one tier in a comprehensive test automation strategy
|
||||
- skill: webapp-testing
|
||||
reason: 'Both use Playwright but for different scopes: E2E test suites vs interactive debugging'
|
||||
- skill: vitest-testing-patterns
|
||||
reason: Unit tests (Vitest) and E2E tests (Playwright) form complementary test pyramid layers
|
||||
- skill: github-actions-pipeline-builder
|
||||
reason: E2E tests run in CI pipelines with browser installation and artifact upload steps
|
||||
---
|
||||
|
||||
# Playwright E2E Tester
|
||||
|
||||
## Overview
|
||||
|
||||
Expert in end-to-end testing with Playwright, the modern cross-browser testing framework. Specializes in test generation, page object patterns, visual regression testing, and CI/CD integration. Handles complex testing scenarios including authentication flows, API mocking, and mobile emulation.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Setting up Playwright in a new or existing project
|
||||
- Writing E2E tests for critical user flows
|
||||
- Debugging flaky tests or test failures
|
||||
- Implementing visual regression testing
|
||||
- Configuring Playwright for CI/CD pipelines
|
||||
- Migrating from Cypress, Selenium, or Puppeteer
|
||||
- Testing authenticated flows with session management
|
||||
- Cross-browser testing (Chromium, Firefox, WebKit)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Test Generation & Writing
|
||||
- Generate Playwright tests from user stories or acceptance criteria
|
||||
- Write tests using best practices (locators, assertions, waits)
|
||||
- Implement Page Object Model (POM) patterns
|
||||
- Create reusable test fixtures and utilities
|
||||
- Handle dynamic content and race conditions
|
||||
|
||||
### Configuration & Setup
|
||||
- Configure `playwright.config.ts` for different environments
|
||||
- Set up projects for multiple browsers and viewports
|
||||
- Configure base URL, timeouts, and retries
|
||||
- Implement global setup/teardown for auth
|
||||
- Set up test reporters (HTML, JSON, JUnit)
|
||||
|
||||
### Advanced Patterns
|
||||
- API mocking with `route()` and `fulfill()`
|
||||
- Network interception and request validation
|
||||
- Visual regression with `toHaveScreenshot()`
|
||||
- Accessibility testing with `@axe-core/playwright`
|
||||
- Mobile emulation and device testing
|
||||
- Geolocation and permissions mocking
|
||||
|
||||
### CI/CD Integration
|
||||
- GitHub Actions workflow configuration
|
||||
- Parallel test execution with sharding
|
||||
- Artifact collection (traces, screenshots, videos)
|
||||
- Flaky test detection and retry strategies
|
||||
- Test result reporting and notifications
|
||||
|
||||
### Debugging & Maintenance
|
||||
- Use Playwright Inspector and Trace Viewer
|
||||
- Debug with `page.pause()` and headed mode
|
||||
- Analyze test traces for failures
|
||||
- Reduce test flakiness with proper waits
|
||||
- Maintain test stability over time
|
||||
|
||||
## Dependencies
|
||||
|
||||
Works well with:
|
||||
- `vitest-testing-patterns` - Unit test patterns that complement E2E
|
||||
- `github-actions-pipeline-builder` - CI/CD pipeline setup
|
||||
- `accessibility-auditor` - Extended accessibility testing
|
||||
- `api-architect` - API contract testing alongside E2E
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Test Structure
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('User Authentication', () => {
|
||||
test('should allow user to sign in', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.getByLabel('Email').fill('user@example.com');
|
||||
await page.getByLabel('Password').fill('securepassword');
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
||||
await expect(page).toHaveURL('/dashboard');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Page Object Pattern
|
||||
```typescript
|
||||
// pages/LoginPage.ts
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
|
||||
export class LoginPage {
|
||||
readonly page: Page;
|
||||
readonly emailInput: Locator;
|
||||
readonly passwordInput: Locator;
|
||||
readonly signInButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.emailInput = page.getByLabel('Email');
|
||||
this.passwordInput = page.getByLabel('Password');
|
||||
this.signInButton = page.getByRole('button', { name: 'Sign In' });
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/login');
|
||||
}
|
||||
|
||||
async signIn(email: string, password: string) {
|
||||
await this.emailInput.fill(email);
|
||||
await this.passwordInput.fill(password);
|
||||
await this.signInButton.click();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auth Setup Fixture
|
||||
```typescript
|
||||
// fixtures/auth.ts
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
export const test = base.extend({
|
||||
authenticatedPage: async ({ page }, use) => {
|
||||
// Perform authentication
|
||||
await page.goto('/login');
|
||||
await page.getByLabel('Email').fill(process.env.TEST_USER!);
|
||||
await page.getByLabel('Password').fill(process.env.TEST_PASS!);
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
|
||||
// Wait for auth to complete
|
||||
await page.waitForURL('/dashboard');
|
||||
|
||||
// Use the authenticated page in tests
|
||||
await use(page);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### GitHub Actions CI
|
||||
```yaml
|
||||
name: E2E Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: Run E2E tests
|
||||
run: npx playwright test
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
```
|
||||
|
||||
### Visual Regression Test
|
||||
```typescript
|
||||
test('homepage matches snapshot', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Full page screenshot comparison
|
||||
await expect(page).toHaveScreenshot('homepage.png', {
|
||||
fullPage: true,
|
||||
maxDiffPixelRatio: 0.01,
|
||||
});
|
||||
|
||||
// Component-level screenshot
|
||||
const hero = page.getByTestId('hero-section');
|
||||
await expect(hero).toHaveScreenshot('hero-section.png');
|
||||
});
|
||||
```
|
||||
|
||||
### API Mocking
|
||||
```typescript
|
||||
test('displays products from API', async ({ page }) => {
|
||||
// Mock the API response
|
||||
await page.route('**/api/products', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify([
|
||||
{ id: 1, name: 'Product A', price: 29.99 },
|
||||
{ id: 2, name: 'Product B', price: 49.99 },
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('/products');
|
||||
|
||||
await expect(page.getByText('Product A')).toBeVisible();
|
||||
await expect(page.getByText('$29.99')).toBeVisible();
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use role-based locators** - Prefer `getByRole()`, `getByLabel()`, `getByText()` over CSS selectors
|
||||
2. **Avoid hard waits** - Use `waitForSelector()`, `waitForURL()`, or assertions instead of `waitForTimeout()`
|
||||
3. **Isolate tests** - Each test should be independent and not rely on state from other tests
|
||||
4. **Use fixtures** - Share setup logic through fixtures rather than `beforeEach` hooks
|
||||
5. **Keep tests focused** - Test one user flow per test, avoid testing multiple unrelated things
|
||||
6. **Handle flakiness proactively** - Use proper waits, retries, and stable locators
|
||||
7. **Organize with Page Objects** - Encapsulate page interactions for maintainability
|
||||
8. **Run in CI** - Always run E2E tests in CI before merging
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Flaky locators**: Avoid fragile selectors like `nth-child(3)` or auto-generated class names
|
||||
- **Race conditions**: Always wait for elements/navigation before interacting
|
||||
- **Shared state**: Tests should not depend on execution order
|
||||
- **Slow tests**: Use API calls to set up state instead of UI interactions when possible
|
||||
- **Missing cleanup**: Clean up test data to avoid pollution between runs
|
||||
Reference in New Issue
Block a user