agentsclimarketplace

E2e test generator

Skill VRIL-LABS/skill-jam/skills/core-collections/new-skills-2026/testing-qa/e2e-test-generator

Welcome to the skill-jam ☄️🏀

Install
npx -y skills add VRIL-LABS/skill-jam --skill e2e-test-generator

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Generate end-to-end tests with Playwright, Cypress, or Selenium — user journey testing, cross-browser validation, and automated testing. Use when testing complete user workflows, validating features across browsers, creating regression test suites, implementing CI/CD testing, or ensuring application quality before releases.

SKILL.md

19.2 KB, as published. Nobody here has run it

e2e-test-generator

Create comprehensive end-to-end tests for web applications with modern testing frameworks.

When to Use

Invoke this skill when you need to:

  • Test complete user workflows from start to finish
  • Validate features across different browsers and devices
  • Create regression test suites for critical paths
  • Automate testing in CI/CD pipelines
  • Generate test code from user stories or requirements
  • Debug failing tests and improve test reliability
  • Implement visual regression testing
  • Test single-page applications (SPAs) or complex UIs

Quick Start

Playwright Example

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

test.describe('User Authentication', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://example.com');
  });

  test('successful login flow', async ({ page }) => {
    // Navigate to login page
    await page.click('text=Sign In');
    
    // Fill in credentials
    await page.fill('input[name="email"]', '[email protected]');
    await page.fill('input[name="password"]', 'SecurePassword123!');
    
    // Submit form
    await page.click('button[type="submit"]');
    
    // Verify redirect to dashboard
    await expect(page).toHaveURL(/.*dashboard/);
    
    // Verify user is logged in
    await expect(page.locator('text=Welcome back')).toBeVisible();
    
    // Verify user menu contains email
    await expect(page.locator('[data-testid="user-menu"]')).toContainText('[email protected]');
  });

  test('login with invalid credentials', async ({ page }) => {
    await page.click('text=Sign In');
    await page.fill('input[name="email"]', '[email protected]');
    await page.fill('input[name="password"]', 'WrongPassword');
    await page.click('button[type="submit"]');
    
    // Verify error message
    await expect(page.locator('.error-message')).toContainText('Invalid credentials');
    
    // Verify still on login page
    await expect(page).toHaveURL(/.*login/);
  });

  test('password reset flow', async ({ page }) => {
    await page.click('text=Sign In');
    await page.click('text=Forgot password?');
    
    await page.fill('input[name="email"]', '[email protected]');
    await page.click('button:has-text("Send Reset Link")');
    
    await expect(page.locator('.success-message'))
      .toContainText('Password reset link sent');
  });
});

Cypress Example

