frontend-testing

安装量: 1.5K
排名: #1005

安装

npx skills add https://github.com/langgenius/dify --skill frontend-testing

Dify Frontend Testing Skill

This skill enables Claude to generate high-quality, comprehensive frontend tests for the Dify project following established conventions and best practices.

⚠️ Authoritative Source: This skill is derived from web/testing/testing.md. Use Vitest mock/timer APIs (vi.*).

When to Apply This Skill

Apply this skill when the user:

Asks to write tests for a component, hook, or utility Asks to review existing tests for completeness Mentions Vitest, React Testing Library, RTL, or spec files Requests test coverage improvement Uses pnpm analyze-component output as context Mentions testing, unit tests, or integration tests for frontend code Wants to understand testing patterns in the Dify codebase

Do NOT apply when:

User is asking about backend/API tests (Python/pytest) User is asking about E2E tests (Playwright/Cypress) User is only asking conceptual questions without code context Quick Reference Tech Stack Tool Version Purpose Vitest 4.0.16 Test runner React Testing Library 16.0 Component testing jsdom - Test environment nock 14.0 HTTP mocking TypeScript 5.x Type safety Key Commands

Run all tests

pnpm test

Watch mode

pnpm test:watch

Run specific file

pnpm test path/to/file.spec.tsx

Generate coverage report

pnpm test:coverage

Analyze component complexity

pnpm analyze-component

Review existing test

pnpm analyze-component --review

File Naming Test files: ComponentName.spec.tsx (same directory as component) Integration tests: web/tests/ directory Test Structure Template import { render, screen, fireEvent, waitFor } from '@testing-library/react' import Component from './index'

// ✅ Import real project components (DO NOT mock these) // import Loading from '@/app/components/base/loading' // import { ChildComponent } from './child-component'

// ✅ Mock external dependencies only vi.mock('@/service/api') vi.mock('next/navigation', () => ({ useRouter: () => ({ push: vi.fn() }), usePathname: () => '/test', }))

// ✅ Zustand stores: Use real stores (auto-mocked globally) // Set test state with: useAppStore.setState({ ... })

// Shared state for mocks (if needed) let mockSharedState = false

describe('ComponentName', () => { beforeEach(() => { vi.clearAllMocks() // ✅ Reset mocks BEFORE each test mockSharedState = false // ✅ Reset shared state })

// Rendering tests (REQUIRED) describe('Rendering', () => { it('should render without crashing', () => { // Arrange const props = { title: 'Test' }

  // Act
  render(<Component {...props} />)

  // Assert
  expect(screen.getByText('Test')).toBeInTheDocument()
})

})

// Props tests (REQUIRED) describe('Props', () => { it('should apply custom className', () => { render() expect(screen.getByRole('button')).toHaveClass('custom') }) })

// User Interactions describe('User Interactions', () => { it('should handle click events', () => { const handleClick = vi.fn() render()

  fireEvent.click(screen.getByRole('button'))

  expect(handleClick).toHaveBeenCalledTimes(1)
})

})

// Edge Cases (REQUIRED) describe('Edge Cases', () => { it('should handle null data', () => { render() expect(screen.getByText(/no data/i)).toBeInTheDocument() })

it('should handle empty array', () => {
  render(<Component items={[]} />)
  expect(screen.getByText(/empty/i)).toBeInTheDocument()
})

}) })

Testing Workflow (CRITICAL) ⚠️ Incremental Approach Required

NEVER generate all test files at once. For complex components or multi-file directories:

Analyze & Plan: List all files, order by complexity (simple → complex) Process ONE at a time: Write test → Run test → Fix if needed → Next Verify before proceeding: Do NOT continue to next file until current passes For each file: ┌────────────────────────────────────────┐ │ 1. Write test │ │ 2. Run: pnpm test .spec.tsx │ │ 3. PASS? → Mark complete, next file │ │ FAIL? → Fix first, then continue │ └────────────────────────────────────────┘

Complexity-Based Order

Process in this order for multi-file testing:

🟢 Utility functions (simplest) 🟢 Custom hooks 🟡 Simple components (presentational) 🟡 Medium components (state, effects) 🔴 Complex components (API, routing) 🔴 Integration tests (index files - last) When to Refactor First Complexity > 50: Break into smaller pieces before testing 500+ lines: Consider splitting before testing Many dependencies: Extract logic into hooks first

📖 See references/workflow.md for complete workflow details and todo list format.

Testing Strategy Path-Level Testing (Directory Testing)

When assigned to test a directory/path, test ALL content within that path:

Test all components, hooks, utilities in the directory (not just index file) Use incremental approach: one file at a time, verify each before proceeding Goal: 100% coverage of ALL files in the directory Integration Testing First

Prefer integration testing when writing tests for a directory:

✅ Import real project components directly (including base components and siblings) ✅ Only mock: API services (@/service/), next/navigation, complex context providers ❌ DO NOT mock base components (@/app/components/base/) ❌ DO NOT mock sibling/child components in the same directory

See Test Structure Template for correct import/mock patterns.

Core Principles 1. AAA Pattern (Arrange-Act-Assert)

Every test should clearly separate:

Arrange: Setup test data and render component Act: Perform user actions Assert: Verify expected outcomes 2. Black-Box Testing Test observable behavior, not implementation details Use semantic queries (getByRole, getByLabelText) Avoid testing internal state directly Prefer pattern matching over hardcoded strings in assertions: // ❌ Avoid: hardcoded text assertions expect(screen.getByText('Loading...')).toBeInTheDocument()

// ✅ Better: role-based queries expect(screen.getByRole('status')).toBeInTheDocument()

// ✅ Better: pattern matching expect(screen.getByText(/loading/i)).toBeInTheDocument()

  1. Single Behavior Per Test

Each test verifies ONE user-observable behavior:

// ✅ Good: One behavior it('should disable button when loading', () => { render(

返回排行榜