Back to catalog

e2e-scenario-creator

Create E2E test scenarios with Playwright, Cypress. User flows, authentication, visual regression. Keywords - e2e testing, playwright, cypress, end to end, user flows, integration testing, visual testing

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: e2e-scenario-creator
description: Create E2E test scenarios with Playwright, Cypress. User flows, authentication, visual regression. Keywords - e2e testing, playwright, cypress, end to end, user flows, integration testing, visual testing
---

# E2E Scenario Creator

## When to Use This Skill

Activate when:
- Testing complete user flows (login → dashboard → action)
- Need cross-browser testing
- Testing authentication flows
- Visual regression testing
- Testing critical user journeys

## What This Skill Does

Creates E2E tests with:
- Complete user flows (multi-step scenarios)
- Authentication (login, sessions, tokens)
- Form submissions and validations
- Navigation and routing
- API interactions
- Visual regression testing
- Cross-browser testing

## Supported Technologies

**E2E Frameworks**:
- Playwright (recommended - modern, fast)
- Cypress (popular, easy to use)
- Puppeteer (headless Chrome)

## Example: Playwright Basic Test

```typescript
// tests/e2e/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Login Flow', () => {
  test('user can log in with valid credentials', async ({ page }) => {
    // Navigate to login page
    await page.goto('/login');

    // Fill in login form
    await page.getByLabel('Email').fill('user@example.com');
    await page.getByLabel('Password').fill('password123');

    // Click login button
    await page.getByRole('button', { name: 'Log in' }).click();

    // Wait for navigation to dashboard
    await page.waitForURL('/dashboard');

    // Verify we're logged in
    await expect(page.getByText('Welcome back!')).toBeVisible();
    await expect(page.getByRole('button', { name: 'Logout' })).toBeVisible();
  });

  test('shows error for invalid credentials', async ({ page }) => {
    await page.goto('/login');

    await page.getByLabel('Email').fill('user@example.com');
    await page.getByLabel('Password').fill('wrongpassword');
    await page.getByRole('button', { name: 'Log in' }).click();

    // Check error message appears
    await expect(page.getByText('Invalid email or password')).toBeVisible();

    // Still on login page
    await expect(page).toHaveURL('/login');
  });

  test('validates required fields', async ({ page }) => {
    await page.goto('/login');

    // Try to submit without filling fields
    await page.getByRole('button', { name: 'Log in' }).click();

    // Check validation errors
    await expect(page.getByText('Email is required')).toBeVisible();
    await expect(page.getByText('Password is required')).toBeVisible();
  });
});
```

## Example: Authenticated User Flow (Playwright)

```typescript
// tests/e2e/authenticated.spec.ts
import { test, expect } from '@playwright/test';

// Setup authenticated state before each test
test.beforeEach(async ({ page }) => {
  // Login before each test
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Log in' }).click();
  await page.waitForURL('/dashboard');
});

test('user can create a new post', async ({ page }) => {
  // Navigate to create post page
  await page.getByRole('link', { name: 'New Post' }).click();
  await expect(page).toHaveURL('/posts/new');

  // Fill in post form
  await page.getByLabel('Title').fill('My Test Post');
  await page.getByLabel('Content').fill('This is a test post content.');

  // Submit form
  await page.getByRole('button', { name: 'Publish' }).click();

  // Wait for success message
  await expect(page.getByText('Post published successfully')).toBeVisible();

  // Verify redirected to post page
  await expect(page).toHaveURL(/\/posts\/\d+/);
  await expect(page.getByRole('heading', { name: 'My Test Post' })).toBeVisible();
});

test('user can edit their profile', async ({ page }) => {
  // Go to profile page
  await page.getByRole('button', { name: 'Account' }).click();
  await page.getByRole('menuitem', { name: 'Profile' }).click();

  // Edit name
  await page.getByLabel('Name').fill('Updated Name');
  await page.getByRole('button', { name: 'Save' }).click();

  // Check success
  await expect(page.getByText('Profile updated')).toBeVisible();
});
```

## Example: API Mocking in E2E Tests

```typescript
// tests/e2e/with-mocking.spec.ts
import { test, expect } from '@playwright/test';

test('handles slow API gracefully', async ({ page }) => {
  // Mock API to respond slowly
  await page.route('/api/users', async (route) => {
    await new Promise((resolve) => setTimeout(resolve, 3000)); // 3s delay
    await route.fulfill({
      status: 200,
      body: JSON.stringify({ users: [] }),
    });
  });

  await page.goto('/users');

  // Check loading state appears
  await expect(page.getByText('Loading...')).toBeVisible();

  // Wait for data to load
  await expect(page.getByText('Loading...')).not.toBeVisible({ timeout: 5000 });
});

test('handles API errors', async ({ page }) => {
  // Mock API to return error
  await page.route('/api/users', (route) => {
    route.fulfill({
      status: 500,
      body: JSON.stringify({ error: 'Internal server error' }),
    });
  });

  await page.goto('/users');

  // Check error message
  await expect(page.getByText('Failed to load users')).toBeVisible();
  await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
});
```

