docs: update frontend testing guidelines and add comprehensive testing documentation in TESTING.md

This commit is contained in:
CodingOnStar 2025-10-29 17:48:48 +08:00
parent 1bb42447cf
commit 009744b71a
6 changed files with 591 additions and 215 deletions

View File

@ -1,193 +1,69 @@
# Cursor Rules for Dify Project - Test Generation
# Cursor Rules for Dify Project
## When Generating Tests
## Frontend Testing Guidelines
Follow these rules when asked to generate tests for React components:
> **📖 Complete Testing Documentation**: For detailed testing specifications and guidelines, see [`web/scripts/TESTING.md`](./web/scripts/TESTING.md)
### Tech Stack
- Next.js 15 + React 19 + TypeScript
- Jest 29.7 + React Testing Library 16.0
- Test environment: @happy-dom/jest-environment
- File naming: `ComponentName.spec.tsx` (same directory)
When generating tests for React components:
### Component Complexity Guidelines
### Key Requirements
#### 🔴 Very Complex Components (Complexity > 50)
- **Split before testing**: Break component into smaller pieces
- **Integration tests**: Test complex workflows end-to-end
- **Data-driven tests**: Use `test.each()` for multiple scenarios
- **Performance benchmarks**: Add performance tests for critical paths
1. **Tech Stack**: Next.js 15 + React 19 + TypeScript + Jest + React Testing Library
2. **File Naming**: `ComponentName.spec.tsx` (same directory as component)
3. **Code Style**:
- Use `fireEvent` (not `userEvent`)
- AAA pattern (Arrange → Act → Assert)
- Test names: `"should [behavior] when [condition]"`
- No `any` types
- Cleanup after each test: `afterEach(() => jest.clearAllMocks())`
#### ⚠️ Complex Components (Complexity 30-50)
- **Multiple describe blocks**: Group related test cases
- **Integration scenarios**: Test feature combinations
- **Organized structure**: Keep tests maintainable
### Required Tests (All Components)
#### 📏 Large Components (500+ lines)
- **Consider refactoring**: Split into smaller components if possible
- **Section testing**: Test major sections separately
- **Helper functions**: Reduce test complexity with utilities
### Test Scenarios
Apply based on component features:
#### 1. Rendering Tests (REQUIRED for all)
```typescript
describe('Rendering', () => {
it('should render without crashing', () => {
render(<Component />)
expect(screen.getByRole('...')).toBeInTheDocument()
})
})
```
#### 2. Props Testing (REQUIRED for all)
- Test each prop variation
- Test required vs optional props
- Test default values
- Test prop type validation
#### 3. State Management
**State Only (useState):**
- Test initial state values
- Test all state transitions
- Test state reset/cleanup scenarios
**Effects Only (useEffect):**
- Test effect execution conditions
- Verify dependencies array correctness
- Test cleanup on unmount
**State + Effects Combined:**
- Test state initialization and updates
- Test useEffect dependencies array
- Test cleanup functions (return from useEffect)
- Use `waitFor()` for async state changes
#### 4. Performance Optimization
**useCallback:**
- Verify callbacks maintain referential equality
- Test callback dependencies
- Ensure re-renders don't recreate functions unnecessarily
**useMemo:**
- Test memoization dependencies
- Ensure expensive computations are cached
- Verify memo recomputation conditions
#### 5. Event Handlers
- Test all onClick, onChange, onSubmit handlers
- Test keyboard events (Enter, Escape, Tab, etc.)
- Verify event.preventDefault() calls if needed
- Test event bubbling/propagation
- Use `fireEvent` (not userEvent)
#### 6. API Calls & Async Operations
- Mock all API calls using `jest.mock`
- Test retry logic if applicable
- Verify error handling and user feedback
- Use `waitFor()` for async operations
#### 7. Next.js Routing
- Mock useRouter, usePathname, useSearchParams
- Test navigation behavior and parameters
- Test query string handling
- Verify route guards/redirects if any
- Test URL parameter updates
#### 8. Edge Cases (REQUIRED for all)
- Test null/undefined/empty values
- Test boundary conditions
- Test error states
- Test loading states
- Test unexpected inputs
#### 9. Accessibility (Optional)
- Test keyboard navigation
- Verify ARIA attributes
- Test focus management
- Ensure screen reader compatibility
#### 10. Snapshots (use sparingly)
- Only for stable UI (icons, badges, static layouts)
- Snapshot small sections only
- Prefer explicit assertions over snapshots
- Update snapshots intentionally, not automatically
**Note**: Dify is a desktop app. NO responsive/mobile testing.
### Code Style
- Use `fireEvent` not `userEvent`
- AAA pattern: Arrange → Act → Assert
- Descriptive test names: "should [behavior] when [condition]"
- TypeScript: No `any` types
- Cleanup: `afterEach(() => jest.clearAllMocks())`
### Dify-Specific Components
#### General
1. **i18n**: Always return key: `t: (key) => key`
2. **Toast**: Mock `@/app/components/base/toast`
3. **Forms**: Test validation thoroughly
#### Workflow Components (`workflow/`)
- **Node configuration**: Test all node configuration options
- **Data validation**: Verify input/output validation rules
- **Variable passing**: Test data flow between nodes
- **Edge connections**: Test graph structure and connections
- **Error handling**: Verify handling of invalid configurations
- **Integration**: Test complete workflow execution paths
#### Dataset Components (`dataset/`)
- **File upload**: Test file upload and validation
- **File types**: Verify supported format handling
- **Pagination**: Test data loading and pagination
- **Search & filtering**: Test query functionality
- **Data format handling**: Test various data formats
- **Error states**: Test upload failures and invalid data
#### Configuration Components (`app/configuration`, `config/`)
- **Form validation**: Test all validation rules thoroughly
- **Save/reset functionality**: Test data persistence
- **Required vs optional fields**: Verify field validation
- **Configuration persistence**: Test state preservation
- **Error feedback**: Verify user error messages
- **Default values**: Test initial configuration state
### Test Strategy Quick Reference
**Always Test:**
- ✅ Rendering without crashing
- ✅ Renders without crashing
- ✅ Props (required, optional, defaults)
- ✅ Edge cases (null, undefined, empty)
- ✅ Edge cases (null, undefined, empty values)
### Conditional Tests (When Present in Component)
**Test When Present:**
- 🔄 **useState** → State initialization, transitions, cleanup
- ⚡ **useEffect** → Execution, dependencies, cleanup
- 🎯 **Event handlers** → All onClick, onChange, onSubmit, keyboard events
- 🌐 **API calls** → Loading, success, error states
- 🔀 **Routing** → Navigation, params, query strings
- 🎯 **Event Handlers** → onClick, onChange, onSubmit, keyboard events
- 🌐 **API Calls** → Loading, success, error states
- 🔀 **Next.js Routing** → Navigation, params, query strings
- 🚀 **useCallback/useMemo** → Referential equality, dependencies
- ⚙️ **Workflow** → Node config, data flow, validation
- 📚 **Dataset** → Upload, pagination, search
- 🎛️ **Configuration** → Form validation, persistence
- ⚙️ **Workflow Components** → Node config, data flow, validation
- 📚 **Dataset Components** → Upload, pagination, search
- 🎛️ **Configuration Components** → Form validation, persistence
**Complex Components (30+):**
- Group tests in multiple `describe` blocks
- Test integration scenarios
- Consider splitting component before testing
### Component Complexity Strategy
### Coverage Target
- **Complexity > 50**: Refactor before testing, use integration tests and `test.each()`
- **Complexity 30-50**: Use multiple `describe` blocks to group tests
- **500+ lines**: Consider splitting component
### Coverage Goals
Aim for 100% coverage:
- Line: >95%
- Branch: >95%
- Function: 100%
- Statement: 100%
- Line coverage: >95%
- Branch coverage: >95%
- Function coverage: 100%
- Statement coverage: 100%
Generate comprehensive tests covering ALL code paths and scenarios.
### Dify-Specific Mocks
```typescript
// Toast
jest.mock('@/app/components/base/toast')
// Next.js Router
jest.mock('next/navigation', () => ({
useRouter: jest.fn(),
usePathname: jest.fn(),
useSearchParams: jest.fn(),
}))
```
---
**Important**: The above is a quick reference only. When generating tests, strictly follow the complete specifications in [`web/scripts/TESTING.md`](./web/scripts/TESTING.md), including detailed examples, best practices, and FAQs.

