E2e test generator
Skill VRIL-LABS/skill-jam/skills/core-collections/new-skills-2026/testing-qa/e2e-test-generator
Welcome to the skill-jam ☄️🏀
npx -y skills add VRIL-LABS/skill-jam --skill e2e-test-generatorAssembled 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