
E2e Testing Automation
- 630 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
e2e-testing-automation is a Claude Code skill that generates and maintains reliable Cypress end-to-end test suites verifying critical user flows for web applications.
About
e2e-testing-automation is a testing skill that scaffolds Cypress end-to-end specs for web applications. It produces cypress/e2e test files with describe and it blocks, cy.visit navigation, data-cy and name attribute selectors, form filling, URL assertions, and repeatable user-flow coverage such as registration and authentication. Developers reach for e2e-testing-automation when they need automated browser tests for signup, login, checkout, or other multi-step UI paths without hand-writing every cy.get interaction. The skill emphasizes reliable selectors, beforeEach setup, and flow verification patterns suitable for CI pipelines running Cypress against staging or production-like environments.
- Generates complete Cypress E2E test files for authentication, registration, validation, and dashboard flows
- Includes realistic form handling, API verification, timestamped test data, and error-state assertions
- Uses beforeEach hooks, cy.request for backend validation, and clear success criteria
- Produces production-grade test patterns that catch UI regressions and backend integration issues
E2e Testing Automation by the numbers
- 630 all-time installs (skills.sh)
- Ranked #586 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill e2e-testing-automationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 630 |
|---|---|
| repo stars | ★ 305 |
| Security audit | 3 / 3 scanners passed |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you write Cypress e2e tests for user flows?
Generate and maintain reliable Cypress end-to-end test suites that verify critical user flows automatically.
Who is it for?
Web developers adding or expanding Cypress coverage for authentication, registration, and other critical browser-based user journeys.
Skip if: Teams using Playwright or Vitest component tests only, or projects without a Cypress test runner configured.
When should I use this skill?
The user asks to create, extend, or fix Cypress end-to-end tests for signup, login, checkout, or multi-step UI flows.
What you get
Cypress .cy.js spec files with describe blocks, cy.visit setup, form interactions, and URL or state assertions.
- cypress/e2e test spec files
- authentication and registration flow tests
Files
E2E Testing Automation
Table of Contents
Overview
End-to-end (E2E) testing validates complete user workflows from the UI through all backend systems, ensuring the entire application stack works together correctly from a user's perspective. E2E tests simulate real user interactions with browsers, handling authentication, navigation, form submissions, and validating results.
When to Use
- Testing critical user journeys (signup, checkout, login)
- Validating multi-step workflows
- Testing across different browsers and devices
- Regression testing for UI changes
- Verifying frontend-backend integration
- Testing with real user interactions (clicks, typing, scrolling)
- Smoke testing deployments
Quick Start
Minimal working example:
// tests/e2e/checkout.spec.ts
import { test, expect, Page } from "@playwright/test";
test.describe("E-commerce Checkout Flow", () => {
let page: Page;
test.beforeEach(async ({ page: p }) => {
page = p;
await page.goto("/");
});
test("complete checkout flow as guest user", async () => {
// 1. Browse and add product to cart
await page.click("text=Shop Now");
await page.click('[data-testid="product-1"]');
await expect(page.locator("h1")).toContainText("Product Name");
await page.click('button:has-text("Add to Cart")');
await expect(page.locator(".cart-count")).toHaveText("1");
// 2. Go to cart and proceed to checkout
await page.click('[data-testid="cart-icon"]');
await expect(page.locator(".cart-item")).toHaveCount(1);
await page.click("text=Proceed to Checkout");
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Playwright E2E Tests | Playwright E2E Tests |
| Cypress E2E Tests | Cypress E2E Tests |
| Selenium with Python (pytest) | Selenium with Python (pytest) |
| Page Object Model Pattern | Page Object Model Pattern |
Best Practices
✅ DO
- Use data-testid attributes for stable selectors
- Implement Page Object Model for maintainability
- Test critical user journeys thoroughly
- Run tests in multiple browsers (cross-browser testing)
- Use explicit waits instead of sleep/timeouts
- Clean up test data after each test
- Take screenshots on failures
- Parallelize test execution where possible
❌ DON'T
- Use brittle CSS selectors (like nth-child)
- Test every possible UI combination (focus on critical paths)
- Share state between tests
- Use fixed delays (sleep/timeout)
- Ignore flaky tests
- Run E2E tests for unit-level testing
- Test third-party UI components in detail
- Skip mobile/responsive testing
Cypress E2E Tests
Cypress E2E Tests
// cypress/e2e/authentication.cy.js
describe("User Authentication Flow", () => {
beforeEach(() => {
cy.visit("/");
});
it("should register a new user account", () => {
cy.get('[data-cy="signup-button"]').click();
cy.url().should("include", "/signup");
// Fill registration form
const timestamp = Date.now();
cy.get('[name="email"]').type(`user${timestamp}@example.com`);
cy.get('[name="password"]').type("SecurePass123!");
cy.get('[name="confirmPassword"]').type("SecurePass123!");
cy.get('[name="firstName"]').type("Test");
cy.get('[name="lastName"]').type("User");
// Accept terms
cy.get('[name="acceptTerms"]').check();
// Submit form
cy.get('button[type="submit"]').click();
// Verify success
cy.url().should("include", "/dashboard");
cy.get(".welcome-message").should("contain", "Welcome, Test!");
// Verify email sent (check via API)
cy.request(`/api/test/emails/${timestamp}@example.com`)
.its("body")
.should("have.property", "subject", "Welcome to Our App");
});
it("should handle validation errors", () => {
cy.get('[data-cy="signup-button"]').click();
// Submit empty form
cy.get('button[type="submit"]').click();
// Check for validation errors
cy.get(".error-message").should("have.length.greaterThan", 0);
cy.get('[name="email"]').parent().should("contain", "Email is required");
// Fill invalid email
cy.get('[name="email"]').type("invalid-email");
cy.get('[name="password"]').type("weak");
cy.get('button[type="submit"]').click();
cy.get('[name="email"]').parent().should("contain", "Invalid email format");
cy.get('[name="password"]')
.parent()
.should("contain", "Password must be at least 8 characters");
});
it("should login with valid credentials", () => {
// Create test user first
cy.request("POST", "/api/test/users", {
email: "test@example.com",
password: "Password123!",
name: "Test User",
});
// Login
cy.get('[data-cy="login-button"]').click();
cy.get('[name="email"]').type("test@example.com");
cy.get('[name="password"]').type("Password123!");
cy.get('button[type="submit"]').click();
// Verify login successful
cy.url().should("include", "/dashboard");
cy.getCookie("auth_token").should("exist");
// Verify user menu
cy.get('[data-cy="user-menu"]').click();
cy.get(".user-email").should("contain", "test@example.com");
});
it("should maintain session across page reloads", () => {
// Login
cy.loginViaAPI("test@example.com", "Password123!");
cy.visit("/dashboard");
// Verify logged in
cy.get(".user-menu").should("exist");
// Reload page
cy.reload();
// Still logged in
cy.get(".user-menu").should("exist");
cy.getCookie("auth_token").should("exist");
});
it("should logout successfully", () => {
cy.loginViaAPI("test@example.com", "Password123!");
cy.visit("/dashboard");
cy.get('[data-cy="user-menu"]').click();
cy.get('[data-cy="logout-button"]').click();
cy.url().should("equal", Cypress.config().baseUrl + "/");
cy.getCookie("auth_token").should("not.exist");
});
});
// Custom command for login
Cypress.Commands.add("loginViaAPI", (email, password) => {
cy.request("POST", "/api/auth/login", { email, password }).then(
(response) => {
window.localStorage.setItem("auth_token", response.body.token);
},
);
});Page Object Model Pattern
Page Object Model Pattern
// pages/LoginPage.ts
import { Page, Locator } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly loginButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.locator('[name="email"]');
this.passwordInput = page.locator('[name="password"]');
this.loginButton = page.locator('button[type="submit"]');
this.errorMessage = page.locator(".error-message");
}
async goto() {
await this.page.goto("/login");
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
async getErrorMessage(): Promise<string> {
return await this.errorMessage.textContent();
}
}
// tests/login.spec.ts
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/LoginPage";
test("login with invalid credentials", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login("invalid@example.com", "wrongpassword");
const error = await loginPage.getErrorMessage();
expect(error).toContain("Invalid credentials");
});Playwright E2E Tests
Playwright E2E Tests
// tests/e2e/checkout.spec.ts
import { test, expect, Page } from "@playwright/test";
test.describe("E-commerce Checkout Flow", () => {
let page: Page;
test.beforeEach(async ({ page: p }) => {
page = p;
await page.goto("/");
});
test("complete checkout flow as guest user", async () => {
// 1. Browse and add product to cart
await page.click("text=Shop Now");
await page.click('[data-testid="product-1"]');
await expect(page.locator("h1")).toContainText("Product Name");
await page.click('button:has-text("Add to Cart")');
await expect(page.locator(".cart-count")).toHaveText("1");
// 2. Go to cart and proceed to checkout
await page.click('[data-testid="cart-icon"]');
await expect(page.locator(".cart-item")).toHaveCount(1);
await page.click("text=Proceed to Checkout");
// 3. Fill shipping information
await page.fill('[name="email"]', "test@example.com");
await page.fill('[name="firstName"]', "John");
await page.fill('[name="lastName"]', "Doe");
await page.fill('[name="address"]', "123 Main St");
await page.fill('[name="city"]', "San Francisco");
await page.selectOption('[name="state"]', "CA");
await page.fill('[name="zip"]', "94105");
// 4. Enter payment information
await page.click("text=Continue to Payment");
// Wait for payment iframe to load
const paymentFrame = page.frameLocator('iframe[name="payment-frame"]');
await paymentFrame.locator('[name="cardNumber"]').fill("4242424242424242");
await paymentFrame.locator('[name="expiry"]').fill("12/25");
await paymentFrame.locator('[name="cvc"]').fill("123");
// 5. Complete order
await page.click('button:has-text("Place Order")');
// 6. Verify success
await expect(page).toHaveURL(/\/order\/confirmation/);
await expect(page.locator(".confirmation-message")).toContainText(
"Order placed successfully",
);
const orderNumber = await page
.locator('[data-testid="order-number"]')
.textContent();
expect(orderNumber).toMatch(/^ORD-\d+$/);
});
test("checkout with existing user account", async () => {
// Login first
await page.click("text=Sign In");
await page.fill('[name="email"]', "existing@example.com");
await page.fill('[name="password"]', "Password123!");
await page.click('button[type="submit"]');
await expect(page.locator(".user-menu")).toContainText(
"existing@example.com",
);
// Add product and checkout with saved information
await page.click('[data-testid="product-2"]');
await page.click('button:has-text("Add to Cart")');
await page.click('[data-testid="cart-icon"]');
await page.click("text=Checkout");
// Verify saved address is pre-filled
await expect(page.locator('[name="address"]')).toHaveValue(/./);
// Complete checkout
await page.click('button:has-text("Use Saved Payment")');
await page.click('button:has-text("Place Order")');
await expect(page).toHaveURL(/\/order\/confirmation/);
});
test("handle out of stock product", async () => {
await page.click('[data-testid="product-out-of-stock"]');
const addToCartButton = page.locator('button:has-text("Add to Cart")');
await expect(addToCartButton).toBeDisabled();
await expect(page.locator(".stock-status")).toHaveText("Out of Stock");
});
});Selenium with Python (pytest)
Selenium with Python (pytest)
# tests/e2e/test_search_functionality.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
class TestSearchFunctionality:
@pytest.fixture
def driver(self):
"""Setup and teardown browser."""
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.implicitly_wait(10)
yield driver
driver.quit()
def test_search_with_results(self, driver):
"""Test search functionality returns relevant results."""
driver.get('http://localhost:3000')
# Find search box and enter query
search_box = driver.find_element(By.NAME, 'search')
search_box.send_keys('laptop')
search_box.send_keys(Keys.RETURN)
# Wait for results
wait = WebDriverWait(driver, 10)
results = wait.until(
EC.presence_of_all_elements_located((By.CLASS_NAME, 'search-result'))
)
# Verify results
assert len(results) > 0
assert 'laptop' in driver.page_source.lower()
# Check first result has required elements
first_result = results[0]
assert first_result.find_element(By.CLASS_NAME, 'product-title')
assert first_result.find_element(By.CLASS_NAME, 'product-price')
assert first_result.find_element(By.CLASS_NAME, 'product-image')
def test_search_filters(self, driver):
"""Test applying filters to search results."""
driver.get('http://localhost:3000/search?q=laptop')
wait = WebDriverWait(driver, 10)
# Wait for results to load
wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'search-result'))
)
initial_count = len(driver.find_elements(By.CLASS_NAME, 'search-result'))
# Apply price filter
price_filter = driver.find_element(By.ID, 'price-filter-500-1000')
price_filter.click()
# Wait for filtered results
wait.until(
EC.staleness_of(driver.find_element(By.CLASS_NAME, 'search-result'))
)
wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'search-result'))
)
filtered_count = len(driver.find_elements(By.CLASS_NAME, 'search-result'))
# Verify filter was applied
assert filtered_count <= initial_count
# Verify all prices are in range
prices = driver.find_elements(By.CLASS_NAME, 'product-price')
for price_elem in prices:
price = float(price_elem.text.replace('$', '').replace(',', ''))
assert 500 <= price <= 1000
def test_pagination(self, driver):
"""Test navigating through search result pages."""
driver.get('http://localhost:3000/search?q=electronics')
wait = WebDriverWait(driver, 10)
# Get first page results
first_page_results = driver.find_elements(By.CLASS_NAME, 'search-result')
first_result_title = first_page_results[0].find_element(
By.CLASS_NAME, 'product-title'
).text
# Click next page
next_button = driver.find_element(By.CSS_SELECTOR, '[aria-label="Next page"]')
next_button.click()
# Wait for new results
wait.until(EC.staleness_of(first_page_results[0]))
# Verify on page 2
assert 'page=2' in driver.current_url
second_page_results = driver.find_elements(By.CLASS_NAME, 'search-result')
second_result_title = second_page_results[0].find_element(
By.CLASS_NAME, 'product-title'
).text
# Results should be different
assert first_result_title != second_result_title
def test_empty_search_results(self, driver):
"""Test handling of searches with no results."""
driver.get('http://localhost:3000')
search_box = driver.find_element(By.NAME, 'search')
search_box.send_keys('xyznonexistentproduct123')
search_box.send_keys(Keys.RETURN)
wait = WebDriverWait(driver, 10)
no_results = wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'no-results'))
)
assert 'no results found' in no_results.text.lower()
assert len(driver.find_elements(By.CLASS_NAME, 'search-result')) == 0#!/bin/bash
# scaffold-tests.sh - Generate test file scaffolding
# Usage: ./scaffold-tests.sh <source_file> [--framework jest|pytest|mocha]
set -euo pipefail
SOURCE_FILE="${{1:?Usage: $0 <source_file> [--framework jest|pytest|mocha]}}"
FRAMEWORK="${{2:-jest}}"
echo "Scaffolding tests for: $SOURCE_FILE (framework: $FRAMEWORK)"
# TODO: Implement test scaffolding logic
# - Parse source file for exported functions/classes
# - Generate test stubs for each export
# - Include setup/teardown boilerplate
# - Add common assertion patterns
echo "Test scaffolding complete."
// Test Template
// TODO: Customize for your testing framework and project
describe('ModuleName', () => {
// Setup
beforeEach(() => {
// TODO: Add test setup
});
afterEach(() => {
// TODO: Add cleanup
});
describe('functionName', () => {
it('should handle the happy path', () => {
// TODO: Add assertion
});
it('should handle edge cases', () => {
// TODO: Add edge case tests
});
it('should handle errors gracefully', () => {
// TODO: Add error handling tests
});
});
});
Related skills
How it compares
Choose e2e-testing-automation over unit-test skills when the goal is full-browser Cypress coverage of multi-page user journeys rather than isolated function tests.
FAQ
What test runner does e2e-testing-automation use?
e2e-testing-automation targets Cypress end-to-end tests stored under cypress/e2e as .cy.js files. Specs use cy.visit, cy.get, cy.type, cy.click, and cy.url().should for flow verification.
Which selectors does the Cypress skill recommend?
e2e-testing-automation favors stable selectors such as data-cy attributes and HTML name fields over brittle CSS paths. Example patterns include cy.get('[data-cy="signup-button"]') and cy.get('[name="email"]') for form fields.
Is E2e Testing Automation safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.