Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
hieutrtr avatar

E2e Testing

  • 1.6k installs
  • 8 repo stars
  • Updated February 6, 2026
  • hieutrtr/ai1-skills

e2e-testing is an agent skill for -

About

The e2e-testing skill - It covers writing E2E tests for complete user workflows login, CRUD operations, multi-page flows. Key workflows include creating critical path regression tests that validate the full stack. Activate this skill when: - Writing E2E tests for complete user workflows login, CRUD operations, multi-page flows - Creating critical path regression tests that validate the full stack - Testing cross-browser compatibility Chromium, Firefox, WebKit - Validating authentication flows end-to-end - Testing file upload/download workflows - Writing smoke tests for deployment verification Do NOT use thi Developers invoke e2e-testing when the task matches the triggers and reference files in SKILL.md for grounded, stepwise execution. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation. Reference files and progressive disclosure keep context focused while preserving concrete commands, configuration fields, and validation checks copied from the upstream documentation.

  • Writing E2E tests for complete user workflows login, CRUD operations, multi-page flows
  • Creating critical path regression tests that validate the full stack
  • Testing cross-browser compatibility Chromium, Firefox, WebKit
  • Validating authentication flows end-to-end
  • Testing file upload/download workflows

E2e Testing by the numbers

  • 1,629 all-time installs (skills.sh)
  • +3 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #459 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)
At a glance

e2e-testing capabilities & compatibility

Capabilities
writing e2e tests for complete user workflows lo · creating critical path regression tests that val · testing cross browser compatibility chromium, fi · validating authentication flows end to end · testing file upload/download workflows
Use cases
testing · debugging · documentation
From the docs

What e2e-testing says it does

End-to-end testing patterns with Playwright for full-stack Python/React applications.
SKILL.md
npx skills add https://github.com/hieutrtr/ai1-skills --skill e2e-testing

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1.6k
repo stars8
Security audit3 / 3 scanners passed
Last updatedFebruary 6, 2026
Repositoryhieutrtr/ai1-skills

What problem does e2e-testing solve for developers using the documented workflows?

-

Who is it for?

Developers working with e2e-testing patterns described in the skill documentation.

Skip if: Skip when docs are empty or the task is outside the skill documented scope.

When should I use this skill?

Use when -

What you get

Actionable e2e-testing guidance grounded in SKILL.md workflows and reference files.

  • Playwright spec files
  • Page object modules
  • API test helper utilities

Files

SKILL.mdMarkdownGitHub ↗

E2E Testing

When to Use

Activate this skill when:

  • Writing E2E tests for complete user workflows (login, CRUD operations, multi-page flows)
  • Creating critical path regression tests that validate the full stack
  • Testing cross-browser compatibility (Chromium, Firefox, WebKit)
  • Validating authentication flows end-to-end
  • Testing file upload/download workflows
  • Writing smoke tests for deployment verification

Do NOT use this skill for:

  • React component unit tests (use react-testing-patterns)
  • Python backend unit/integration tests (use pytest-patterns)
  • TDD workflow enforcement (use tdd-workflow)
  • API contract testing without a browser (use pytest-patterns with httpx)

Instructions

Test Structure

e2e/
├── playwright.config.ts         # Global Playwright configuration
├── fixtures/
│   ├── auth.fixture.ts          # Authentication state setup
│   └── test-data.fixture.ts     # Test data creation/cleanup
├── pages/
│   ├── base.page.ts             # Base page object with shared methods
│   ├── login.page.ts            # Login page object
│   ├── users.page.ts            # Users list page object
│   └── user-detail.page.ts     # User detail page object
├── tests/
│   ├── auth/
│   │   ├── login.spec.ts
│   │   └── logout.spec.ts
│   ├── users/
│   │   ├── create-user.spec.ts
│   │   ├── edit-user.spec.ts
│   │   └── list-users.spec.ts
│   └── smoke/
│       └── critical-paths.spec.ts
└── utils/
    ├── api-helpers.ts           # Direct API calls for test setup
    └── test-constants.ts        # Shared constants

Naming conventions:

  • Test files: <feature>.spec.ts
  • Page objects: <page-name>.page.ts
  • Fixtures: <concern>.fixture.ts
  • Test names: human-readable sentences describing the user action and expected outcome

Page Object Model

