# Higashi Kota Drawing Explorer E2e

> E2E Testing Skill

- Skill: `tomevault-io/higashi-kota-drawing-explorer-e2e` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/higashi-kota-drawing-explorer-e2e`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/higashi-kota-drawing-explorer-e2e/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/higashi-kota-drawing-explorer-e2e

---


# E2E Testing Skill

## Playwright Setup

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

## playwright.config.ts

```typescript
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

```typescript
// 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

```typescript
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

```typescript
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

```typescript
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

```bash
pnpm e2e --update-snapshots
```

## Page Object Pattern

```typescript
// 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

```typescript
// 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

```bash
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

```yaml
# .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

```typescript
// 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

- [Playwright Documentation](https://playwright.dev/docs/intro)
- [Playwright Best Practices](https://playwright.dev/docs/best-practices)
- [axe-core Playwright](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/playwright)
- [Testing Trophy](https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications)

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/higashi-kota) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-14 -->