// cypress/e2e/checkout.cy.ts
describe('E-commerce Checkout', () => {
  beforeEach(() => {
    cy.visit('/');
    // Login before each test
    cy.login('[email protected]', 'password123');
  });

  it('completes full checkout flow', () => {
    // Add items to cart
    cy.get('[data-testid="product-1"]').click();
    cy.get('button:contains("Add to Cart")').click();
    cy.get('.notification').should('contain', 'Added to cart');
    
    // Go to cart
    cy.get('[data-testid="cart-icon"]').click();
    cy.url().should('include', '/cart');
    
    // Verify cart contents
    cy.get('[data-testid="cart-items"]').should('have.length', 1);
    cy.get('[data-testid="cart-total"]').should('contain', '$29.99');
    
    // Proceed to checkout
    cy.get('button:contains("Checkout")').click();
    
    // Fill shipping information
    cy.get('input[name="fullName"]').type('John Doe');
    cy.get('input[name="address"]').type('123 Main St');
    cy.get('input[name="city"]').type('New York');
    cy.get('select[name="state"]').select('NY');
    cy.get('input[name="zipCode"]').type('10001');
    
    // Continue to payment
    cy.get('button:contains("Continue to Payment")').click();
    
    // Fill payment information (using test card)
    cy.get('iframe[name="card-frame"]').then($iframe => {
      const $body = $iframe.contents().find('body');
      cy.wrap($body)
        .find('input[name="cardNumber"]')
        .type('4242424242424242');
      cy.wrap($body)
        .find('input[name="cardExpiry"]')
        .type('1225');
      cy.wrap($body)
        .find('input[name="cardCvc"]')
        .type('123');
    });
    
    // Place order
    cy.get('button:contains("Place Order")').click();
    
    // Verify order confirmation
    cy.url().should('include', '/order-confirmation');
    cy.get('h1').should('contain', 'Order Confirmed');
    cy.get('[data-testid="order-number"]').should('exist');
    
    // Verify email notification sent
    cy.task('checkEmail', {
      to: '[email protected]',
      subject: 'Order Confirmation'
    }).should('exist');
  });

  it('applies discount code', () => {
    // Add item to cart
    cy.get('[data-testid="product-1"]').click();
    cy.get('button:contains("Add to Cart")').click();
    cy.get('[data-testid="cart-icon"]').click();
    
    // Apply discount code
    cy.get('input[name="discountCode"]').type('SAVE20');
    cy.get('button:contains("Apply")').click();
    
    // Verify discount applied
    cy.get('[data-testid="discount-amount"]').should('contain', '-$6.00');
    cy.get('[data-testid="cart-total"]').should('contain', '$23.99');
  });

  it('handles out of stock items', () => {
    cy.get('[data-testid="product-out-of-stock"]').click();
    
    cy.get('button:contains("Add to Cart")').should('be.disabled');
    cy.get('.stock-status').should('contain', 'Out of Stock');
    
    // Verify notify me option
    cy.get('button:contains("Notify Me")').should('be.visible');
  });
});

// cypress/support/commands.ts
Cypress.Commands.add('login', (email: string, password: string) => {
  cy.session([email, password], () => {
    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');
  });
});

Common Scenarios

Scenario 1: API Mocking and Stubbing

// Playwright with API mocking
import { test, expect } from '@playwright/test';

test('load users with mocked API', async ({ page }) => {
  // Intercept API requests and return mock data
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'Alice', email: '[email protected]' },
        { id: 2, name: 'Bob', email: '[email protected]' }
      ])
    });
  });
  
  await page.goto('/users');
  
  // Verify mocked data is displayed
  await expect(page.locator('[data-testid="user-1"]')).toContainText('Alice');
  await expect(page.locator('[data-testid="user-2"]')).toContainText('Bob');
});

test('handle API error gracefully', async ({ page }) => {
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 500,
      body: JSON.stringify({ error: 'Internal Server Error' })
    });
  });
  
  await page.goto('/users');
  
  await expect(page.locator('.error-message'))
    .toContainText('Failed to load users');
});

// Cypress with network stubbing
cy.intercept('GET', '/api/products', {
  statusCode: 200,
  body: {
    products: [
      { id: 1, name: 'Product 1', price: 29.99 },
      { id: 2, name: 'Product 2', price: 49.99 }
    ]
  }
}).as('getProducts');

cy.visit('/shop');
cy.wait('@getProducts');
cy.get('[data-testid="product"]').should('have.length', 2);

Scenario 2: File Upload Testing

// Playwright file upload
test('upload profile picture', async ({ page }) => {
  await page.goto('/profile/edit');
  
  // Set up file chooser listener
  const fileChooserPromise = page.waitForEvent('filechooser');
  await page.click('text=Upload Photo');
  const fileChooser = await fileChooserPromise;
  
  // Upload file
  await fileChooser.setFiles('./fixtures/profile-photo.jpg');
  
  // Verify preview
  const preview = page.locator('[data-testid="photo-preview"]');
  await expect(preview).toBeVisible();
  
  // Save changes
  await page.click('button:has-text("Save")');
  
  // Verify upload success
  await expect(page.locator('.success-message')).toContainText('Profile updated');
});

// Cypress file upload
cy.get('input[type="file"]').selectFile('cypress/fixtures/document.pdf');
cy.get('[data-testid="file-name"]').should('contain', 'document.pdf');
cy.get('button:contains("Upload")').click();
cy.get('.upload-status').should('contain', 'Upload complete');

