Skip to content
Back to skills

Create E2e Test

ASecurity

Create end-to-end tests using Playwright with page objects and fixtures following project conventions. Use when the user asks to add e2e tests for a feature or user flow.

  • 45 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 7, 2026
testingtypescriptgobash

Works with

  • cli

Security analysis

A100/100

Scanned September 30, 2026

npx -y skills add incubateur-ademe/benefriches --skill create-e2e-test --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Create E2e Test?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Create E2e Test
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/incubateur-ademe-create-e2e-test/badge)](https://www.skillsdirectory.com/skills/incubateur-ademe-create-e2e-test)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: create-e2e-test
description: Create end-to-end tests using Playwright with page objects and fixtures following project conventions. Use when the user asks to add e2e tests for a feature or user flow.
effort: medium
allowed-tools: Bash, Read, Write, Glob, Grep
user-invocable: true
---

# Create E2E Test

Create end-to-end tests for the user flow described below.

## Instructions

### 1. Understand the User Flow

First, analyze the `User Flow` to identify:
- **Entry point**: Which page/route does the flow start from?
- **Steps**: What user actions are involved (clicks, form fills, navigation)?
- **Assertions**: What should be verified at each step or at the end?
- **Prerequisites**: Does the user need to be authenticated? Any setup data needed?

### 2. Explore Existing Patterns

Before writing code, explore relevant existing files:

```
apps/e2e-tests/
├── pages/                    # Page Objects - check for reusable pages
├── fixtures/                 # Base fixtures (auth.fixtures.ts)
└── tests/
    └── [feature]/            # Existing test patterns
        ├── [feature].fixtures.ts
        └── [feature].spec.ts
```

**Key files to reference**:
- `apps/e2e-tests/fixtures/auth.fixtures.ts` - Authentication fixtures
- `apps/e2e-tests/pages/SiteCreationPage.ts` - Page object pattern example
- `apps/e2e-tests/tests/site-creation/site-creation.fixtures.ts` - Fixture composition example

### 3. Create/Update Page Objects

For each page/screen in the flow, create or update a page object in `apps/e2e-tests/pages/`:

**Page Object Pattern**:
```typescript
import { expect, type Page } from "@playwright/test";

export class FeaturePage {
  readonly page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  // Navigation
  async goto(): Promise<void> {
    await this.page.goto("/route-path");
  }

  // Assertions (prefix with expect*)
  async expectStepVisible(): Promise<void> {
    await expect(this.page.getByRole("heading", { name: "Step Title" })).toBeVisible();
  }

  // User Actions (verb-based names)
  async clickButton(): Promise<void> {
    await this.page.getByRole("button", { name: "Button Text" }).click();
  }

  async fillInput(value: string): Promise<void> {
    await this.page.getByLabel("Input Label").fill(value);
  }

  async selectOption(value: string): Promise<void> {
    await this.page.getByRole("radio", { name: value }).check({ force: true });
    await this.submit();
  }

  // Private helpers
  private async submit(): Promise<void> {
    await this.page.getByRole("button", { name: /Valider|Suivant/ }).click();
  }
}
```

**Multi-step wizards**: assert each step's title as you walk through it (`expectStepTitle(title)` — see `apps/e2e-tests/pages/PhotovoltaicProjectCreationPage.ts` for the pattern). Find each title's exact text with `grep -rn "WizardFormLayout title" apps/web/src` rather than guessing — many step components (e.g. create-mode selection, reinstatement question) are shared across wizards, so the same title string applies in multiple flows.

**Selector Priority** (accessibility-first):
1. `getByRole()` - buttons, links, headings, textboxes
2. `getByLabel()` - form inputs with labels
3. `getByText()` - visible text content
4. `locator()` - CSS selectors (last resort)

### 4. Create Feature Fixtures

Create `apps/e2e-tests/tests/[feature]/[feature].fixtures.ts`:

```typescript
import { test as authTest } from "../../fixtures/auth.fixtures";
import { FeaturePage } from "../../pages/FeaturePage";

type FeatureFixtures = {
  featurePage: FeaturePage;
};

export const test = authTest.extend<FeatureFixtures>({
  featurePage: async ({ authenticatedPage }, use) => {
    const featurePage = new FeaturePage(authenticatedPage);
    await use(featurePage);
  },
});

export { expect } from "@playwright/test";
```

**If authentication is NOT needed**, extend from base Playwright test instead:
```typescript
import { test as base, expect } from "@playwright/test";
```

### 5. Write Test File

Create `apps/e2e-tests/tests/[feature]/[feature].spec.ts`:

```typescript
import { test } from "./[feature].fixtures";

test.describe("feature name", () => {
  test("describes what the user can do", async ({ featurePage }) => {
    // Navigate to starting point
    await featurePage.goto();

    // Step 1: Action + optional assertion
    await featurePage.expectStepVisible();
    await featurePage.clickStart();

    // Step 2: Another action
    await featurePage.fillInput("value");
    await featurePage.submit();

    // Final assertions
    await featurePage.expectSuccessMessage();
  });
});
```

**Test Naming**: Use descriptive names that explain what the user can do:
- `"allows authenticated user to create a new project"`
- `"shows error when required field is empty"`
- `"redirects to login when not authenticated"`

### 6. Run and Verify

Run `pnpm --filter e2e-tests typecheck`, then run the new spec with the `run-e2e-tests` skill (`/run-e2e-tests tests/[feature]/`): it starts the stack, runs the tests and stops the stack.

## File Checklist

Before completing, ensure you have created/updated:

- [ ] Page object(s) in `apps/e2e-tests/pages/[PageName].ts`
- [ ] Fixtures in `apps/e2e-tests/tests/[feature]/[feature].fixtures.ts`
- [ ] Test file in `apps/e2e-tests/tests/[feature]/[feature].spec.ts`
- [ ] Tests pass: `/run-e2e-tests tests/[feature]/`
- [ ] Type check passes: `pnpm --filter e2e-tests typecheck`

## Patterns Reference

### Using Shared Types
```typescript
import type { SiteNature, FricheActivity } from "shared";
import { getLabelForNaturalAreaType } from "shared";
```

### Waiting for Async Content
```typescript
// Wait for element to appear
await this.page.getByRole("option").first().waitFor({ state: "visible", timeout: 10000 });

// Wait for navigation
await expect(this.page).toHaveURL("/expected-path");
```

### Form Autocomplete
```typescript
async fillAutocomplete(searchText: string): Promise<void> {
  const input = this.page.getByRole("searchbox", { name: /Label/i });
  await input.pressSequentially(searchText, { delay: 50 });
  const firstOption = this.page.getByRole("option").first();
  await firstOption.waitFor({ state: "visible", timeout: 10000 });
  await firstOption.click();
}
```

### Data Verification
```typescript
async expectDataInList(expectedData: [label: string, value: string][]): Promise<void> {
  for (const [label, value] of expectedData) {
    await expect(
      this.page.locator("dl").filter({ hasText: label }).locator("dt")
    ).toHaveText(value);
  }
}
```

---

## User Flow

$ARGUMENTS

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.