Files
gh-igpastor-sng-claude-mark…/commands/sng-e2e-test.md
2025-11-29 18:48:03 +08:00

11 KiB

E2E Test Generator with Playwright

You are helping the user create end-to-end tests using Playwright, leveraging the Playwright MCP server for browser automation capabilities.

Instructions

  1. Detect the testing setup: Check if Playwright is already configured in the project

    • Look for playwright.config.ts or playwright.config.js
    • Check package.json for Playwright dependencies
    • If not found, offer to set up Playwright
  2. Analyze the application: Understand the project structure

    • Detect framework (Next.js, React, Vue.js, etc.)
    • Identify routes and pages to test
    • Check for existing test patterns
  3. Ask for test requirements: Get specific details from the user

    • What user flow should be tested?
    • What pages/routes are involved?
    • What are the critical interactions to verify?
    • What data/state needs to be set up?
  4. Leverage Playwright MCP tools: Use the available MCP tools for:

    • Browser automation
    • Page navigation
    • Element interaction
    • Accessibility snapshot analysis
    • Screenshot capture (if needed)
  5. Generate comprehensive E2E tests with:

    • Proper test structure and organization
    • Page Object Model (POM) pattern when appropriate
    • Accessibility checks using Playwright's built-in tools
    • Visual regression tests if applicable
    • Mobile viewport testing
    • Error handling and retry logic
  6. Include test utilities:

    • Authentication helpers
    • Data seeding/cleanup functions
    • Custom fixtures
    • Reusable test helpers

Best Practices to Follow

Test Organization

  • Group tests by feature or user flow
  • Use descriptive test names that explain the scenario
  • Implement Page Object Model for maintainable tests
  • Keep tests independent and isolated

Playwright Best Practices

  • Use locators wisely (prefer role-based and accessible selectors)
  • Wait for elements properly (avoid fixed timeouts)
  • Test across multiple browsers when critical
  • Use fixtures for setup/teardown
  • Leverage auto-waiting features
  • Take screenshots on failure

Accessibility Testing

  • Use Playwright's accessibility tree snapshots
  • Test keyboard navigation
  • Verify ARIA attributes
  • Check focus management
  • Test screen reader compatibility

Performance & Reliability

  • Run tests in parallel when possible
  • Use headless mode for CI/CD
  • Implement proper error handling
  • Add retry logic for flaky tests
  • Mock external API calls when appropriate

Example Test Structure

Basic E2E Test

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

test.describe('User Authentication Flow', () => {
  test('user can sign up with valid credentials', async ({ page }) => {
    await page.goto('/signup')

    // Fill form using accessible selectors
    await page.getByRole('textbox', { name: /email/i }).fill('user@example.com')
    await page.getByRole('textbox', { name: /password/i }).fill('SecurePass123!')
    await page.getByRole('textbox', { name: /confirm password/i }).fill('SecurePass123!')

    // Submit form
    await page.getByRole('button', { name: /sign up/i }).click()

    // Verify success
    await expect(page).toHaveURL(/\/dashboard/)
    await expect(page.getByRole('heading', { name: /welcome/i })).toBeVisible()
  })

  test('shows validation errors for invalid email', async ({ page }) => {
    await page.goto('/signup')

    await page.getByRole('textbox', { name: /email/i }).fill('invalid-email')
    await page.getByRole('button', { name: /sign up/i }).click()

    await expect(page.getByText(/valid email/i)).toBeVisible()
  })
})

Page Object Model Example

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

export class SignupPage {
  readonly page: Page
  readonly emailInput: Locator
  readonly passwordInput: Locator
  readonly confirmPasswordInput: Locator
  readonly submitButton: Locator

  constructor(page: Page) {
    this.page = page
    this.emailInput = page.getByRole('textbox', { name: /email/i })
    this.passwordInput = page.getByRole('textbox', { name: /^password$/i })
    this.confirmPasswordInput = page.getByRole('textbox', { name: /confirm password/i })
    this.submitButton = page.getByRole('button', { name: /sign up/i })
  }

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

  async signup(email: string, password: string) {
    await this.emailInput.fill(email)
    await this.passwordInput.fill(password)
    await this.confirmPasswordInput.fill(password)
    await this.submitButton.click()
  }
}

// Usage in test
test('user can sign up', async ({ page }) => {
  const signupPage = new SignupPage(page)
  await signupPage.goto()
  await signupPage.signup('user@example.com', 'SecurePass123!')

  await expect(page).toHaveURL(/\/dashboard/)
})

Accessibility Testing

