mirror of https://github.com/langgenius/dify.git
5.8 KiB
5.8 KiB
Test Generation Checklist
Use this checklist when generating or reviewing tests for Dify frontend components.
Pre-Generation
- Read the component source code completely
- Identify component type (component, hook, utility, page)
- Run
pnpm analyze-component <path>if available - Note complexity score and features detected
- Check for existing tests in the same directory
- Identify ALL files in the directory that need testing (not just index)
Testing Strategy
⚠️ Incremental Workflow (CRITICAL for Multi-File)
- NEVER generate all tests at once - process one file at a time
- Order files by complexity: utilities → hooks → simple → complex → integration
- Create a todo list to track progress before starting
- For EACH file: write → run test → verify pass → then next
- DO NOT proceed to next file until current one passes
Path-Level Coverage
- Test ALL files in the assigned directory/path
- List all components, hooks, utilities that need coverage
- Decide: single spec file (integration) or multiple spec files (unit)
Complexity Assessment
- Run
pnpm analyze-component <path>for complexity score - Complexity > 50: Consider refactoring before testing
- 500+ lines: Consider splitting before testing
- 30-50 complexity: Use multiple describe blocks, organized structure
Integration vs Mocking
- DO NOT mock base components (
Loading,Button,Tooltip, etc.) - Import real project components instead of mocking
- Only mock: API calls, complex context providers, third-party libs with side effects
- Prefer integration testing when using single spec file
Required Test Sections
All Components MUST Have
- Rendering tests - Component renders without crashing
- Props tests - Required props, optional props, default values
- Edge cases - null, undefined, empty values, boundaries
Conditional Sections (Add When Feature Present)
| Feature | Add Tests For |
|---|---|
useState |
Initial state, transitions, cleanup |
useEffect |
Execution, dependencies, cleanup |
| Event handlers | onClick, onChange, onSubmit, keyboard |
| API calls | Loading, success, error states |
| Routing | Navigation, params, query strings |
useCallback/useMemo |
Referential equality |
| Context | Provider values, consumer behavior |
| Forms | Validation, submission, error display |
Code Quality Checklist
Structure
- Uses
describeblocks to group related tests - Test names follow
should <behavior> when <condition>pattern - AAA pattern (Arrange-Act-Assert) is clear
- Comments explain complex test scenarios
Mocks
- DO NOT mock base components (
@/app/components/base/*) vi.clearAllMocks()inbeforeEach(notafterEach)- Shared mock state reset in
beforeEach - i18n uses global mock (auto-loaded in
web/vitest.setup.ts); only override locally for custom translations - Router mocks match actual Next.js API
- Mocks reflect actual component conditional behavior
- Only mock: API services, complex context providers, third-party libs
Queries
- Prefer semantic queries (
getByRole,getByLabelText) - Use
queryBy*for absence assertions - Use
findBy*for async elements getByTestIdonly as last resort
Async
- All async tests use
async/await waitForwraps async assertions- Fake timers properly setup/teardown
- No floating promises
TypeScript
- No
anytypes without justification - Mock data uses actual types from source
- Factory functions have proper return types
Coverage Goals (Per File)
For the current file being tested:
- 100% function coverage
- 100% statement coverage
- >95% branch coverage
- >95% line coverage
Post-Generation (Per File)
Run these checks after EACH test file, not just at the end:
- Run
pnpm test -- path/to/file.spec.tsx- MUST PASS before next file - Fix any failures immediately
- Mark file as complete in todo list
- Only then proceed to next file
After All Files Complete
- Run full directory test:
pnpm test -- path/to/directory/ - Check coverage report:
pnpm test -- --coverage - Run
pnpm lint:fixon all test files - Run
pnpm type-check:tsgo
Common Issues to Watch
False Positives
// ❌ Mock doesn't match actual behavior
vi.mock('./Component', () => () => <div>Mocked</div>)
// ✅ Mock matches actual conditional logic
vi.mock('./Component', () => ({ isOpen }: any) =>
isOpen ? <div>Content</div> : null
)
State Leakage
// ❌ Shared state not reset
let mockState = false
vi.mock('./useHook', () => () => mockState)
// ✅ Reset in beforeEach
beforeEach(() => {
mockState = false
})
Async Race Conditions
// ❌ Not awaited
it('loads data', () => {
render(<Component />)
expect(screen.getByText('Data')).toBeInTheDocument()
})
// ✅ Properly awaited
it('loads data', async () => {
render(<Component />)
await waitFor(() => {
expect(screen.getByText('Data')).toBeInTheDocument()
})
})
Missing Edge Cases
Always test these scenarios:
null/undefinedinputs- Empty strings / arrays / objects
- Boundary values (0, -1, MAX_INT)
- Error states
- Loading states
- Disabled states
Quick Commands
# Run specific test
pnpm test -- path/to/file.spec.tsx
# Run with coverage
pnpm test -- --coverage path/to/file.spec.tsx
# Watch mode
pnpm test:watch -- path/to/file.spec.tsx
# Update snapshots (use sparingly)
pnpm test -- -u path/to/file.spec.tsx
# Analyze component
pnpm analyze-component path/to/component.tsx
# Review existing test
pnpm analyze-component path/to/component.tsx --review