Scenario 3: Multi-Step Form Validation

test('wizard form with validation', async ({ page }) => {
  await page.goto('/registration');
  
  // Step 1: Personal Information
  await page.fill('input[name="firstName"]', 'John');
  await page.fill('input[name="lastName"]', 'Doe');
  await page.fill('input[name="email"]', '[email protected]');
  await page.click('button:has-text("Next")');
  
  // Step 2: Address
  await expect(page.locator('h2')).toContainText('Address Information');
  await page.fill('input[name="street"]', '123 Main St');
  await page.fill('input[name="city"]', 'Boston');
  await page.selectOption('select[name="state"]', 'MA');
  await page.fill('input[name="zipCode"]', '02101');
  await page.click('button:has-text("Next")');
  
  // Step 3: Preferences
  await expect(page.locator('h2')).toContainText('Preferences');
  await page.check('input[name="newsletter"]');
  await page.selectOption('select[name="language"]', 'en');
  
  // Submit
  await page.click('button[type="submit"]');
  
  // Verify success
  await expect(page.locator('.success-message'))
    .toContainText('Registration complete');
});

Scenario 4: Authentication and Authorization

test.describe('Protected Routes', () => {
  test('redirect to login when not authenticated', async ({ page }) => {
    await page.goto('/dashboard');
    await expect(page).toHaveURL(/.*login/);
  });

  test('admin can access admin panel', async ({ page, context }) => {
    // Set admin auth cookie
    await context.addCookies([{
      name: 'auth_token',
      value: 'admin-token-123',
      domain: 'example.com',
      path: '/'
    }]);
    
    await page.goto('/admin');
    await expect(page.locator('h1')).toContainText('Admin Panel');
  });

  test('regular user cannot access admin panel', async ({ page, context }) => {
    await context.addCookies([{
      name: 'auth_token',
      value: 'user-token-456',
      domain: 'example.com',
      path: '/'
    }]);
    
    await page.goto('/admin');
    await expect(page.locator('.error-message'))
      .toContainText('Access denied');
  });
});

Best Practices

Page Object Model

// 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('input[name="email"]', email);
    await this.page.fill('input[name="password"]', password);
    await this.page.click('button[type="submit"]');
  }

  async getErrorMessage() {
    return await this.page.locator('.error-message').textContent();
  }

  async isLoggedIn() {
    await this.page.waitForURL(/.*dashboard/);
    return this.page.url().includes('dashboard');
  }
}

// pages/DashboardPage.ts
export class DashboardPage {
  constructor(private page: Page) {}

  async getUserName() {
    return await this.page.locator('[data-testid="user-name"]').textContent();
  }

  async navigateToSettings() {
    await this.page.click('[data-testid="settings-link"]');
  }

  async logout() {
    await this.page.click('[data-testid="user-menu"]');
    await this.page.click('text=Logout');
  }
}

// Using Page Objects
test('login and navigate', async ({ page }) => {
  const loginPage = new LoginPage(page);
  const dashboardPage = new DashboardPage(page);
  
  await loginPage.goto();
  await loginPage.login('[email protected]', 'password');
  
  expect(await loginPage.isLoggedIn()).toBeTruthy();
  expect(await dashboardPage.getUserName()).toBe('John Doe');
  
  await dashboardPage.navigateToSettings();
});

Test Data Management

// fixtures/users.json
{
  "admin": {
    "email": "[email protected]",
    "password": "Admin123!",
    "role": "admin"
  },
  "customer": {
    "email": "[email protected]",
    "password": "Customer123!",
    "role": "customer"
  }
}

// tests/helpers.ts
import users from '../fixtures/users.json';

export function getUser(role: 'admin' | 'customer') {
  return users[role];
}

export async function loginAs(page: Page, role: 'admin' | 'customer') {
  const user = getUser(role);
  await page.goto('/login');
  await page.fill('input[name="email"]', user.email);
  await page.fill('input[name="password"]', user.password);
  await page.click('button[type="submit"]');
  await page.waitForURL(/.*dashboard/);
}