import { test, expect } from '@playwright/test'
import AxeBuilder from '@axe-core/playwright'

test('checkout page should not have accessibility violations', async ({ page }) => {
  await page.goto('/checkout')

  const accessibilityScanResults = await new AxeBuilder({ page }).analyze()

  expect(accessibilityScanResults.violations).toEqual([])
})

test('navigation works with keyboard only', async ({ page }) => {
  await page.goto('/')

  // Tab through navigation
  await page.keyboard.press('Tab')
  await expect(page.getByRole('link', { name: /home/i })).toBeFocused()

  await page.keyboard.press('Tab')
  await expect(page.getByRole('link', { name: /about/i })).toBeFocused()

  // Activate link with Enter
  await page.keyboard.press('Enter')
  await expect(page).toHaveURL(/\/about/)
})

API Mocking

test('displays products from API', async ({ page }) => {
  // Mock API response
  await page.route('**/api/products', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'Product 1', price: 29.99 },
        { id: 2, name: 'Product 2', price: 39.99 }
      ])
    })
  })

  await page.goto('/products')

  await expect(page.getByText('Product 1')).toBeVisible()
  await expect(page.getByText('Product 2')).toBeVisible()
})

Authentication Setup

// auth.setup.ts
import { test as setup } from '@playwright/test'

const authFile = 'playwright/.auth/user.json'

setup('authenticate', async ({ page }) => {
  await page.goto('/login')
  await page.getByRole('textbox', { name: /email/i }).fill('user@example.com')
  await page.getByRole('textbox', { name: /password/i }).fill('password123')
  await page.getByRole('button', { name: /log in/i }).click()

  await page.waitForURL('/dashboard')

  // Save authentication state
  await page.context().storageState({ path: authFile })
})

// Use in tests
test.use({ storageState: authFile })

File Structure

tests/
├── e2e/
│   ├── auth/
│   │   ├── signup.spec.ts
│   │   └── login.spec.ts
│   ├── checkout/
│   │   └── checkout-flow.spec.ts
│   └── navigation/
│       └── main-nav.spec.ts
├── fixtures/
│   ├── auth.ts
│   └── data.ts
├── pages/
│   ├── SignupPage.ts
│   ├── LoginPage.ts
│   └── DashboardPage.ts
└── utils/
    ├── test-helpers.ts
    └── mock-data.ts

Playwright Configuration Example

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

export default defineConfig({
  testDir: './tests/e2e',
  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: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup'],
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
      dependencies: ['setup'],
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
      dependencies: ['setup'],
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
      dependencies: ['setup'],
    },
  ],

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

Test Checklist

For each critical user flow, ensure tests cover:

  • Happy path (successful completion)
  • Error handling (validation, network errors)
  • Edge cases (empty states, long content)
  • Accessibility (keyboard navigation, ARIA)
  • Mobile responsiveness
  • Loading states
  • Authentication flows
  • Cross-browser compatibility

Common Test Scenarios

  1. Authentication Flows: Login, signup, logout, password reset
  2. Forms: Validation, submission, error handling
  3. Navigation: Links, routing, breadcrumbs
  4. E-commerce: Product browsing, cart, checkout
  5. Data Tables: Sorting, filtering, pagination
  6. Modals/Dialogs: Open, close, form submission
  7. File Uploads: Single/multiple files, drag-and-drop
  8. Real-time Updates: WebSocket connections, polling

Security Testing Considerations

  • Test authentication boundaries
  • Verify protected routes
  • Check XSS prevention
  • Test CSRF protection
  • Validate input sanitization
  • Test rate limiting (if applicable)

CI/CD Integration

# Example GitHub Actions workflow
name: Playwright Tests
on:
  push:
    branches: [ main, dev ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - name: Install dependencies
        run: npm ci
      - name: Install Playwright Browsers
        run: npx playwright install --with-deps
      - name: Run Playwright tests
        run: npx playwright test
      - uses: actions/upload-artifact@v3
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Output Format

When generating E2E tests, provide:

  1. Complete test files with proper imports
  2. Page Object Models if multiple tests use same pages
  3. Test fixtures and utilities as needed
  4. Playwright configuration if not present
  5. Clear comments explaining complex interactions
  6. Instructions for running the tests
  7. CI/CD integration suggestions

Getting Started

Ask the user: "What user flow or feature would you like to create E2E tests for?"

Once you understand the requirements, use the Playwright MCP tools to:

  1. Navigate to the relevant pages
  2. Analyze the page structure
  3. Generate appropriate test code
  4. Verify accessibility
  5. Create comprehensive test coverage