agentsclimarketplace

E2etesting

Skill jjmendezrodriguez/jm-claude-plugin/skills/e2etesting

End-to-end testing with Playwright for TypeScript/React applications. Use this when setting up Playwright, writing E2E specs, building Page Object Models, choosing selectors, configuring projects (Chromium/Firefox/WebKit), or wiring E2E into CI. Trigger on: "add E2E test", "Playwright setup", "test the login flow", "POM", "data-testid", any critical user-flow test (login/signup/checkout/payment), or any flaky-E2E investigation.From its SKILL.md

Install
npx -y skills add jjmendezrodriguez/jm-claude-plugin --skill e2etesting

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.9 KB, as published. Nobody here has run it

E2E Testing — Playwright / TypeScript

Staff Engineer standard: E2E tests cover critical flows only — they're expensive, so spend them where unit tests can't reach.


1. When to Reach for Playwright

✅ USE FOR:                          ❌ DON'T USE FOR:
- Critical user flows                 - Logic unit tests (use Vitest)
  (login, signup, checkout, payment)  - Component-level tests (use RTL)
- Cross-browser regressions           - Pure functions (use Vitest)
- Auth + session persistence          - Anything you can mock cheaply
- Visual regression (snapshots)

Rule of thumb: if breakage would page someone at 2am, it deserves an E2E test.


2. Installation & Structure

pnpm create playwright@latest
tests/e2e/
├── playwright.config.ts     # Config (browsers, base URL, retries)
├── fixtures/                # Test data, auth helpers
├── pages/                   # Page Object Models
│   ├── login.page.ts
│   └── dashboard.page.ts
└── specs/                   # Test files
    ├── auth.spec.ts
    └── checkout.spec.ts

POMs separate what the page is from what the test does. Without POMs, refactoring a form selector means editing every spec.


3. Configuration

// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./tests/e2e/specs",
  timeout: 30_000,
  retries: process.env.CI ? 2 : 0,   // retry only in CI — local flakes need fixing
  workers: process.env.CI ? 1 : undefined,
  reporter: [["html"], ["list"]],

  use: {
    baseURL: process.env.BASE_URL ?? "http://localhost:3000",
    screenshot: "only-on-failure",
    video: "retain-on-failure",
    trace: "on-first-retry",
  },

  projects: [
    { name: "chromium", use: { ...devices["Desktop Chrome"] } },
    { name: "firefox",  use: { ...devices["Desktop Firefox"] } },
    { name: "webkit",   use: { ...devices["Desktop Safari"] } },
  ],
});

Why these defaults:

  • retries: 2 in CI hides infra flakes (network, cold start); locally 0 forces you to fix the spec
  • trace: "on-first-retry" — full timeline + DOM snapshots, only when needed (large files)
  • workers: 1 in CI avoids race conditions on shared backend state

4. Page Object Model

// pages/login.page.ts
import { Page, Locator, expect } from "@playwright/test";

export class LoginPage {
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(private readonly page: Page) {
    this.emailInput    = page.getByTestId("email-input");
    this.passwordInput = page.getByTestId("password-input");
    this.submitButton  = page.getByRole("button", { name: /sign in/i });
    this.errorMessage  = page.getByTestId("error-message");
  }

  async goto() {
    await this.page.goto("/login");
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectError(message: string | RegExp) {
    await expect(this.errorMessage).toContainText(message);
  }
}

Rules:

