kvnwolf

create-e2e-test

Write end-to-end tests with Playwright Test for full user flow verification. Consult this skill whenever testing complete user flows, verifying multi-page interactions, testing authentication flows, or creating E2E tests that exercise the full application stack.

kvnwolf 4 Updated 3mo ago
GitHub

Install

npx skillscat add kvnwolf/devtools/create-e2e-test

Install via the SkillsCat registry.

SKILL.md

E2E Tests

Write end-to-end tests with Playwright Test for full user flow verification. E2E tests exercise the real application — real server, real browser, real data.

Convention

E2E tests live in e2e/ at the project root, NOT co-located with source files. They are separate from unit and component tests.

Run with bun run test:e2e (not included in validate — run separately).

Imports

import { expect, test } from "@playwright/test";

Core Patterns

Navigation

test("navigates to about page", async ({ page }) => {
  await page.goto("/");
  await page.getByRole("link", { name: "About" }).click();
  await expect(page).toHaveURL("/about");
  await expect(page.getByRole("heading")).toHaveText("About Us");
});

Form Submission

test("creates a new user", async ({ page }) => {
  await page.goto("/users/new");

  await page.getByLabel("Name").fill("Alice");
  await page.getByLabel("Email").fill("alice@test.com");
  await page.getByRole("button", { name: "Create" }).click();

  await expect(page).toHaveURL(/\/users\/.+/);
  await expect(page.getByText("Alice")).toBeVisible();
});

Data CRUD Flow

test("creates, edits, and deletes an item", async ({ page }) => {
  // Create
  await page.goto("/items");
  await page.getByRole("button", { name: "New Item" }).click();
  await page.getByLabel("Name").fill("Test Item");
  await page.getByRole("button", { name: "Save" }).click();
  await expect(page.getByText("Test Item")).toBeVisible();

  // Edit
  await page.getByText("Test Item").click();
  await page.getByLabel("Name").fill("Updated Item");
  await page.getByRole("button", { name: "Save" }).click();
  await expect(page.getByText("Updated Item")).toBeVisible();

  // Delete
  await page.getByRole("button", { name: "Delete" }).click();
  await page.getByRole("button", { name: "Confirm" }).click();
  await expect(page.getByText("Updated Item")).not.toBeVisible();
});

Auth Persistence

Login once and reuse the session across tests:

// e2e/auth.setup.ts
import { test as setup } from "@playwright/test";

const AUTH_FILE = "e2e/.auth/user.json";

setup("authenticate", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("Email").fill("test@test.com");
  await page.getByLabel("Password").fill("password");
  await page.getByRole("button", { name: "Sign in" }).click();
  await page.waitForURL("/dashboard");
  await page.context().storageState({ path: AUTH_FILE });
});

Configure in playwright.config.ts:

export default defineConfig({
  projects: [
    { name: "setup", testMatch: /auth\.setup\.ts/ },
    {
      name: "tests",
      dependencies: ["setup"],
      use: { storageState: "e2e/.auth/user.json" },
    },
  ],
});

When E2E vs Component Test

Scenario Test Type
Multi-page user flow E2E
Server-side rendering verification E2E
Authentication and authorization E2E
Isolated UI component behavior Component test
Form validation feedback Component test
Component state management Component test
API integration across pages E2E

Rule of thumb: If the test needs the real server or navigates between pages, use E2E. If it tests isolated UI behavior, use a component test.