View File

@ -77,6 +77,8 @@ How we prioritize:
For setting up the frontend service, please refer to our comprehensive [guide](https://github.com/langgenius/dify/blob/main/web/README.md) in the `web/README.md` file. This document provides detailed instructions to help you set up the frontend environment properly.
**Testing**: All React components must have comprehensive test coverage. See [web/scripts/TESTING.md](https://github.com/langgenius/dify/blob/main/web/scripts/TESTING.md) for detailed testing guidelines.
#### Backend
For setting up the backend service, kindly refer to our detailed [instructions](https://github.com/langgenius/dify/blob/main/api/README.md) in the `api/README.md` file. This document contains step-by-step guidance to help you get the backend up and running smoothly.

View File

@ -93,20 +93,26 @@ If your IDE is VSCode, rename `web/.vscode/settings.example.json` to `web/.vscod
## Test
We start to use [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) for Unit Testing.
We use [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) for Unit Testing.
You can create a test file with a suffix of `.spec` beside the file that to be tested. For example, if you want to test a file named `util.ts`. The test file name should be `util.spec.ts`.
**📖 Complete Testing Guide**: See [scripts/TESTING.md](./scripts/TESTING.md) for detailed testing specifications, best practices, and examples.
Run test:
### Example Code
If you are not familiar with writing tests, refer to:
- [classnames.spec.ts](./utils/classnames.spec.ts) - Utility function test example
- [index.spec.tsx](./app/components/base/button/index.spec.tsx) - Component test example
### Analyze Component Complexity
Before writing tests, use the script to analyze component complexity:
```bash
pnpm run test
pnpm analyze-component app/components/your-component/index.tsx
```
If you are not familiar with writing tests, here is some code to refer to:
- [classnames.spec.ts](./utils/classnames.spec.ts)
- [index.spec.tsx](./app/components/base/button/index.spec.tsx)
This will help you determine the testing strategy. See [scripts/TESTING.md](./scripts/TESTING.md) for details.
## Documentation

View File

@ -10,7 +10,7 @@ Frontend development utility scripts.
---
## 🚀 Generate Tests (Using Cursor AI)
## 🚀 Generate Tests (Using AI Assistants)
### Quick Start
@ -18,11 +18,14 @@ Frontend development utility scripts.
# 1. Analyze component
pnpm test:gen app/components/base/button/index.tsx
# Output: Component analysis + Cursor prompt (auto-copied)
# Output: Component analysis + AI prompt (auto-copied to clipboard)
# 2. In Cursor: Cmd+L → Cmd+V → Enter → Apply
# 2. Paste in your AI assistant:
# - Cursor: Cmd+L (Chat) or Cmd+I (Composer) → Cmd+V → Enter
# - GitHub Copilot Chat: Cmd+I → Cmd+V → Enter
# - Claude/ChatGPT: Paste the prompt directly
# 3. Verify
# 3. Apply the generated test and verify
pnpm test app/components/base/button/index.spec.tsx
```
@ -37,19 +40,19 @@ pnpm test app/components/base/button/index.spec.tsx
Script analyzes and scores components:
- **0-10**: 🟢 Simple (5-10 min to test)
- **11-30Menu 🟡 Medium (15-30 min to test)
- **31-50Menu 🟠 Complex (30-60 min to test)
- **11-30**: 🟡 Medium (15-30 min to test)
- **31-50**: 🟠 Complex (30-60 min to test)
- **51+**: 🔴 Very Complex (60+ min, consider refactoring)
### Test Scenarios (11 types)
### Test Scenarios
Defined in `.cursorrules`:
Defined in `TESTING.md`:
**Must test**: Rendering, Props, Edge Cases
**CommonMenuInteractions, Accessibility, i18n, Async
**OptionalMenuState, Security, Performance, Snapshots
**Conditional**: State, Effects, Events, API calls, Routing
**Optional**: Accessibility, Performance, Snapshots
Cursor AI auto-selects scenarios based on component features.
AI assistant auto-selects scenarios based on component features.
---
@ -58,10 +61,12 @@ Cursor AI auto-selects scenarios based on component features.
```bash
# New component
pnpm test:gen app/components/new-feature/index.tsx
# → Cursor → Apply → Done
# → Paste in AI assistant → Apply → Done
# Or even simpler in Cursor
# Cmd+I → "Generate test" → Apply
# Quick shortcuts:
# Cursor users: Cmd+I → "Generate test for [file]" → Apply
# Copilot users: Cmd+I → Paste prompt → Accept
# Others: Copy prompt → Paste in your AI tool
```
---
@ -80,10 +85,24 @@ pnpm type-check # Type check
## 🎯 Customize
Edit `.cursorrules` to modify test standards for your team.
Edit testing standards for your team:
```bash
# Complete testing guide (for all team members)
code web/scripts/TESTING.md
# Quick reference for Cursor users
code .cursorrules
git commit -m "docs: update test rules"
# Commit your changes
git commit -m "docs: update test standards"
```
---
## 📚 Resources
- **Testing Guide**: [TESTING.md](./TESTING.md) - Complete testing specifications
- **Quick Reference**: [.cursorrules](../../.cursorrules) - For Cursor users
- **Examples**: [classnames.spec.ts](../utils/classnames.spec.ts), [button/index.spec.tsx](../app/components/base/button/index.spec.tsx)

459
web/scripts/TESTING.md Normal file
View File

@ -0,0 +1,459 @@
# Frontend Testing Guide
This document is the complete testing specification for the Dify frontend project.
## Tech Stack
- **Framework**: Next.js 15 + React 19 + TypeScript
- **Testing Tools**: Jest 29.7 + React Testing Library 16.0
- **Test Environment**: @happy-dom/jest-environment
- **File Naming**: `ComponentName.spec.tsx` (same directory as component)
## Running Tests
```bash
# Run all tests
pnpm test
# Watch mode
pnpm test -- --watch
# Generate coverage report
pnpm test -- --coverage
# Run specific file
pnpm test -- path/to/file.spec.tsx
```
## Component Complexity Guidelines
Use `pnpm analyze-component <path>` to analyze component complexity and adopt different testing strategies based on the results.
### 🔴 Very Complex Components (Complexity > 50)
- **Refactor first**: Break component into smaller pieces
- **Integration tests**: Test complex workflows end-to-end
- **Data-driven tests**: Use `test.each()` for multiple scenarios
- **Performance benchmarks**: Add performance tests for critical paths
### ⚠️ Complex Components (Complexity 30-50)
- **Multiple describe blocks**: Group related test cases
- **Integration scenarios**: Test feature combinations
- **Organized structure**: Keep tests maintainable
### 📏 Large Components (500+ lines)
- **Consider refactoring**: Split into smaller components if possible
- **Section testing**: Test major sections separately
- **Helper functions**: Reduce test complexity with utilities
## Test Scenarios
Apply the following test scenarios based on component features:
### 1. Rendering Tests (REQUIRED - All Components)
```typescript
describe('Rendering', () => {
it('should render without crashing', () => {
render(<Component />)
expect(screen.getByRole('...')).toBeInTheDocument()
})
})
```
**Key Points**:
- Verify component renders properly
- Check key elements exist
- Use semantic queries (getByRole, getByLabelText)
### 2. Props Testing (REQUIRED - All Components)
**Must Test**:
- ✅ Different values for each prop
- ✅ Required vs optional props
- ✅ Default values
- ✅ Props type validation
### 3. State Management
#### useState
When testing state-related components:
- ✅ Test initial state values
- ✅ Test all state transitions
- ✅ Test state reset/cleanup scenarios
#### useEffect
When testing side effects:
- ✅ Test effect execution conditions
- ✅ Verify dependencies array correctness
- ✅ Test cleanup function on unmount
#### useState + useEffect Combined
- ✅ Test state initialization and updates
- ✅ Test useEffect dependencies array
- ✅ Test cleanup functions (useEffect return value)
- ✅ Use `waitFor()` for async state changes
### 4. Performance Optimization
#### useCallback
- ✅ Verify callbacks maintain referential equality
- ✅ Test callback dependencies
- ✅ Ensure re-renders don't recreate functions unnecessarily
#### useMemo
- ✅ Test memoization dependencies
- ✅ Ensure expensive computations are cached
- ✅ Verify memo recomputation conditions
### 5. Event Handlers
**Must Test**:
- ✅ All onClick, onChange, onSubmit handlers
- ✅ Keyboard events (Enter, Escape, Tab, etc.)
- ✅ Verify event.preventDefault() calls (if needed)
- ✅ Test event bubbling/propagation
**Note**: Use `fireEvent` (not `userEvent`)
### 6. API Calls and Async Operations
**Must Test**:
- ✅ Mock all API calls using `jest.mock`
- ✅ Test retry logic (if applicable)
- ✅ Verify error handling and user feedback
- ✅ Use `waitFor()` for async operations
### 7. Next.js Routing
**Must Test**:
- ✅ Mock useRouter, usePathname, useSearchParams
- ✅ Test navigation behavior and parameters
- ✅ Test query string handling
- ✅ Verify route guards/redirects (if any)
- ✅ Test URL parameter updates
### 8. Edge Cases (REQUIRED - All Components)
**Must Test**:
- ✅ null/undefined/empty values
- ✅ Boundary conditions
- ✅ Error states
- ✅ Loading states
- ✅ Unexpected inputs
### 9. Accessibility Testing (Optional)
- Test keyboard navigation
- Verify ARIA attributes
- Test focus management
- Ensure screen reader compatibility
### 10. Snapshot Testing (Use Sparingly)
**Only Use For**:
- ✅ Stable UI (icons, badges, static layouts)
- ✅ Snapshot small sections only
- ✅ Prefer explicit assertions over snapshots
- ✅ Update snapshots intentionally, not automatically
**Note**: Dify is a desktop application. **No need for** responsive/mobile testing.
## Code Style
### Basic Guidelines
- ✅ Use `fireEvent` instead of `userEvent`
- ✅ AAA pattern: Arrange (setup) → Act (execute) → Assert (verify)
- ✅ Descriptive test names: `"should [behavior] when [condition]"`
- ✅ TypeScript: No `any` types
- ✅ Cleanup: `afterEach(() => jest.clearAllMocks())`
### Example Structure
```typescript
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import Component from './index'
// Mock dependencies
jest.mock('@/service/api')
describe('ComponentName', () => {
// Cleanup after each test
afterEach(() => {
jest.clearAllMocks()
})
describe('Rendering', () => {
it('should render without crashing', () => {
// Arrange
const props = { title: 'Test' }
// Act
render(<Component {...props} />)
// Assert
expect(screen.getByText('Test')).toBeInTheDocument()
})
})
describe('User Interactions', () => {
it('should handle click events', () => {
const handleClick = jest.fn()
render(<Component onClick={handleClick} />)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
describe('Edge Cases', () => {
it('should handle null data', () => {
render(<Component data={null} />)
expect(screen.getByText(/no data/i)).toBeInTheDocument()
})
})
})
```
## Dify-Specific Components
### General
1. **i18n**: Always return key
```typescript
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}))
```
2. **Toast**: Mock toast component
```typescript
jest.mock('@/app/components/base/toast', () => ({
notify: jest.fn(),
}))
```
3. **Forms**: Test validation logic thoroughly
### Workflow Components (`workflow/`)
**Must Test**:
- ⚙️ **Node configuration**: Test all node configuration options
- ✔️ **Data validation**: Verify input/output validation rules
- 🔄 **Variable passing**: Test data flow between nodes
- 🔗 **Edge connections**: Test graph structure and connections
- ❌ **Error handling**: Verify invalid configuration handling
- 🧪 **Integration**: Test complete workflow execution paths
### Dataset Components (`dataset/`)
**Must Test**:
- 📤 **File upload**: Test file upload and validation
- 📄 **File types**: Verify supported format handling
- 📃 **Pagination**: Test data loading and pagination
- 🔍 **Search & filtering**: Test query functionality
- 📊 **Data format handling**: Test various data formats
- ⚠️ **Error states**: Test upload failures and invalid data
### Configuration Components (`app/configuration`, `config/`)
**Must Test**:
- ✅ **Form validation**: Test all validation rules thoroughly
- 💾 **Save/reset functionality**: Test data persistence
- 🔒 **Required vs optional fields**: Verify field validation
- 📌 **Configuration persistence**: Test state preservation
- 💬 **Error feedback**: Verify user error messages
- 🎯 **Default values**: Test initial configuration state
## Testing Strategy Quick Reference
### Required (All Components)
- ✅ Renders without crashing
- ✅ Props (required, optional, defaults)
- ✅ Edge cases (null, undefined, empty values)
### Conditional (When Present in Component)
- 🔄 **useState** → State initialization, transitions, cleanup
- ⚡ **useEffect** → Execution, dependencies, cleanup
- 🎯 **Event Handlers** → All onClick, onChange, onSubmit, keyboard events
- 🌐 **API Calls** → Loading, success, error states
- 🔀 **Routing** → Navigation, params, query strings
- 🚀 **useCallback/useMemo** → Referential equality, dependencies
- ⚙️ **Workflow** → Node config, data flow, validation
- 📚 **Dataset** → Upload, pagination, search
- 🎛️ **Configuration** → Form validation, persistence
### Complex Components (Complexity 30+)
- Group tests in multiple `describe` blocks
- Test integration scenarios
- Consider splitting component before testing
## Coverage Goals
Aim for 100% coverage:
- **Line coverage**: >95%
- **Branch coverage**: >95%
- **Function coverage**: 100%
- **Statement coverage**: 100%
Generate comprehensive tests covering **all** code paths and scenarios.
## Common Mock Patterns
### Mock Hooks
```typescript
// Mock useState
const mockSetState = jest.fn()
jest.spyOn(React, 'useState').mockImplementation((init) => [init, mockSetState])
// Mock useContext
jest.spyOn(React, 'useContext').mockReturnValue({ user: mockUser })
```
### Mock Modules
```typescript
// Mock entire module
jest.mock('@/utils/api', () => ({
get: jest.fn(),
post: jest.fn(),
}))
// Mock partial module
jest.mock('@/utils/helpers', () => ({
...jest.requireActual('@/utils/helpers'),
specificFunction: jest.fn(),
}))
```
### Mock Next.js
```typescript
// useRouter
jest.mock('next/navigation', () => ({
useRouter: () => ({
push: jest.fn(),
replace: jest.fn(),
prefetch: jest.fn(),
}),
usePathname: () => '/current-path',
useSearchParams: () => new URLSearchParams(),
}))
// next/image
jest.mock('next/image', () => ({
__esModule: true,
default: (props: any) => <img {...props} />,
}))
```
## Debugging Tips
### View Rendered DOM
```typescript
import { screen } from '@testing-library/react'
// Print entire DOM
screen.debug()
// Print specific element
screen.debug(screen.getByRole('button'))
```
### Finding Elements
Priority order (recommended top to bottom):
1. `getByRole` - Most recommended, follows accessibility standards
2. `getByLabelText` - Form fields
3. `getByPlaceholderText` - Only when no label
4. `getByText` - Non-interactive elements
5. `getByDisplayValue` - Current form value
6. `getByAltText` - Images
7. `getByTitle` - Last choice
8. `getByTestId` - Only as last resort
### Async Debugging
```typescript
// Wait for element to appear
await waitFor(() => {
expect(screen.getByText('Loaded')).toBeInTheDocument()
})
// Wait for element to disappear
await waitFor(() => {
expect(screen.queryByText('Loading')).not.toBeInTheDocument()
})
// Find async element
const element = await screen.findByText('Async Content')
```
## Reference Examples
Test examples in the project:
- [classnames.spec.ts](../utils/classnames.spec.ts) - Utility function tests
- [index.spec.tsx](../app/components/base/button/index.spec.tsx) - Component tests
## Resources
- [Jest Documentation](https://jestjs.io/docs/getting-started)
- [React Testing Library Documentation](https://testing-library.com/docs/react-testing-library/intro/)
- [Testing Library Best Practices](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
- [Jest Mock Functions](https://jestjs.io/docs/mock-functions)
## FAQ
### Q: When to use `getBy` vs `queryBy` vs `findBy`?
- `getBy*`: Expect element to exist, throws error if not found
- `queryBy*`: Element may not exist, returns null if not found
- `findBy*`: Async query, returns Promise
### Q: How to test conditional rendering?
```typescript
it('should conditionally render content', () => {
const { rerender } = render(<Component show={false} />)
expect(screen.queryByText('Content')).not.toBeInTheDocument()
rerender(<Component show={true} />)
expect(screen.getByText('Content')).toBeInTheDocument()
})
```
### Q: How to test custom hooks?
```typescript
import { renderHook, act } from '@testing-library/react'
it('should update counter', () => {
const { result } = renderHook(() => useCounter())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
```
---
**Remember**: Writing tests is not just about coverage, but ensuring code quality and maintainability. Good tests should be clear, concise, and meaningful.

View File

@ -1,9 +1,9 @@
#!/usr/bin/env node
/**
* Component Analyzer for Cursor AI
* Component Analyzer for Test Generation
*
* Analyzes a component and generates a structured prompt for Cursor AI.
* Copy the output and paste into Cursor Chat (Cmd+L) or Composer (Cmd+I).
* Analyzes a component and generates a structured prompt for AI assistants.
* Works with Cursor, GitHub Copilot, and other AI coding tools.
*
* Usage:
* node scripts/analyze-component.js <component-path>
@ -11,6 +11,8 @@
* Examples:
* node scripts/analyze-component.js app/components/base/button/index.tsx
* node scripts/analyze-component.js app/components/workflow/nodes/llm/panel.tsx
*
* For complete testing guidelines, see: web/scripts/TESTING.md
*/
const fs = require('node:fs')
@ -272,16 +274,16 @@ class ComponentAnalyzer {
}
// ============================================================================
// Prompt Builder for Cursor
// Prompt Builder for AI Assistants
// ============================================================================
class CursorPromptBuilder {
class TestPromptBuilder {
build(analysis, _sourceCode) {
const testPath = analysis.path.replace(/\.tsx?$/, '.spec.tsx')
return `
📋 GENERATE TEST FOR DIFY COMPONENT
📋 GENERATE TEST FOR DIFY COMPONENT
📍 Component: ${analysis.name}
@ -307,7 +309,7 @@ Features Detected:
${analysis.hasAPI ? '✓' : '✗'} API calls
📝 TASK FOR CURSOR AI:
📝 TASK:
Please generate a comprehensive test file for this component at:
@ -316,16 +318,18 @@ Please generate a comprehensive test file for this component at:
The component is located at:
${analysis.path}
Follow the testing guidelines in .cursorrules file.
Follow the testing guidelines in:
- web/scripts/TESTING.md (complete testing guide)
- .cursorrules (quick reference for Cursor users)
${this.getSpecificGuidelines(analysis)}
📋 COPY THIS TO CURSOR:
📋 PROMPT FOR AI ASSISTANT (COPY THIS TO YOUR AI ASSISTANT):
Generate a comprehensive test file for @${analysis.path} following the project's testing guidelines in .cursorrules.
Generate a comprehensive test file for @${analysis.path} following the project's testing guidelines in web/scripts/TESTING.md.
Including but not limited to:
${this.buildFocusPoints(analysis)}
@ -514,8 +518,13 @@ Examples:
node scripts/analyze-component.js app/components/base/button/index.tsx
node scripts/analyze-component.js app/components/workflow/nodes/llm/panel.tsx
This tool analyzes your component and generates a prompt for Cursor AI.
Copy the output and use it in Cursor Chat (Cmd+L) or Composer (Cmd+I).
This tool analyzes your component and generates a prompt for AI assistants.
Copy the output and use it with:
- Cursor (Cmd+L for Chat, Cmd+I for Composer)
- GitHub Copilot Chat (Cmd+I)
- Claude, ChatGPT, or any other AI coding tool
For complete testing guidelines, see: web/scripts/TESTING.md
`)
process.exit(1)
}
@ -587,8 +596,8 @@ This component is too complex to test effectively. Please consider:
process.exit(0)
}
// Build prompt for Cursor
const builder = new CursorPromptBuilder()
// Build prompt for AI assistant
const builder = new TestPromptBuilder()
const prompt = builder.build(analysis, sourceCode)
// Output
@ -619,8 +628,13 @@ This component is too complex to test effectively. Please consider:
encoding: 'utf-8',
})
if (result.status === 0)
console.log('\n📋 Prompt copied to clipboard! Paste it in Cursor Chat (Cmd+L).\n')
if (result.status === 0) {
console.log('\n📋 Prompt copied to clipboard!')
console.log(' Paste it in your AI assistant:')
console.log(' - Cursor: Cmd+L (Chat) or Cmd+I (Composer)')
console.log(' - GitHub Copilot Chat: Cmd+I')
console.log(' - Or any other AI coding tool\n')
}
}
catch {
// pbcopy failed, but don't break the script