// Usage in tests
test('admin workflow', async ({ page }) => {
  await loginAs(page, 'admin');
  // Test admin features
});

Waiting Strategies

// Wait for element
await page.waitForSelector('[data-testid="results"]');

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

// Wait for network idle
await page.waitForLoadState('networkidle');

// Wait for custom condition
await page.waitForFunction(() => {
  return document.querySelectorAll('.item').length > 5;
});

// Wait for API response
const responsePromise = page.waitForResponse(
  response => response.url().includes('/api/data') && response.status() === 200
);
await page.click('button:has-text("Load Data")');
const response = await responsePromise;
const data = await response.json();

Visual Regression Testing

// Playwright screenshot comparison
test('homepage visual test', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png', {
    fullPage: true,
    maxDiffPixels: 100
  });
});

test('button states', async ({ page }) => {
  await page.goto('/components');
  
  const button = page.locator('button.primary');
  
  // Default state
  await expect(button).toHaveScreenshot('button-default.png');
  
  // Hover state
  await button.hover();
  await expect(button).toHaveScreenshot('button-hover.png');
  
  // Disabled state
  await page.evaluate(() => {
    document.querySelector('button.primary').disabled = true;
  });
  await expect(button).toHaveScreenshot('button-disabled.png');
});

Playwright Configuration

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html'],
    ['junit', { outputFile: 'test-results/junit.xml' }],
    ['json', { outputFile: 'test-results/results.json' }]
  ],
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-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 12'] },
    },
  ],
  webServer: {
    command: 'npm run start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Cypress Configuration

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: true,
    screenshotOnRunFailure: true,
    experimentalStudio: true,
    setupNodeEvents(on, config) {
      // Email checking task
      on('task', {
        async checkEmail({ to, subject }) {
          // Implementation to check test email
          return { found: true };
        }
      });
      
      // Database seeding
      on('task', {
        async seedDatabase(data) {
          // Seed test database
          return null;
        }
      });
    },
  },
  env: {
    apiUrl: 'http://localhost:4000/api',
  },
  retries: {
    runMode: 2,
    openMode: 0,
  },
});

CI/CD Integration

# .github/workflows/e2e-tests.yml
name: E2E Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        browser: [chromium, firefox, webkit]
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Install Playwright browsers
        run: npx playwright install --with-deps ${{ matrix.browser }}
      
      - name: Run E2E tests
        run: npx playwright test --project=${{ matrix.browser }}
        env:
          CI: true
      
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: test-results-${{ matrix.browser }}
          path: test-results/
          retention-days: 30
      
      - name: Upload Playwright report
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report-${{ matrix.browser }}
          path: playwright-report/
          retention-days: 30

Troubleshooting

Flaky Tests

// Use auto-waiting
await page.click('button'); // Automatically waits for button to be actionable

// Increase timeout for slow operations
await page.waitForSelector('.results', { timeout: 30000 });

// Disable animations
await page.addStyleTag({
  content: '*, *::before, *::after { animation-duration: 0s !important; }'
});

// Wait for network to be idle
await page.waitForLoadState('networkidle');

// Use test.slow() for tests that need more time
test.slow();
test('slow operation', async ({ page }) => {
  // This test gets 3x timeout
});

Debugging

// Playwright debugging
test('debug test', async ({ page }) => {
  await page.pause(); // Opens Playwright Inspector
  
  // Step-by-step debugging
  await page.goto('/');
  console.log(await page.title());
  await page.screenshot({ path: 'debug.png' });
});

// Run in headed mode
npx playwright test --headed

// Run in debug mode
npx playwright test --debug

// Cypress debugging
cy.pause(); // Pause execution
cy.debug(); // Enable debugger
cy.log('Custom message');

Related Skills

  • visual-regression-tester: Compare UI screenshots
  • accessibility-tester: Test WCAG compliance
  • cross-browser-tester: Multi-browser testing
  • api-fuzz-tester: Test API security
  • load-generator: Performance testing
  • integration-test-builder: API and backend testing

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.