  • One POM per page or major section
  • Locators live in the constructor — refactor-friendly
  • Methods describe user intent (login()), not mechanics (clickAndType())

5. Writing Specs

// specs/auth.spec.ts
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/login.page";

test.describe("Authentication", () => {
  test("successful login redirects to dashboard", async ({ page }) => {
    const login = new LoginPage(page);
    await login.goto();
    await login.login("[email protected]", "validPassword123");

    await expect(page).toHaveURL("/dashboard");
    await expect(page.getByRole("heading", { name: /welcome/i })).toBeVisible();
  });

  test("invalid credentials show error", async ({ page }) => {
    const login = new LoginPage(page);
    await login.goto();
    await login.login("[email protected]", "wrongPassword");

    await login.expectError(/invalid credentials/i);
  });
});

6. Selector Priority

// ✅ BEST — stable, intentional
page.getByTestId("submit-button");

// ✅ GOOD — accessibility-friendly, survives copy changes via name regex
page.getByRole("button", { name: /submit/i });

// ⚠️ OK — for unique static text
page.getByText("Submit order");

// ❌ AVOID — couples test to styling
page.locator(".btn-primary-xl");

// ❌ AVOID — couples test to DOM structure
page.locator("form > div:nth-child(3) > button");

Pattern: add data-testid to interactive elements as you build them — retrofitting tests later is twice the work.


7. Auth Reuse — Avoid Logging In Every Test

// playwright.config.ts
projects: [
  {
    name: "setup",
    testMatch: /global\.setup\.ts/,
  },
  {
    name: "chromium",
    use: { ...devices["Desktop Chrome"], storageState: "playwright/.auth/user.json" },
    dependencies: ["setup"],
  },
],
// global.setup.ts — runs once, persists session
import { test as setup } from "@playwright/test";

setup("authenticate", async ({ page }) => {
  await page.goto("/login");
  await page.getByTestId("email-input").fill(process.env.TEST_EMAIL!);
  await page.getByTestId("password-input").fill(process.env.TEST_PASSWORD!);
  await page.getByRole("button", { name: /sign in/i }).click();
  await page.waitForURL("/dashboard");
  await page.context().storageState({ path: "playwright/.auth/user.json" });
});

Cuts spec time by ~80% on auth-heavy suites.


8. Running Tests

pnpm exec playwright test                  # All specs, all browsers
pnpm exec playwright test auth.spec.ts     # Single file
pnpm exec playwright test --headed         # Watch the browser
pnpm exec playwright test --debug          # Inspector + step-through
pnpm exec playwright test --ui             # Time-travel UI (best DX)
pnpm exec playwright show-report           # HTML report after run

9. CI Integration

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

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile
      - run: pnpm exec playwright install --with-deps
      - run: pnpm exec playwright test
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7

Artifacts on failure only — passing runs don't need 50MB of video evidence.


10. Flaky Test Triage

1. Reproduce locally with --repeat-each=10
2. Open trace: pnpm exec playwright show-trace trace.zip
3. Common culprits:
   - waitForSelector instead of web-first assertions (expect().toBeVisible())
   - Network races — use page.waitForResponse() or route().fulfill()
   - Animation timing — disable via CSS or wait for stable state
   - Shared backend state — isolate via test-scoped users/data

Rule: a flake fixed by retry is a bug deferred, not solved.


11. Deep-Dive References

references/ holds the full Playwright best-practices library:

SubdirContents
core/Locators, assertions, fixtures, page objects
browser-apis/Network interception, file uploads, dialogs, geolocation
testing-patterns/Auth, data isolation, visual testing, API mocking
debugging/Trace viewer, inspector, video, slow-mo
architecture/Multi-project setup, custom workers, shared state
frameworks/React/Vue/Angular-specific patterns
infrastructure-ci-cd/Docker, GitHub Actions, sharding, parallelism
advanced/Custom reporters, component testing, Playwright API

12. Quick Checklist

[ ] data-testid on every interactive element
[ ] POM per page — no raw locators in specs
[ ] storageState reused for auth-required tests
[ ] Web-first assertions (expect().toBeVisible()) — not waitFor*
[ ] Specs cover critical flows only (login/checkout/payment)
[ ] CI runs against ephemeral test users, not prod data
[ ] Failure artifacts uploaded (screenshots, video, trace)
[ ] No skipped/disabled specs without an open ticket link

Keep looking

Skills are one crate of 626,194. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.