---
name: manage-react-component-tests
description: Create or update test files for React components. Use when user asks to "create component tests", "test MyComponent", "generate tests for component", "update component tests", or mentions needing tests for a React component. Generates vitest test files with render, mocked sub-components, and proper test structure focusing on logic and prop validation.
---
# Manage React Component Tests Skill
This skill helps you create or update test files for React components. Tests follow a specific pattern with vitest, `render` from React Testing Library, and mocked sub-components to test logic and prop passing in isolation.
## When to Use This Skill
Use this skill when you need to:
- **Create** a new test file for a React component
- **Update** an existing test file when the component changes
- **Generate** test coverage for component logic and prop validation
The skill will generate/update:
- A test file with vitest and React Testing Library imports
- Mocked versions of all imported sub-components
- A `renderMyComponent` helper function using `render`
- Individual test cases for different component behaviors and prop permutations
## Usage
Invoke this skill when the user asks to:
- "Create tests for [MyComponent]"
- "Generate a test file for [MyComponent]"
- "I need tests for [MyComponent]"
- "Update tests for [MyComponent]"
- "The [MyComponent] changed, update its tests"
- "Test [MyComponent]"
## Core Principles
### Testing Philosophy
1. **Logic-Focused**: Test component logic, not UI appearance
2. **Prop Validation**: Verify sub-components receive correct props
3. **Isolation**: Mock all sub-components to test the component in isolation
4. **Callback Testing**: Simulate callbacks to test handlers
5. **Unit Testing**: Each component has its own tests; dependencies should have separate tests
## Prerequisites
Before creating/updating component tests:
1. **Verify the component exists** - The component you're testing must already be defined
2. **Check for existing test file** - Use Glob to search for existing `.spec.tsx` file
3. **Identify sub-components** - Determine what components are imported and rendered
4. **Verify @testing-library/react** - Ensure it's installed (for `render`)
## Create vs Update Decision
**If test file exists:** Update mode
- Read the existing test file
- Read the component definition
- Compare and identify what's missing or outdated
- Update the test to match current implementation
**If test file does NOT exist:** Create mode
- Read the component definition
- Identify all sub-components to mock
- Generate complete test file from scratch
## Test File Location
**CRITICAL**: Test files MUST be in the `__tests__` folder, which is a **SIBLING** of the `/src` folder, NOT inside it.
### Directory Structure
```
packages/
my-package/
src/
components/
MyComponent.tsx
__tests__/ # Sibling to src/, NOT inside src/
MyComponent.spec.tsx # .tsx extension for React components
```
### Naming Convention
For a component named `MyComponent`:
- Test file: `MyComponent.spec.tsx` (matches component name exactly, with `.tsx` extension)
- Located in: `packages/my-package/__tests__/MyComponent.spec.tsx`
## Test File Structure
### 1. Imports
**Import Rules:**
- Import React (required for this project's JSX transform)
- Import vitest utilities from `'vitest'`
- Import `render` and `screen` from `'@testing-library/react'`
- Import the component being tested
- DO NOT import sub-components (they will be mocked)
**Example:**
```typescript
import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MyComponent } from '../src/components/MyComponent';
```
### 2. Mocking Sub-Components
**All imported sub-components must be mocked** to test the component in isolation.
**Pattern:**
```typescript
// Mock all sub-component modules
vi.mock('../src/components/SubComponentA', () => ({
SubComponentA: vi.fn(() =>
SubComponentA
)
}));
vi.mock('../src/components/SubComponentB', () => ({
SubComponentB: vi.fn(() => SubComponentB
)
}));
```
**Rules:**
- Place mocks at the top of the file, after imports
- Use `vi.fn()` to create a mock function that returns a simple ``
- Add `data-testid` for easy querying in tests
- Mock EVERY component imported by the component under test
### 3. Main Describe Block
```typescript
describe('MyComponent', () => {
// Helper function
function renderMyComponent(props?: Partial
) {
// ...
}
// Reset mocks before each test
beforeEach(() => {
vi.clearAllMocks();
});
// Test cases
it('should render sub-components with correct props', () => {
// Test...
});
});
```
**Structure Rules:**
- Main describe uses the component name
- Contains one `renderMyComponent` helper function at the top
- Includes `beforeEach` to clear mocks
- All test cases use the `renderMyComponent` helper
### 4. renderMyComponent Helper Function
**Purpose:** Factory function that renders the component with default or custom props.
**Pattern:**
```typescript
function renderMyComponent(props?: Partial) {
const defaultProps: MyComponentProps = {
title: 'Test Title',
onSubmit: vi.fn(),
items: [],
};
return render();
}
```
**Rules:**
- Accept partial props (optional)
- Define sensible defaults for all required props
- Use `vi.fn()` for callback props
- Spread defaults first, then custom props
- Return the result of `render()`
### 5. Testing Sub-Component Props
**Access the mock to verify props:**
```typescript
import { SubComponentA } from '../src/components/SubComponentA';
it('should pass correct props to SubComponentA', () => {
renderMyComponent({ title: 'My Title', count: 5 });
expect(SubComponentA).toHaveBeenCalledWith(
expect.objectContaining({
title: 'My Title',
count: 5
}),
expect.anything() // React context
);
});
```
**Rules:**
- Import the mocked component at the top
- Use `expect(MockedComponent).toHaveBeenCalledWith()`
- Use `expect.objectContaining()` to match props
- Use `expect.anything()` as second arg (React context)
### 6. Testing Callbacks
**Simulate callback invocation to test handlers:**
```typescript
it('should call onSubmit when button is clicked', () => {
const mockOnSubmit = vi.fn();
const { SubButton } = require('../src/components/SubButton');
renderMyComponent({ onSubmit: mockOnSubmit });
// Get the onClick prop passed to SubButton
const onClickProp = SubButton.mock.calls[0][0].onClick;
// Simulate the click
onClickProp();
expect(mockOnSubmit).toHaveBeenCalled();
});
```
**Rules:**
- Pass `vi.fn()` as callback props
- Extract callback from mock's call arguments
- Invoke the callback to test the handler
- Assert the handler was called correctly
### 7. Testing Conditional Rendering
**Test different branches:**
```typescript
it('should render ErrorMessage when error prop is provided', () => {
const { ErrorMessage } = require('../src/components/ErrorMessage');
renderMyComponent({ error: 'Something went wrong' });
expect(ErrorMessage).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Something went wrong'
}),
expect.anything()
);
});
it('should not render ErrorMessage when no error', () => {
const { ErrorMessage } = require('../src/components/ErrorMessage');
renderMyComponent({ error: null });
expect(ErrorMessage).not.toHaveBeenCalled();
});
```
### 8. Testing Lists and Iterations
**Test components rendered in loops:**
```typescript
it('should render ListItem for each item', () => {
const { ListItem } = require('../src/components/ListItem');
const items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' }
];
renderMyComponent({ items });
expect(ListItem).toHaveBeenCalledTimes(3);
expect(ListItem).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ id: '1', name: 'Item 1' }),
expect.anything()
);
expect(ListItem).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ id: '2', name: 'Item 2' }),
expect.anything()
);
expect(ListItem).toHaveBeenNthCalledWith(
3,
expect.objectContaining({ id: '3', name: 'Item 3' }),
expect.anything()
);
});
```
## Workflow
### Create Workflow
1. **Identify the component** - Determine which component to test
2. **Locate the component file** - Use Glob to find the component definition
3. **Read the component** - Understand props, sub-components, logic branches
4. **Identify sub-components** - List all imported components to mock
5. **Ensure `__tests__` exists** - Create folder if needed (sibling to `/src`)
6. **Verify @testing-library/react** - Check package.json
7. **Create test file** in `__tests__/MyComponent.spec.tsx` with:
- All required imports (including React)
- Mock declarations for all sub-components
- Main describe block
- `renderMyComponent` helper function
- `beforeEach` to clear mocks
- Test cases covering all scenarios
### Update Workflow
1. **Read existing test and component** - Compare current state
2. **Identify changes**:
- New sub-components → Add mocks
- Changed props → Update test cases
- New logic branches → Add test cases
- Removed functionality → Remove tests
3. **Apply updates using Edit tool** - Targeted changes only
4. **Verify coverage** - Ensure all logic branches tested
### Update Guidelines
- **Preserve structure** - Use Edit tool, not Write
- **Maintain consistency** - Follow existing patterns
- **Keep descriptive names** - Clear, behavior-focused
- **Don't delete passing tests** - Only update broken/obsolete tests
- **Add missing coverage** - Test new logic and props
## Best Practices
1. **Test Logic, Not UI** - Focus on props passed and callbacks invoked
2. **Mock All Sub-Components** - Test the component in complete isolation
3. **Descriptive Test Names** - Explain expected behavior clearly
4. **Clear Mocks Between Tests** - Use `beforeEach` with `vi.clearAllMocks()`
5. **Test All Branches** - Cover conditional rendering, loops, error states
6. **Test Callbacks** - Simulate sub-component callbacks to test handlers
7. **Use expect.objectContaining** - Match specific props without over-specifying
## Common Pitfalls to Avoid
1. ❌ **Don't Import Sub-Components Normally** - They should be mocked, not imported
2. ❌ **Don't Forget React Import** - Required for JSX in this project
3. ❌ **Don't Forget beforeEach** - Mocks persist between tests
4. ❌ **Don't Test UI Appearance** - Focus on logic and prop passing
5. ❌ **Don't Skip expect.anything()** - It's needed as the second argument for React context
6. ❌ **Don't Forget to Mock Everything** - ALL sub-components must be mocked
## Example Reference
See `examples.md` in the same directory for complete working examples of:
- Simple component with sub-components
- Component with conditional rendering
- Component with lists and iterations
- Component with callbacks and handlers
- Component with complex prop passing
- Component with multiple branches
## Important Notes
### File Organization
- Tests in `__tests__/` at package root (sibling to `/src`)
- Use `.tsx` extension
- Match component name exactly
### Dependencies
- Ensure `@testing-library/react` is installed
- Use `vi.mock()` for all sub-components
- Mock at the module level, not inside tests
### Test Quality
- Many small tests > few large tests
- Test happy path and error cases
- Test all conditional branches
- Descriptive test names
- Simple, focused tests
### Running Tests
After creating/updating:
1. Run `pnpm test` to verify tests pass
2. Run `pnpm lint` to check linting
3. Run `pnpm build` to verify TypeScript compiles
### Mocking Best Practices
- Always use `vi.fn()` for mock components
- Return simple `` elements with `data-testid`
- Clear mocks in `beforeEach`
- Import mocked components when you need to assert on them
- Use `expect.objectContaining()` to verify props