К основному содержанию
скилл CX Codex

e2e-testing · hieutrtr — скилл для Claude Code: что делает и как установить

Задаёт шаблоны E2E-тестирования на Playwright для веб-приложений на React и Python. Помогает строить надёжные сквозные сценарии и Page Objects.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add hieutrtr/ai1-skills --skill e2e-testing -a claude-code
Открыть на GitHub

Что делает e2e-testing

Скилл внедряет в проект стандарты сквозного тестирования на базе Playwright. Он задаёт структуру каталога e2e/, создаёт базовые и дочерние классы Page Object Model, изолирует локаторы от проверок и настраивает работу с фикстурами авторизации.

В правилах определена иерархия поиска элементов: приоритет отдаётся data-testid и семантическим ARIA-ролям. Использовать селекторы CSS и XPath запрещено, так как они ломаются при изменениях вёрстки. Для синхронизации применяются явные ожидания ответов сети и состояния элементов. Фиксированные паузы через waitForTimeout полностью исключены.

Для работы требуются Node.js 20+ и Playwright 1.40+. Скилл рассчитан на сквозные сценарии в браузере и не подходит для изолированных модульных тестов компонентов React или бэкенда на Python.

Какие задачи решает

  • написать сквозной тест авторизации и сценариев CRUD
  • построить классы Page Object Model без прямых проверок внутри
  • заменить хрупкие CSS-селекторы на data-testid и ARIA-роли
  • настроить ожидания сетевых ответов вместо фиксированных пауз

Примеры запросов агенту

  • Создай Page Object и E2E-тест Playwright для формы создания пользователя
  • Перепиши тесты в e2e/tests/auth/, используя фикстуру авторизации и data-testid
  • Добавь сквозную проверку поиска пользователей с ожиданием сетевого ответа API

Как установить e2e-testing

1Через Skills CLI (Vercel)

npx skills add hieutrtr/ai1-skills --skill e2e-testing -a claude-code

2Вручную: положить файлы

~/.claude/skills/e2e-testing/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/e2e-testing/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о e2e-testing

Как установить e2e-testing в Claude Code?

Выполните команду npx skills add hieutrtr/ai1-skills --skill e2e-testing -a claude-code. Также агент может прочитать инструкции напрямую из файла ~/.claude/skills/e2e-testing/SKILL.md.

Что делает скилл e2e-testing?

Он направляет ИИ-агента при создании тестов Playwright по шаблону Page Object Model, организует папки и внедряет строгие правила ожидания элементов.

Подходит ли скилл для юнит-тестов React или Python?

Нет, он предназначен исключительно для полных сценариев в браузере. Для тестов компонентов React и тестов бэкенда на Python предусмотрены отдельные наборы правил.

Исходный текст

SKILL.md · MIT
---
name: e2e-testing
description: >-
  End-to-end testing patterns with Playwright for full-stack Python/React applications.
  Use when writing E2E tests for complete user workflows (login, CRUD, navigation),
  critical path regression tests, or cross-browser validation. Covers test structure,
  page object model, selector strategy (data-testid > role > label), wait strategies,
  auth state reuse, test data management, and CI integration. Does NOT cover unit tests
  or component tests (use pytest-patterns or react-testing-patterns).
license: MIT
compatibility: 'Playwright 1.40+, Node.js 20+'
metadata:
  author: platform-team
  version: '1.0.0'
  sdlc-phase: testing
allowed-tools: Read Edit Write Bash(npx:*) Bash(npm:*)
context: fork
---

# 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:**
```typescript
// 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:**
```typescript
// 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):**

| Priority | Selector | Example | When to Use |
|----------|----------|---------|-------------|
| 1 | `data-testid` | `getByTestId("submit-btn")` | Interactive elements, dynamic content |
| 2 | Role | `getByRole("button", { name: /save/i })` | Buttons, links, headings, inputs |
| 3 | Label | `getByLabel("Email")` | Form inputs with labels |
| 4 | Placeholder | `getByPlaceholder("Search...")` | Search inputs |
| 5 | Text | `getByText("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:**
```tsx
// 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:**
```typescript
// BAD: Hardcoded wait -- flaky, slow
await page.waitForTimeout(3000);

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

**Use explicit wait conditions:**
```typescript
// 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:**
```typescript
// 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("[email protected]");
  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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
test("edit user name", async ({ page, request }) => {
  const api = new TestDataAPI(request);

  // Setup: create user via API (fast)
  const user = await api.createUser({
    email: "[email protected]",
    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:**
```typescript
// 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:**
```bash
npx playwright test --headed --debug tests/users/create-user.spec.ts
```

**3. Common causes of flaky tests:**
| Cause | Fix |
|-------|-----|
| Hardcoded waits | Use explicit wait conditions |
| Shared test data | Each test creates its own data |
| Animation interference | Set `animations: "disabled"` in config |
| Race conditions | Wait for API responses before assertions |
| Viewport-dependent behavior | Set explicit viewport in config |
| Session leaks between tests | Use `storageState` correctly, clear cookies |

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

### CI Configuration

```yaml
# .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.

© platform-team · MIT · hieutrtr/ai1-skills

Похожие скиллы

Все скиллы по теме «Тесты и QA» →

Сначала — e2e-testing от других авторов.

e2e-testing affaan-m Шаблоны для E2E-тестов на Playwright: настройка Page Object Model, конфигов запуска, изоляция флакующих сценариев и выгрузка артефактов в CI/CD. CX Codex +1 5 100установок improve-codebase-architecture Matt Pocock Скилл ищет архитектурные узкие места в кодовой базе и показывает варианты углубления модулей в HTML-отчёте. Подойдёт для ревью перед рефакторингом. 1 163 035установок design-taste-frontend Leonxlnx Скилл задаёт арт-дирекцию для публичных страниц и рабочих экранов, а затем проверяет результат в браузере по скриншотам. Нужен, когда интерфейс выглядит шаблонным или недоработанным. CX Codex +1 678 700установок tdd Matt Pocock Скилл управляет циклом красный-зелёный в TDD: проверяет, что тесты пишутся до кода, держит их на уровне публичных интерфейсов и не завязывает на внутреннюю структуру. 1 141 062установок skill-creator официальный anthropics Помогает создать новый скилл, доработать существующий и проверить результат через тестовые промпты и оценки. 414 180установок agent-browser vercel-labs Скилл управляет браузером через CLI: открывает страницы, заполняет формы, нажимает кнопки и проверяет веб-приложения. Подходит для задач, где агенту нужно действовать в интерфейсе. CX Codex +1 1 200 000установок prototype Matt Pocock Создаёт временный прототип, чтобы проверить логику, модель состояния или вид интерфейса до работы над основным кодом. 986 201установок

Из того же набора2

Спонсоры проекта

Компании, которые помогают держать аналитику открытой