Every page gets a page object class that encapsulates selectors and actions. Tests never interact with selectors directly.

Base page object:

// e2e/pages/base.page.ts
import { type Page, type Locator } from "@playwright/test";

export abstract class BasePage {
  constructor(protected readonly page: Page) {}

  /** Navigate to the page's URL. */
  abstract goto(): Promise<void>;

  /** Wait for the page to be fully loaded. */
  async waitForLoad(): Promise<void> {
    await this.page.waitForLoadState("networkidle");
  }

  /** Get a toast/notification message. */
  get toast(): Locator {
    return this.page.getByRole("alert");
  }

  /** Get the page heading. */
  get heading(): Locator {
    return this.page.getByRole("heading", { level: 1 });
  }
}

Concrete page object:

// e2e/pages/users.page.ts
import { type Page, type Locator } from "@playwright/test";
import { BasePage } from "./base.page";

export class UsersPage extends BasePage {
  // ─── Locators ─────────────────────────────────────────
  readonly createButton: Locator;
  readonly searchInput: Locator;
  readonly userTable: Locator;

  constructor(page: Page) {
    super(page);
    this.createButton = page.getByTestId("create-user-btn");
    this.searchInput = page.getByRole("searchbox", { name: /search users/i });
    this.userTable = page.getByRole("table");
  }

  // ─── Actions ──────────────────────────────────────────
  async goto(): Promise<void> {
    await this.page.goto("/users");
    await this.waitForLoad();
  }

  async searchFor(query: string): Promise<void> {
    await this.searchInput.fill(query);
    // Wait for search results to update (debounced)
    await this.page.waitForResponse("**/api/v1/users?*");
  }

  async clickCreateUser(): Promise<void> {
    await this.createButton.click();
  }

  async getUserRow(email: string): Promise<Locator> {
    return this.userTable.getByRole("row").filter({ hasText: email });
  }

  async getUserCount(): Promise<number> {
    // Subtract 1 for header row
    return (await this.userTable.getByRole("row").count()) - 1;
  }
}

Rules for page objects:

  • One page object per page or major UI section
  • Locators are public readonly properties
  • Actions are async methods
  • Page objects never contain assertions -- tests assert
  • Page objects handle waits internally after actions

Selector Strategy

Priority order (highest to lowest):

PrioritySelectorExampleWhen to Use
1data-testidgetByTestId("submit-btn")Interactive elements, dynamic content
2RolegetByRole("button", { name: /save/i })Buttons, links, headings, inputs
3LabelgetByLabel("Email")Form inputs with labels
4PlaceholdergetByPlaceholder("Search...")Search inputs
5TextgetByText("Welcome back")Static text content

NEVER use:

  • CSS selectors (.class-name, #id) -- brittle, break on styling changes
  • XPath (//div[@class="foo"]) -- unreadable, extremely brittle
  • DOM structure selectors (div > span:nth-child(2)) -- break on layout changes

Adding data-testid attributes:

// In React components -- add data-testid to interactive elements
<button data-testid="create-user-btn" onClick={handleCreate}>
  Create User
</button>

// Convention: kebab-case, descriptive
// Pattern: <action>-<entity>-<element-type>
// Examples: create-user-btn, user-email-input, delete-confirm-dialog

Wait Strategies

NEVER use hardcoded waits:

// BAD: Hardcoded wait -- flaky, slow
await page.waitForTimeout(3000);

// BAD: Sleep
await new Promise((resolve) => setTimeout(resolve, 2000));

Use explicit wait conditions:

// GOOD: Wait for a specific element to appear
await page.getByRole("heading", { name: "Dashboard" }).waitFor();

// GOOD: Wait for navigation
await page.waitForURL("/dashboard");

// GOOD: Wait for API response
await page.waitForResponse(
  (response) =>
    response.url().includes("/api/v1/users") && response.status() === 200,
);

// GOOD: Wait for network to settle
await page.waitForLoadState("networkidle");

// GOOD: Wait for element state
await page.getByTestId("submit-btn").waitFor({ state: "visible" });
await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });

Auto-waiting: Playwright auto-waits for elements to be actionable before clicking, filling, etc. Explicit waits are needed only for assertions or complex state transitions.

Auth State Reuse

Avoid logging in before every test. Save auth state and reuse it.

Setup auth state once:

