Initial commit
This commit is contained in:
215
commands/generate-e2e.md
Normal file
215
commands/generate-e2e.md
Normal file
@@ -0,0 +1,215 @@
|
||||
---
|
||||
description: Generate end-to-end browser tests for user workflows
|
||||
shortcut: e2e
|
||||
---
|
||||
|
||||
# End-to-End Test Generator
|
||||
|
||||
Generate comprehensive browser-based E2E tests for complete user workflows using Playwright, Cypress, or Selenium.
|
||||
|
||||
## Purpose
|
||||
|
||||
Create automated tests that simulate real user interactions:
|
||||
- Complete user journeys (signup → login → purchase)
|
||||
- Multi-page workflows
|
||||
- Form interactions and validations
|
||||
- Navigation flows
|
||||
- Browser-specific behaviors
|
||||
- Mobile responsive testing
|
||||
|
||||
## Supported Frameworks
|
||||
|
||||
**Playwright** (Recommended)
|
||||
- Multi-browser support (Chromium, Firefox, WebKit)
|
||||
- Auto-wait for elements
|
||||
- Network interception
|
||||
- Mobile emulation
|
||||
- Parallel execution
|
||||
|
||||
**Cypress**
|
||||
- Time-travel debugging
|
||||
- Real-time reloads
|
||||
- Automatic waiting
|
||||
- Network stubbing
|
||||
- Screenshot/video capture
|
||||
|
||||
**Selenium WebDriver**
|
||||
- Mature ecosystem
|
||||
- Cross-browser support
|
||||
- Remote execution (Selenium Grid)
|
||||
- Mobile testing (Appium)
|
||||
|
||||
## Test Generation
|
||||
|
||||
When invoked, generate E2E tests with:
|
||||
|
||||
1. **User workflow analysis**
|
||||
- Identify key user journeys
|
||||
- Map page interactions
|
||||
- Define success criteria
|
||||
|
||||
2. **Page Object Model**
|
||||
- Create page classes
|
||||
- Define selectors
|
||||
- Implement action methods
|
||||
|
||||
3. **Test scenarios**
|
||||
- Happy path workflows
|
||||
- Error handling paths
|
||||
- Edge cases
|
||||
|
||||
4. **Assertions**
|
||||
- URL validation
|
||||
- Element presence
|
||||
- Content verification
|
||||
- State changes
|
||||
|
||||
## Example: Playwright Test
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('User Registration Flow', () => {
|
||||
test('should complete full registration workflow', async ({ page }) => {
|
||||
// Navigate to signup
|
||||
await page.goto('https://example.com/signup');
|
||||
|
||||
// Fill registration form
|
||||
await page.fill('[name="email"]', '[email protected]');
|
||||
await page.fill('[name="password"]', 'SecurePass123!');
|
||||
await page.fill('[name="confirmPassword"]', 'SecurePass123!');
|
||||
await page.check('[name="terms"]');
|
||||
|
||||
// Submit and wait for navigation
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForURL('**/verify-email');
|
||||
|
||||
// Verify success message
|
||||
await expect(page.locator('.success-message')).toContainText(
|
||||
'Please check your email'
|
||||
);
|
||||
});
|
||||
|
||||
test('should show validation errors for invalid data', async ({ page }) => {
|
||||
await page.goto('https://example.com/signup');
|
||||
|
||||
// Submit empty form
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
// Verify error messages
|
||||
await expect(page.locator('.error-email')).toBeVisible();
|
||||
await expect(page.locator('.error-password')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('E-commerce Purchase Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Login before each test
|
||||
await page.goto('https://example.com/login');
|
||||
await page.fill('[name="email"]', '[email protected]');
|
||||
await page.fill('[name="password"]', 'password123');
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForURL('**/dashboard');
|
||||
});
|
||||
|
||||
test('should complete product purchase', async ({ page }) => {
|
||||
// Browse products
|
||||
await page.goto('https://example.com/products');
|
||||
await page.click('text=Add to Cart').first();
|
||||
|
||||
// Verify cart badge
|
||||
await expect(page.locator('.cart-badge')).toHaveText('1');
|
||||
|
||||
// Go to checkout
|
||||
await page.click('.cart-icon');
|
||||
await page.click('text=Checkout');
|
||||
|
||||
// Fill shipping info
|
||||
await page.fill('[name="address"]', '123 Main St');
|
||||
await page.fill('[name="city"]', 'San Francisco');
|
||||
await page.fill('[name="zip"]', '94102');
|
||||
await page.click('text=Continue');
|
||||
|
||||
// Enter payment (test mode)
|
||||
await page.fill('[name="cardNumber"]', '4242424242424242');
|
||||
await page.fill('[name="expiry"]', '12/25');
|
||||
await page.fill('[name="cvc"]', '123');
|
||||
await page.click('text=Place Order');
|
||||
|
||||
// Verify success
|
||||
await page.waitForURL('**/order/confirmation');
|
||||
await expect(page.locator('.order-success')).toBeVisible();
|
||||
await expect(page.locator('.order-number')).toContainText('ORDER-');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Example: Cypress Test
|
||||
|
||||
```javascript
|
||||
describe('User Dashboard Workflow', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('[email protected]', 'password123');
|
||||
});
|
||||
|
||||
it('should load dashboard with user data', () => {
|
||||
cy.visit('/dashboard');
|
||||
cy.get('.welcome-message').should('contain', 'Welcome, John');
|
||||
cy.get('.user-stats').should('be.visible');
|
||||
});
|
||||
|
||||
it('should create new project', () => {
|
||||
cy.visit('/dashboard');
|
||||
cy.get('[data-testid="new-project"]').click();
|
||||
|
||||
cy.get('[name="projectName"]').type('My New Project');
|
||||
cy.get('[name="description"]').type('Project description here');
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
||||
cy.url().should('include', '/projects/');
|
||||
cy.get('.project-title').should('contain', 'My New Project');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Page Object Model
|
||||
|
||||
```typescript
|
||||
// pages/LoginPage.ts
|
||||
export class LoginPage {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/login');
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
await this.page.fill('[name="email"]', email);
|
||||
await this.page.fill('[name="password"]', password);
|
||||
await this.page.click('button[type="submit"]');
|
||||
}
|
||||
|
||||
async getErrorMessage() {
|
||||
return this.page.locator('.error-message').textContent();
|
||||
}
|
||||
}
|
||||
|
||||
// Test using Page Object
|
||||
test('login with page object', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
await loginPage.goto();
|
||||
await loginPage.login('[email protected]', 'password123');
|
||||
await expect(page).toHaveURL('/dashboard');
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Use data-testid attributes** - Stable selectors
|
||||
- **Implement Page Objects** - Reusable page interactions
|
||||
- **Handle waits properly** - Auto-wait or explicit waits
|
||||
- **Test realistic scenarios** - Actual user workflows
|
||||
- **Parallelize tests** - Faster execution
|
||||
- **Capture screenshots on failure** - Debugging aid
|
||||
- **Use fixtures** - Consistent test data
|
||||
- **Test across browsers** - Chrome, Firefox, Safari
|
||||
Reference in New Issue
Block a user