
E2e Testing
Bootstrap Playwright end-to-end tests with page objects, API fixtures, and auth reuse so agents write tests that match your repo layout.
Overview
E2E Testing is an agent skill for the Ship phase that provides an annotated Playwright template using page objects, API helpers, and explicit waits for reliable browser tests.
Install
npx skills add https://github.com/hieutrtr/ai1-skills --skill e2e-testingWhat is this skill?
- Annotated Playwright spec template at e2e/tests/users/create-user.spec.ts
- Page objects only—tests avoid raw selectors (UsersPage, CreateUserDialog)
- API-based TestDataAPI for fast setup and reliable teardown
- Explicit waits—no hardcoded sleep timeouts
- Covers success, error, and edge paths with storageState auth reuse
Adoption & trust: 1.6k installs on skills.sh; 8 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your agent writes flaky E2E tests that inline selectors, use fixed timeouts, and leave test data behind after runs.
Who is it for?
Indie SaaS teams standardizing Playwright structure when scaffolding create-user or similar critical-path specs.
Skip if: Unit or integration-only projects, non-TypeScript stacks without Playwright, or one-off manual QA checklists.
When should I use this skill?
User needs Playwright E2E test structure, page objects, API-based setup or cleanup, or annotated create-flow specs.
What do I get? / Deliverables
You get copy-ready Playwright specs organized with page objects, API setup or cleanup, and auth storageState patterns you can extend per feature.
- Annotated .spec.ts E2E test file following the template
- Page object and TestDataAPI usage pattern for new flows
Recommended Skills
Journey fit
How it compares
A concrete test template skill—not a full test runner MCP server or generic “write any test” chat prompt.
Common Questions / FAQ
Who is e2e-testing for?
Solo builders and small teams shipping web apps who want Claude or Cursor to follow a consistent Playwright layout with page objects and API helpers.
When should I use e2e-testing?
In Ship testing when adding Playwright coverage for user flows, before release regression, or when refactoring specs away from inline selectors.
Is e2e-testing safe to install?
It is source template code; review the Security Audits panel on this page and inspect the skill files before agents run shell or browser commands against your app.
SKILL.md
READMESKILL.md - E2e Testing
/** * e2e-test-template.ts — Annotated E2E test using Playwright. * * Place at: e2e/tests/users/create-user.spec.ts * * This template demonstrates: * - Using page objects (tests never touch selectors directly) * - API-based test data setup (fast) and cleanup (reliable) * - Explicit waits (never hardcoded timeouts) * - Testing success paths, error paths, and edge cases * - Auth state reuse via storageState */ import { test, expect } from "@playwright/test"; import { UsersPage, CreateUserDialog } from "../pages/users.page"; import { TestDataAPI } from "../utils/api-helpers"; // ─── Test Suite: Create User ──────────────────────────────────────────────────── test.describe("Create User", () => { let usersPage: UsersPage; let api: TestDataAPI; test.beforeEach(async ({ page, request }) => { // Initialize page objects and API helpers usersPage = new UsersPage(page); api = new TestDataAPI(request); // Navigate to the users page (auth state is pre-loaded via storageState) await usersPage.goto(); }); // ─── Success Path ─────────────────────────────────── test("creates a new user with valid data", async ({ page }) => { // Arrange: open the create dialog await usersPage.clickCreateUser(); const dialog = new CreateUserDialog(page); // Act: fill and submit the form await dialog.fillForm({ email: `e2e-create-${Date.now()}@example.com`, displayName: "E2E Test User", role: "member", }); await dialog.submit(); // Assert: success toast appears await expect(usersPage.toast).toContainText(/created successfully/i); // Assert: user appears in the list await expect( usersPage.getUserRow(`e2e-create-${Date.now()}@example.com`), ).toBeVisible(); }); // ─── Validation Error Path ────────────────────────── test("shows validation error for invalid email", async ({ page }) => { await usersPage.clickCreateUser(); const dialog = new CreateUserDialog(page); // Act: submit with invalid email await dialog.fillForm({ email: "not-an-email", displayName: "Invalid Email User", }); await dialog.submitButton.click(); // Assert: validation error appears (form is NOT submitted) const errorMessage = page.getByText(/invalid email/i); await expect(errorMessage).toBeVisible(); }); // ─── Duplicate Error Path ────────────────────────── test("shows error when creating user with duplicate email", async ({ page, request, }) => { const duplicateEmail = `e2e-dup-${Date.now()}@example.com`; // Arrange: create a user via API first (fast setup) const existingUser = await api.createUser({ email: duplicateEmail, displayName: "Existing User", }); try { // Act: try to create another user with the same email via UI await usersPage.clickCreateUser(); const dialog = new CreateUserDialog(page); await dialog.fillForm({ email: duplicateEmail, displayName: "Duplicate User", }); await dialog.submitButton.click(); // Assert: error toast with conflict message await expect(usersPage.toast).toContainText(/already exists/i); } finally { // Cleanup: remove the test user await api.deleteUser(existingUser.id); } }); // ─── Cancel Flow ──────────────────────────────────── test("cancelling the dialog does not create a user", async ({ page }) => { const countBefore = await usersPage.getUserCount(); // Act: open dialog, fill form, then cancel await usersPage.clickCreateUser(); const dialog = new CreateUserDialog(page); await dialog.fillForm({ email: "should-not-exist@example.com", displayName: "Cancelled User", }); await dialog.cancel(); // Assert: user count unchanged const countAfter = await usersPage.getUserCount(); expect(countAfter).toBe(countBefore); }); }); // ─── Test Suite: User List ────────────────────────────