// e2e/fixtures/auth.fixture.ts
import { test as base } from "@playwright/test";
import path from "path";

const AUTH_STATE_PATH = path.resolve("e2e/.auth/user.json");

export const setup = base.extend({});

setup("authenticate", async ({ page }) => {
  // Perform real login
  await page.goto("/login");
  await page.getByLabel("Email").fill("testuser@example.com");
  await page.getByLabel("Password").fill("TestPassword123!");
  await page.getByRole("button", { name: /sign in/i }).click();

  // Wait for auth to complete
  await page.waitForURL("/dashboard");

  // Save signed-in state
  await page.context().storageState({ path: AUTH_STATE_PATH });
});

Reuse in tests:

// playwright.config.ts
export default defineConfig({
  projects: [
    // Setup project runs first and saves auth state
    { name: "setup", testDir: "./e2e/fixtures", testMatch: "auth.fixture.ts" },
    {
      name: "chromium",
      use: {
        storageState: "e2e/.auth/user.json",  // Reuse auth state
      },
      dependencies: ["setup"],
    },
  ],
});

Test Data Management

Principles:

  • Tests create their own data (never depend on pre-existing data)
  • Tests clean up after themselves (or use API to reset)
  • Use API calls for setup, not UI interactions (faster, more reliable)

API helpers for test data:

// e2e/utils/api-helpers.ts
import { type APIRequestContext } from "@playwright/test";

export class TestDataAPI {
  constructor(private request: APIRequestContext) {}

  async createUser(data: { email: string; displayName: string }) {
    const response = await this.request.post("/api/v1/users", { data });
    return response.json();
  }

  async deleteUser(userId: number) {
    await this.request.delete(`/api/v1/users/${userId}`);
  }

  async createOrder(userId: number, items: Array<Record<string, unknown>>) {
    const response = await this.request.post("/api/v1/orders", {
      data: { user_id: userId, items },
    });
    return response.json();
  }
}

Usage in tests:

test("edit user name", async ({ page, request }) => {
  const api = new TestDataAPI(request);

  // Setup: create user via API (fast)
  const user = await api.createUser({
    email: "edit-test@example.com",
    displayName: "Before Edit",
  });

  try {
    // Test: edit via UI
    const usersPage = new UsersPage(page);
    await usersPage.goto();
    // ... perform edit via UI ...
  } finally {
    // Cleanup: remove test data
    await api.deleteUser(user.id);
  }
});

Debugging Flaky Tests

1. Use trace viewer for failures:

// playwright.config.ts
use: {
  trace: "on-first-retry",  // Capture trace only on retry
}

View trace: npx playwright show-trace trace.zip

2. Run in headed mode for debugging:

npx playwright test --headed --debug tests/users/create-user.spec.ts

3. Common causes of flaky tests:

CauseFix
Hardcoded waitsUse explicit wait conditions
Shared test dataEach test creates its own data
Animation interferenceSet animations: "disabled" in config
Race conditionsWait for API responses before assertions
Viewport-dependent behaviorSet explicit viewport in config
Session leaks between testsUse storageState correctly, clear cookies

4. Retry strategy:

// playwright.config.ts
export default defineConfig({
  retries: process.env.CI ? 2 : 0,  // Retry in CI only
});

CI Configuration

# .github/workflows/e2e.yml
name: E2E Tests
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Start application
        run: |
          docker compose up -d
          npx wait-on http://localhost:3000 --timeout 60000

      - name: Run E2E tests
        run: npx playwright test

      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

      - name: Upload traces on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-traces
          path: test-results/

Use scripts/run-e2e-with-report.sh to run Playwright with HTML report output locally.

Examples

See references/page-object-template.ts for annotated page object class. See references/e2e-test-template.ts for annotated E2E test. See references/playwright-config-example.ts for production Playwright config.

Related skills

Forks & variants (1)

E2e Testing has 1 known copy in the catalog totaling 71 installs. They canonicalize to this original listing.

How it compares

Choose this for opinionated Playwright page-object and API-fixture templates instead of bare Playwright codegen snippets.

FAQ

Who is e2e-testing for?

Developers and software engineers working with e2e-testing patterns described in the skill documentation.

When should I use e2e-testing?

When -.

Is e2e-testing safe to install?

Review the Security Audits panel on this page before installing in production.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.