## Example: Visual Regression Testing

```typescript
// tests/e2e/visual.spec.ts
import { test, expect } from '@playwright/test';

test('homepage looks correct', async ({ page }) => {
  await page.goto('/');

  // Take screenshot and compare with baseline
  await expect(page).toHaveScreenshot('homepage.png');
});

test('dashboard in light mode', async ({ page }) => {
  await page.goto('/dashboard');

  // Take full page screenshot
  await expect(page).toHaveScreenshot('dashboard-light.png', {
    fullPage: true,
  });
});

test('modal dialog appearance', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('button', { name: 'Open Modal' }).click();

  // Screenshot specific element
  const modal = page.getByRole('dialog');
  await expect(modal).toHaveScreenshot('modal-dialog.png');
});
```

## Example: Cypress Test

```typescript
// cypress/e2e/login.cy.ts
describe('Login Flow', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('user can log in with valid credentials', () => {
    cy.get('input[name="email"]').type('user@example.com');
    cy.get('input[name="password"]').type('password123');
    cy.get('button[type="submit"]').click();

    // Check redirected to dashboard
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome back!').should('be.visible');
  });

  it('shows error for invalid credentials', () => {
    cy.get('input[name="email"]').type('user@example.com');
    cy.get('input[name="password"]').type('wrongpassword');
    cy.get('button[type="submit"]').click();

    cy.contains('Invalid email or password').should('be.visible');
    cy.url().should('include', '/login');
  });
});

// Custom command for login
Cypress.Commands.add('login', (email: string, password: string) => {
  cy.visit('/login');
  cy.get('input[name="email"]').type(email);
  cy.get('input[name="password"]').type(password);
  cy.get('button[type="submit"]').click();
  cy.url().should('include', '/dashboard');
});

// Usage
it('user can access protected page', () => {
  cy.login('user@example.com', 'password123');
  cy.visit('/settings');
  cy.contains('Settings').should('be.visible');
});
```

## Example: Multi-Step User Journey

```typescript
// tests/e2e/checkout.spec.ts
import { test, expect } from '@playwright/test';

test('complete checkout flow', async ({ page }) => {
  // Step 1: Browse products
  await page.goto('/products');
  await page.getByText('iPhone 15').click();

  // Step 2: Add to cart
  await page.getByRole('button', { name: 'Add to Cart' }).click();
  await expect(page.getByText('Added to cart')).toBeVisible();

  // Step 3: Go to cart
  await page.getByRole('link', { name: 'Cart (1)' }).click();
  await expect(page).toHaveURL('/cart');
  await expect(page.getByText('iPhone 15')).toBeVisible();

  // Step 4: Proceed to checkout
  await page.getByRole('button', { name: 'Checkout' }).click();
  await expect(page).toHaveURL('/checkout');

  // Step 5: Fill shipping info
  await page.getByLabel('Full Name').fill('John Doe');
  await page.getByLabel('Address').fill('123 Main St');
  await page.getByLabel('City').fill('New York');
  await page.getByLabel('ZIP').fill('10001');

  // Step 6: Fill payment info
  await page.getByLabel('Card Number').fill('4242424242424242');
  await page.getByLabel('Expiry').fill('12/25');
  await page.getByLabel('CVC').fill('123');

  // Step 7: Place order
  await page.getByRole('button', { name: 'Place Order' }).click();

  // Step 8: Verify success
  await expect(page).toHaveURL(/\/orders\/\d+/);
  await expect(page.getByText('Order confirmed!')).toBeVisible();
  await expect(page.getByText('Order #')).toBeVisible();
});
```

## Playwright Configuration

```typescript
// 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: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    {
      name: 'mobile-chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'mobile-safari',
      use: { ...devices['iPhone 13'] },
    },
  ],

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

## Best Practices

1. **Test user flows** - Complete journeys, not just pages
2. **Use data-testid** - For elements that change frequently
3. **Wait for elements** - Use `waitFor` instead of `sleep`
4. **Authenticate once** - Save auth state, reuse in tests
5. **Mock external APIs** - Don't depend on third-party services
6. **Run in parallel** - Use workers for speed
7. **Cross-browser** - Test on Chrome, Firefox, Safari
8. **Visual regression** - Screenshot critical pages

## Integration with Other Skills

- **test-suite-generator** - Combine with unit tests
- **mock-data-builder** - Mock API responses in E2E tests
- **api-endpoint-builder** - Test API endpoints

---

**Version**: 1.0.0
**Category**: Testing + DevOps
**Complexity**: Medium-High