testing

Expert at Playwright E2E tests, Vitest unit tests, Storybook interaction tests. Use when writing tests, debugging test failures, or improving test coverage.

allowed_tools: Read, Write, Edit, Bash, Grep, Glob

$ 安裝

git clone https://github.com/CrazySwami/cross-platform-web-app-research /tmp/cross-platform-web-app-research && cp -r /tmp/cross-platform-web-app-research/.claude/skills/testing ~/.claude/skills/cross-platform-web-app-research

// tip: Run this command in your terminal to install the skill


name: testing description: Expert at Playwright E2E tests, Vitest unit tests, Storybook interaction tests. Use when writing tests, debugging test failures, or improving test coverage. allowed-tools: Read, Write, Edit, Bash, Grep, Glob

Testing Specialist

You are an expert at testing TypeScript/React applications using Playwright, Vitest, and Storybook.

When To Use

Claude should automatically use this skill when:

  • User asks to write tests or improve coverage
  • User mentions Playwright, Vitest, E2E, unit tests
  • Debugging test failures
  • Setting up test infrastructure

Testing Stack

ToolPurposeLocation
PlaywrightE2E browser testse2e/*.spec.ts
VitestUnit/integration testssrc/**/*.test.ts
StorybookComponent visual testssrc/**/*.stories.tsx

CRITICAL: Read Before Write (Reconnaissance-Then-Action)

Before writing ANY E2E test, you MUST:

  1. Read the component source file (e.g., Toolbar.tsx, Editor.tsx)
  2. Identify stable selectors in this priority order:
    • data-testid attributes (explicit testing contract)
    • title attributes (accessibility, stable)
    • aria-label attributes (accessibility)
    • Text content (fragile, last resort)
  3. Extract exact values from the source - never guess or assume

Selector Priority (Playwright Official)

PriorityMethodExampleStability
1data-testidpage.getByTestId('save-btn')Most stable
2role + namepage.getByRole('button', { name: 'Save' })Stable
3titlepage.locator('[title="Save"]')Stable
4textpage.getByText('Save')Fragile

NEVER use: CSS classes, XPath, or auto-generated IDs

Anti-Pattern: Speculative Selectors

// ❌ WRONG - Guessing without reading source
await editor.clickToolbarButton('B');  // Assumes button text is "B"

// ✅ CORRECT - After reading Toolbar.tsx:54 shows title="Bold (Cmd+B)"
await editor.clickToolbarButton('Bold (Cmd+B)');

data-testid Convention

When adding test IDs to components, use format: {scope}-{element}-{type}

data-testid="toolbar-bold-button"
data-testid="editor-content-area"
data-testid="sidebar-note-list"

Playwright E2E Tests

File Structure

e2e/
├── fixtures/
│   └── editor-helpers.ts   # Shared test utilities
├── editor.spec.ts          # Editor functionality
├── formatting.spec.ts      # Text formatting
├── headings.spec.ts        # Heading toggles
├── lists.spec.ts           # List operations
├── blocks.spec.ts          # Block elements
├── keyboard-shortcuts.spec.ts
├── toolbar.spec.ts
└── history.spec.ts

Test Pattern

import { test, expect } from '@playwright/test';
import { EditorHelper } from './fixtures/editor-helpers';

test.describe('Feature Name', () => {
  let editor: EditorHelper;

  test.beforeEach(async ({ page }) => {
    editor = new EditorHelper(page);
    await editor.goto();
  });

  test('descriptive test name', async () => {
    await editor.type('test content');
    await editor.selectAll();
    // Use title from Toolbar.tsx - NOT guessed text
    await editor.clickToolbarButton('Bold (Cmd+B)');
    await editor.expectElement('strong');
  });
});

EditorHelper Methods

editor.goto()                      // Navigate to app
editor.clear()                     // Clear editor content
editor.type(text)                  // Type text
editor.selectAll()                 // Select all (Cmd+A)
editor.clickToolbarButton(title)   // Click toolbar button by title attribute
editor.isToolbarButtonActive(title) // Check button state by title
editor.expectText(text)            // Assert text exists
editor.expectElement(selector)     // Assert element exists
editor.pressShortcut(key)          // Press keyboard shortcut

Toolbar Button Titles (from Toolbar.tsx)

ButtonTitle Attribute
BoldBold (Cmd+B)
ItalicItalic (Cmd+I)
UnderlineUnderline (Cmd+U)
StrikeStrikethrough
H1Heading 1
H2Heading 2
H3Heading 3
Bullet ListBullet List
Numbered ListNumbered List
Task ListTask List
QuoteQuote
CodeCode Block
UndoUndo (Cmd+Z)
RedoRedo (Cmd+Shift+Z)
NewNew Document
OpenOpen Document
SaveSave Document

Commands

pnpm test:e2e           # Run all E2E tests
pnpm test:e2e:ui        # Open Playwright UI
pnpm test:e2e:headed    # Run with browser visible
pnpm test:e2e:chromium  # Chrome only
pnpm test:e2e:webkit    # Safari only

Vitest Unit Tests

File Naming

  • Component: ComponentName.test.tsx
  • Hook: useHookName.test.ts
  • Utility: utilName.test.ts

Test Pattern

import { describe, it, expect, vi } from 'vitest';
import { functionName } from './module';

describe('functionName', () => {
  it('should do something', () => {
    const result = functionName(input);
    expect(result).toBe(expected);
  });

  it('should handle edge case', () => {
    expect(() => functionName(null)).toThrow();
  });
});

Hook Testing

import { renderHook, act } from '@testing-library/react';
import { useHookName } from './useHookName';

describe('useHookName', () => {
  it('should return initial state', () => {
    const { result } = renderHook(() => useHookName());
    expect(result.current.value).toBe(initial);
  });

  it('should update on action', async () => {
    const { result } = renderHook(() => useHookName());
    await act(async () => {
      await result.current.doAction();
    });
    expect(result.current.value).toBe(updated);
  });
});

Storybook Interaction Tests

import { within, userEvent, expect } from '@storybook/test';

export const WithInteraction: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Find and interact with elements
    const button = canvas.getByRole('button', { name: 'Submit' });
    await userEvent.click(button);

    // Assert results
    await expect(canvas.getByText('Success')).toBeInTheDocument();
  },
};

Test Checklist

When writing tests:

  • Test happy path (expected behavior)
  • Test edge cases (empty, null, boundary values)
  • Test error handling
  • Test loading/async states
  • Use descriptive test names
  • Keep tests focused and isolated
  • Mock external dependencies
  • Avoid testing implementation details

Coverage Goals

TypeTarget
E2ECritical user flows
UnitBusiness logic, utilities
ComponentVisual states, interactions