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 testing
description Vitest + Storybook testing strategy with clear role separation. Reference for implementing unit tests and UI interaction tests.

Testing Skill

Role Separation

Tool Responsibility Target
Vitest Logic & Unit Tests Classes, utilities, calculations
Storybook UI Catalog + Interaction Tests Component visual state changes

Key Principles

Storybook Story Selection Criteria

Include: Cases where component state changes visually

  • Default / Empty / Loading / Error / Disabled
  • Selected / Hover / Focus states
  • Form input / Validation error display

Exclude: Stories only for coverage

  • Stories for internal logic branches
  • Exhaustive props combinations → Use argTypes controls
  • Cases that look visually identical

Use argTypes for Props Combinations

Control props dynamically via the controls panel instead of creating more stories.

const meta = {
  component: TreeView,
  argTypes: {
    variant: {
      control: "select",
      options: ["default", "compact", "comfortable"],
    },
    disabled: { control: "boolean" },
    size: { control: { type: "range", min: 12, max: 24, step: 2 } },
  },
} satisfies Meta<typeof TreeView>

Active Use of Play Functions

Implement interaction tests with play functions, especially for form handling.

// Form submission test
export const FormSubmission: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)
    const user = userEvent.setup()

    await user.type(canvas.getByLabelText("Filename"), "test.txt")
    await user.click(canvas.getByRole("button", { name: "Create" }))

    await expect(canvas.getByText("Created successfully")).toBeInTheDocument()
  },
}

// Keyboard navigation test
export const KeyboardNavigation: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)
    const user = userEvent.setup()

    canvas.getByRole("treeitem").focus()
    await user.keyboard("{ArrowDown}")
    await user.keyboard("{Enter}")
  },
}

A11y Testing (@storybook/addon-a11y)

Setup:

// .storybook/main.ts
export default {
  addons: ["@storybook/addon-a11y"],
}

// .storybook/preview.ts
export default {
  parameters: {
    a11y: {
      config: {
        rules: [
          { id: "color-contrast", enabled: true },
          { id: "label", enabled: true },
        ],
      },
    },
  },
}

Per-story configuration: Disable rules for intentional violations.

export const DecorativeIcon: Story = {
  parameters: {
    a11y: {
      config: {
        rules: [{ id: "image-alt", enabled: false }], // Decorative icons don't need alt
      },
    },
  },
}

test-runner for CI automation:

// .storybook/test-runner.ts
import { checkA11y, injectAxe } from "axe-playwright"

export default {
  async preVisit(page) {
    await injectAxe(page)
  },
  async postVisit(page) {
    await checkA11y(page, "#storybook-root", {
      detailedReport: true,
      detailedReportOptions: { html: true },
    })
  },
}
pnpm test-storybook  # Run a11y checks on all stories

Verify ARIA state in play functions:

export const ExpandItem: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)
    const item = canvas.getByRole("treeitem", { name: /Documents/i })

    await expect(item).toHaveAttribute("aria-expanded", "false")
    await userEvent.click(item)
    await expect(item).toHaveAttribute("aria-expanded", "true")
  },
}

Keyboard navigation verification:

export const KeyboardNavigation: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement)
    const item = canvas.getByRole("treeitem", { name: /item/i })

    item.focus()
    await expect(item).toHaveFocus()

    await userEvent.keyboard("{Delete}")
    await expect(canvas.getByRole("alertdialog")).toBeInTheDocument()
  },
}

Vitest for Logic Tests

Test UI-independent logic directly with Vitest.

// core/Manager.test.ts
describe("Manager", () => {
  it("finds node by path", () => {
    const manager = new Manager()
    const node = manager.findByPath("/root/docs")
    expect(node?.name).toBe("docs")
  })

  it("sorts child nodes", () => {
    const sorted = sortNodes(nodes)
    expect(sorted[0].type).toBe("folder") // Folders first
  })
})

File Structure

src/
├── core/
│   ├── Manager.ts
│   └── Manager.test.ts          # Logic tests
└── components/Component/
    ├── Component.tsx
    ├── Component.stories.tsx    # State catalog + play functions
    └── mocks.ts                 # Shared mock data

Vitest Browser Mode (Component Testing)

For components that require real DOM/browser APIs:

// vitest.config.ts
import { defineConfig } from "vitest/config"

export default defineConfig({
  test: {
    browser: {
      enabled: true,
      provider: "playwright",
      name: "chromium",
    },
  },
})
// Component.browser.test.tsx
import { render } from "vitest-browser-react"
import { page } from "@vitest/browser/context"

test("renders and responds to interaction", async () => {
  const { getByRole } = render(<Button>Click me</Button>)

  const button = getByRole("button")
  await button.click()

  await expect.element(button).toHaveTextContent("Clicked!")
})

When to Use Browser Mode vs JSDOM

Scenario Use
Unit tests for logic/utilities Vitest (JSDOM)
Component rendering/snapshots Vitest (JSDOM)
Tests requiring real browser APIs Vitest Browser Mode
Complex interactions, focus, scroll Vitest Browser Mode
Visual state catalog Storybook
Full user flows across pages Playwright E2E

Test Organization Decision Matrix

What to Test Where
Pure functions, utilities *.test.ts (Vitest)
State management logic *.test.ts (Vitest)
Component props/variants Storybook showcase stories
Component interactions Storybook play functions
A11y compliance Storybook + addon-a11y
Real browser behavior *.browser.test.tsx (Vitest Browser)
Cross-page user journeys e2e/*.spec.ts (Playwright)

Commands

pnpm test              # Vitest watch mode
pnpm test:coverage     # Coverage report
pnpm test:browser      # Vitest browser mode
pnpm storybook         # Storybook dev server

References