Claude Code Plugins

Community-maintained marketplace

Feedback

|

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name e2e
description Playwright E2E testing patterns with chrome-devtools MCP integration. Reference for integration tests, A11y validation, and visual regression.

E2E Testing Skill

Playwright Setup

pnpm add -D @playwright/test
pnpm exec playwright install

playwright.config.ts

import { defineConfig } from "@playwright/test"

export default defineConfig({
  testDir: "./e2e",
  fullyParallel: true,
  use: {
    baseURL: "http://localhost:5173",
    trace: "on-first-retry",
  },
  webServer: {
    command: "pnpm dev",
    url: "http://localhost:5173",
    reuseExistingServer: !process.env.CI,
  },
})

Test Patterns

Basic Structure

// e2e/example.spec.ts
import { test, expect } from "@playwright/test"

test.describe("Feature", () => {
  test("displays correct ARIA structure", async ({ page }) => {
    await page.goto("/")

    const tree = page.getByRole("tree")
    await expect(tree).toBeVisible()
    await expect(tree).toHaveAttribute("aria-label", "File explorer")
  })

  test("keyboard navigation works", async ({ page }) => {
    await page.goto("/")

    const firstItem = page.getByRole("treeitem").first()
    await firstItem.focus()

    // ArrowDown moves to next
    await page.keyboard.press("ArrowDown")
    await expect(page.getByRole("treeitem").nth(1)).toBeFocused()

    // ArrowRight expands
    await page.keyboard.press("ArrowRight")
    await expect(firstItem).toHaveAttribute("aria-expanded", "true")
  })
})

A11y Testing with axe-core

import AxeBuilder from "@axe-core/playwright"

test("meets accessibility standards", async ({ page }) => {
  await page.goto("/")

  const results = await new AxeBuilder({ page }).analyze()
  expect(results.violations).toEqual([])
})

Form Interaction

test("submits form correctly", async ({ page }) => {
  await page.goto("/form")

  await page.getByLabel("Name").fill("John Doe")
  await page.getByLabel("Email").fill("john@example.com")
  await page.getByRole("button", { name: "Submit" }).click()

  await expect(page.getByText("Success")).toBeVisible()
})

chrome-devtools MCP Integration

When running E2E verification via Claude Code:

1. Start the application:
   pnpm dev

2. Navigate to page:
   mcp__chrome-devtools__navigate_page
   url: "http://localhost:5173"

3. Take accessibility snapshot:
   mcp__chrome-devtools__take_snapshot
   → Get A11y tree

4. Verify:
   - role="tree" exists
   - aria-expanded attributes
   - aria-selected attributes

5. Test keyboard navigation:
   mcp__chrome-devtools__press_key
   key: "ArrowDown"

6. Verify focus movement:
   mcp__chrome-devtools__take_snapshot
   → Confirm focus changed

Visual Regression

test("visual regression", async ({ page }) => {
  await page.goto("/")
  await expect(page).toHaveScreenshot("homepage.png")
})

test("component visual regression", async ({ page }) => {
  await page.goto("/components/button")
  const button = page.getByRole("button", { name: "Primary" })
  await expect(button).toHaveScreenshot("button-primary.png")
})

Updating Snapshots

pnpm e2e --update-snapshots

Page Object Pattern

// e2e/pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}

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

  async login(email: string, password: string) {
    await this.page.getByLabel("Email").fill(email)
    await this.page.getByLabel("Password").fill(password)
    await this.page.getByRole("button", { name: "Sign in" }).click()
  }

  async expectError(message: string) {
    await expect(this.page.getByRole("alert")).toContainText(message)
  }
}

// e2e/auth.spec.ts
test("shows error for invalid credentials", async ({ page }) => {
  const loginPage = new LoginPage(page)
  await loginPage.goto()
  await loginPage.login("invalid@example.com", "wrong")
  await loginPage.expectError("Invalid credentials")
})

Fixtures

// e2e/fixtures.ts
import { test as base } from "@playwright/test"
import { LoginPage } from "./pages/LoginPage"

type Fixtures = {
  loginPage: LoginPage
}

export const test = base.extend<Fixtures>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page))
  },
})

// Usage
test("login flow", async ({ loginPage }) => {
  await loginPage.goto()
  await loginPage.login("user@example.com", "password")
})

Commands

pnpm e2e            # Run E2E tests
pnpm e2e:headed     # Run with browser visible
pnpm e2e:debug      # Debug mode
pnpm e2e:ui         # Interactive UI mode

When to Use E2E vs Other Test Types

Test Pyramid Strategy

     ▲ E2E (Playwright)
    ╱ ╲   - Full user journeys
   ╱   ╲  - Cross-page flows
  ╱─────╲ - Critical paths only
 ╱       ╲
╱ Component╲  - Storybook play functions
╱  Tests    ╲ - Vitest Browser Mode
╱────────────╲- Isolated component behavior
╱              ╲
╱   Unit Tests   ╲ - Vitest
╱     (Base)      ╲- Pure functions, logic
╱──────────────────╲

Decision Matrix

Test Scope Tool When to Use
Unit Vitest Pure functions, utilities, state logic
Component Storybook + Vitest Browser Single component behavior, props, states
Integration Vitest Browser Multiple components together
E2E Playwright Full user flows, multi-page, auth

E2E Test Selection Criteria

Include in E2E:

  • Critical business flows (checkout, signup, login)
  • Multi-page navigation flows
  • Flows requiring server state
  • External API integrations
  • Authentication/authorization

Avoid in E2E (use component tests):

  • Individual component variants
  • Form validation rules
  • UI state toggling
  • Styling variations

Cost-Benefit Analysis

Factor Unit Component E2E
Speed ~1ms ~50ms ~2-5s
Reliability High High Medium
Maintenance Low Low High
Coverage Narrow Medium Wide
Debug ease Easy Easy Hard

Rule of thumb: Maximize unit/component tests, minimize E2E to critical paths only.

CI Integration

# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2

      - name: Unit & Component Tests
        run: pnpm test

      - name: E2E Tests
        run: pnpm e2e
        env:
          CI: true

Parallel E2E Execution

// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  retries: process.env.CI ? 2 : 0,
  reporter: process.env.CI ? "github" : "list",
})

References